From 300f0fbeea460c2140a83057dad1f5e3991d6282 Mon Sep 17 00:00:00 2001 From: Lakshmi Tanmay Date: Thu, 22 Oct 2026 23:44:08 +0530 Subject: [PATCH 01/93] docs(engine): track migration phases and baseline evidence --- docs/rust-engine/progress.md | 54 ++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 docs/rust-engine/progress.md diff --git a/docs/rust-engine/progress.md b/docs/rust-engine/progress.md new file mode 100644 index 000000000..2421ed6e2 --- /dev/null +++ b/docs/rust-engine/progress.md @@ -0,0 +1,54 @@ +# Rust engine migration progress + +Implementation owner: sole agent; no delegation. +Starting commit: `7b1ae05f204be53b5d9f2714f21c6eea08b25935`. +Branch: `t3/rust-engine-migration`. Initial working tree clean. + +## Delivery + +Three PRs, stacked: Stage 1 engine → Stage 2 website → Stage 3 terminal. +Small commits at coherent behavior/build/documentation boundaries. Do not create +empty commits to inflate count. Do not publish production or releases implicitly. + +## Current position + +Stage 1, Phase 1.1 in progress. Read root AGENTS.md and the complete migration +plan before edits. Installed workspace dependencies with frozen lockfile. +No acceptance gate passed yet. Existing website engine remains authoritative. + +| Phase | Status | Evidence / remaining gate | +| --- | --- | --- | +| 1.1 Inventory / baselines | Active | Core baseline: 28 files, 349 tests pass. Browser/performance baseline pending. | +| 1.2 Contracts / fixtures | Pending | Requires inventory and recorded baseline. | +| 1.3 Native / browser proof | Pending | Requires frozen contracts. | +| 1.4 Input / lifecycle / timing | Pending | Requires production browser proof. | +| 1.5 Generation / configuration | Pending | Requires contract and proof. | +| 1.6 Events / results / replay | Pending | Requires input and generation. | +| 1.7 Binding / engine gate | Pending | Full three-path parity and budgets. | +| 2.1–2.6 Website | Pending | Requires Stage 1 gate. | +| 3.1–3.5 Terminal | Pending | Requires Stage 2 gate. | + +## Verification log + +- `vp install --frozen-lockfile`: pass; 1,102 packages, lockfile unchanged. +- `vp test run --project=@oxytype/typing-core`: pass; 349 tests, 28 files. +- Environment: Linux, Node 24.21.0; installed Rust 1.99.1; GitHub CLI authenticated. + Toolchain/binding pins not yet selected. Browser/OS support unverified. + +## Decisions + +- User's three-stage PR structure overrides the plan's suggested per-phase PRs. +- Browser rendering constraints (visible previous word, overflow) must be explicit + host observations; Rust decides their rule effects. They cannot be discarded + merely because headless TestSession does not model them. +- Preserve frozen snapshots. Record discrepancies; never regenerate to pass. + +## Blockers + +None established. Missing verification is pending work, not a passed gate. + +## Resume + +Read this file, inventory, contract, decisions, then `vp exec git status --short`. +Continue the first incomplete gate. Record commands and actual outcomes here; +do not rerun completed checks without a new reason. From afae54ccf68741b4b0c8c2d253add3955666221d Mon Sep 17 00:00:00 2001 From: Lakshmi Tanmay Date: Thu, 22 Oct 2026 23:47:40 +0530 Subject: [PATCH 02/93] test(engine): freeze consumer inventory and browser performance baseline --- docs/rust-engine/baseline-input.json | 494 +++++++++++++++++++ docs/rust-engine/baseline-readiness.json | 17 + docs/rust-engine/baseline.md | 48 ++ docs/rust-engine/features.md | 88 ++++ docs/rust-engine/inventory.md | 277 +++++++++++ docs/rust-engine/progress.md | 8 +- docs/rust-engine/reference-manifest.json | 94 ++++ frontend/e2e/engine-performance.migration.ts | 87 ++++ 8 files changed, 1109 insertions(+), 4 deletions(-) create mode 100644 docs/rust-engine/baseline-input.json create mode 100644 docs/rust-engine/baseline-readiness.json create mode 100644 docs/rust-engine/baseline.md create mode 100644 docs/rust-engine/features.md create mode 100644 docs/rust-engine/inventory.md create mode 100644 docs/rust-engine/reference-manifest.json create mode 100644 frontend/e2e/engine-performance.migration.ts diff --git a/docs/rust-engine/baseline-input.json b/docs/rust-engine/baseline-input.json new file mode 100644 index 000000000..d8ead56e8 --- /dev/null +++ b/docs/rust-engine/baseline-input.json @@ -0,0 +1,494 @@ +{ + "browser": "153.0.8010.12", + "fixture": "built Start Worker; desktop Chromium; zen; 20 input/restart samples; double-rAF paint; 2000 keyboard characters; forced GC", + "inputToPaint": [ + 138.20000000298023, 80.20000000298023, 84, 88.1000000089407, + 68.1000000089407, 87.90000000596046, 80.5, 77.8999999910593, + 60.599999994039536, 73.59999999403954, 107.5, 78.1000000089407, + 59.1000000089407, 92, 76.90000000596046, 72.3999999910593, 57.5, + 87.69999998807907, 76.3999999910593, 89.20000000298023 + ], + "restart": [ + 388.47247100000004, 423.63136399999985, 433.59411, 426.2511919999997, + 449.42194900000004, 438.79164199999923, 409.1686869999994, + 415.29368799999975, 388.8231259999993, 448.3593600000004, 452.6419649999998, + 408.8987789999992, 401.96822299999985, 441.1246890000002, 436.5003859999997, + 401.4671980000003, 451.58758399999897, 385.44698999999855, + 423.49853500000063, 394.7576749999989 + ], + "initialHeap": 8628096, + "activeHeap": 13696236, + "resetHeap": 13135484, + "assets": [ + { + "name": "/assets/index-Bih2PTRA.js", + "bytes": 120018, + "transfer": 48207 + }, + { + "name": "/assets/web-DoCSsFQb.js", + "bytes": 32378, + "transfer": 12617 + }, + { + "name": "/assets/link-BBh-inDq.js", + "bytes": 62968, + "transfer": 22646 + }, + { + "name": "/assets/redirect-D6mi4iKQ.js", + "bytes": 848, + "transfer": 784 + }, + { + "name": "/assets/matchContext-CJdPZbyb.js", + "bytes": 106, + "transfer": 410 + }, + { + "name": "/assets/not-found-i5RsCZif.js", + "bytes": 118, + "transfer": 411 + }, + { + "name": "/assets/ClientOnly-BFOvw4I-.js", + "bytes": 315, + "transfer": 528 + }, + { + "name": "/assets/application-BO7jqupq.js", + "bytes": 1199, + "transfer": 898 + }, + { + "name": "/assets/useRouter-Ca7pFXE8.js", + "bytes": 103, + "transfer": 414 + }, + { + "name": "/assets/createServerFn-DlBie0Bv.js", + "bytes": 36231, + "transfer": 11909 + }, + { + "name": "/assets/store-CAx0GJVG.js", + "bytes": 7034, + "transfer": 2818 + }, + { + "name": "/assets/build-BK2Ekhsu.js", + "bytes": 15058, + "transfer": 5010 + }, + { + "name": "/assets/queryObserver-D957XF2z.js", + "bytes": 20747, + "transfer": 7040 + }, + { + "name": "/assets/preload-helper-BZ1Pz5am.js", + "bytes": 1218, + "transfer": 988 + }, + { + "name": "/assets/schemas-BCsEXk5z.js", + "bytes": 95477, + "transfer": 26790 + }, + { + "name": "/assets/dist-Dz3b91zX.js", + "bytes": 2701, + "transfer": 1352 + }, + { + "name": "/assets/cn-DLyFV4qv.js", + "bytes": 27805, + "transfer": 9057 + }, + { + "name": "/assets/skeleton-CWhZcH5J.js", + "bytes": 2536, + "transfer": 1185 + }, + { + "name": "/assets/skeleton-CtcKlTjt.js", + "bytes": 417, + "transfer": 608 + }, + { + "name": "/assets/skeleton-text-CMtoHw-F.js", + "bytes": 390, + "transfer": 591 + }, + { + "name": "/assets/shared-DsmhGVsE.js", + "bytes": 8590, + "transfer": 3686 + }, + { + "name": "/assets/skeleton-DZbZqgc0.js", + "bytes": 1235, + "transfer": 910 + }, + { + "name": "/assets/zod-messages-DFcmgwyp.js", + "bytes": 2774, + "transfer": 1398 + }, + { + "name": "/assets/_app-BOKTVhON.js", + "bytes": 158, + "transfer": 451 + }, + { + "name": "/assets/_app.index-DywpqF7e.js", + "bytes": 2637, + "transfer": 1607 + }, + { + "name": "/assets/browser-runtime.client-DqEaJ8Tp.js", + "bytes": 13557, + "transfer": 6118 + }, + { + "name": "/assets/personal-bests-DoKtXru2.js", + "bytes": 83199, + "transfer": 26749 + }, + { + "name": "/assets/default-config-Ch20GKSO.js", + "bytes": 2053, + "transfer": 1372 + }, + { + "name": "/assets/test-activity-calendar-lFGxz15v.js", + "bytes": 5837, + "transfer": 2611 + }, + { + "name": "/assets/users-DZf1VidT.js", + "bytes": 19012, + "transfer": 9304 + }, + { + "name": "/assets/_virtual_env-config-D6szLuD5.js", + "bytes": 196, + "transfer": 482 + }, + { + "name": "/assets/useRef-9UY2IK1A.js", + "bytes": 98, + "transfer": 415 + }, + { + "name": "/assets/app-BQPR1dmp.js", + "bytes": 53139, + "transfer": 16377 + }, + { + "name": "/assets/createEvent-DUwnlqx6.js", + "bytes": 313, + "transfer": 528 + }, + { + "name": "/assets/effects-7SE5KZhe.js", + "bytes": 38433, + "transfer": 15617 + }, + { + "name": "/assets/useLocalStorage-ICcLxfs0.js", + "bytes": 3971, + "transfer": 2259 + }, + { + "name": "/assets/parse-C_h2zvC4.js", + "bytes": 5672, + "transfer": 2774 + }, + { + "name": "/assets/test-C11xF37d.js", + "bytes": 13652, + "transfer": 5833 + }, + { + "name": "/assets/rolldown-runtime-Dd_uD5pT.js", + "bytes": 1106, + "transfer": 927 + }, + { + "name": "/assets/env-hNDnyPZd.js", + "bytes": 100, + "transfer": 412 + }, + { + "name": "/assets/auth-client-Do4XO1Ss.js", + "bytes": 1099, + "transfer": 907 + }, + { + "name": "/assets/connection-BMlZb37h.js", + "bytes": 903, + "transfer": 837 + }, + { + "name": "/assets/popup.client-Cozt_Hi9.js", + "bytes": 31904, + "transfer": 12210 + }, + { + "name": "/assets/auth-EgLHI1c9.js", + "bytes": 7905, + "transfer": 3646 + }, + { + "name": "/assets/date-and-time-Bxt-WH9L.js", + "bytes": 964, + "transfer": 795 + }, + { + "name": "/assets/settled.client-BoF65wsR.js", + "bytes": 530, + "transfer": 644 + }, + { + "name": "/assets/query-cache.client-Bht1In51.js", + "bytes": 3064, + "transfer": 1726 + }, + { + "name": "/assets/build-hTByFGMt.js", + "bytes": 3318, + "transfer": 1634 + }, + { + "name": "/assets/settled.client-H6Sptnm2.js", + "bytes": 349, + "transfer": 558 + }, + { + "name": "/assets/update-safety-C66gpHLz.js", + "bytes": 140, + "transfer": 443 + }, + { + "name": "/assets/connection-DQSF9opu.js", + "bytes": 6679, + "transfer": 3042 + }, + { + "name": "/assets/cookies-DAiHfj0P.js", + "bytes": 691, + "transfer": 724 + }, + { + "name": "/assets/glarses-mode-SixLinXc.js", + "bytes": 285, + "transfer": 524 + }, + { + "name": "/assets/fonts.client-CxGm7qeF.js", + "bytes": 840, + "transfer": 815 + }, + { + "name": "/assets/chrome.client-BT71_e9g.js", + "bytes": 41572, + "transfer": 14441 + }, + { + "name": "/assets/mode-BgIo3a2E.js", + "bytes": 326, + "transfer": 514 + }, + { + "name": "/assets/list-Fui5YVEc.js", + "bytes": 72086, + "transfer": 25222 + }, + { + "name": "/assets/Kbd-DPDwH4vf.js", + "bytes": 1953, + "transfer": 1436 + }, + { + "name": "/assets/overlay-visibility-CHOMN59j.js", + "bytes": 181, + "transfer": 461 + }, + { + "name": "/assets/UpdateNameModal-DCeulr76.js", + "bytes": 6845, + "transfer": 3187 + }, + { + "name": "/assets/breakpoints-Cc6HYHps.js", + "bytes": 111, + "transfer": 409 + }, + { + "name": "/assets/simple-modal-QQBzpojW.js", + "bytes": 255, + "transfer": 501 + }, + { + "name": "/assets/utils-OCzDnQsQ.js", + "bytes": 638, + "transfer": 665 + }, + { + "name": "/assets/modifier-keys-CBdW-khj.js", + "bytes": 165, + "transfer": 437 + }, + { + "name": "/assets/navigate-H3LNuvCa.js", + "bytes": 268, + "transfer": 469 + }, + { + "name": "/assets/anime-CmdYVwG7.js", + "bytes": 5568, + "transfer": 2629 + }, + { + "name": "/assets/levels-DlYwc516.js", + "bytes": 414, + "transfer": 593 + }, + { + "name": "/assets/page.client-DkiCL-AL.js", + "bytes": 77067, + "transfer": 23940 + }, + { + "name": "/assets/modifiers-Cavid0O1.js", + "bytes": 1872, + "transfer": 1176 + }, + { + "name": "/assets/practise-words-DX1fN1hH.js", + "bytes": 2060, + "transfer": 1337 + }, + { + "name": "/assets/quote-rate-D0u-bc68.js", + "bytes": 42597, + "transfer": 14482 + }, + { + "name": "/assets/test-logic-DBD-TUd8.js", + "bytes": 32336, + "transfer": 11982 + }, + { + "name": "/assets/quotes-controller-NMBMwU9J.js", + "bytes": 2830, + "transfer": 1500 + }, + { + "name": "/assets/format-BdXx0Xan.js", + "bytes": 187, + "transfer": 460 + }, + { + "name": "/assets/open-8_Twa7Cq.js", + "bytes": 253, + "transfer": 492 + }, + { + "name": "/assets/challenge-controller-BsG6c5c5.js", + "bytes": 3970, + "transfer": 1879 + }, + { + "name": "/assets/lz-ts-BjRkF-Z2.js", + "bytes": 3789, + "transfer": 1602 + }, + { + "name": "/assets/Result-KOjBV5UZ.js", + "bytes": 36010, + "transfer": 12527 + }, + { + "name": "/assets/tags-x7qZ75SS.js", + "bytes": 6056, + "transfer": 2476 + }, + { + "name": "/assets/reader-CD0iXdWl.js", + "bytes": 336480, + "transfer": 92657 + }, + { + "name": "/assets/UserFlags-D0zcp66J.js", + "bytes": 994, + "transfer": 788 + }, + { + "name": "/assets/typing-speed-units-C-jdytt1.js", + "bytes": 37, + "transfer": 357 + }, + { + "name": "/assets/edit-result-tags-aFuXCmB7.js", + "bytes": 294, + "transfer": 531 + }, + { + "name": "/assets/result-Bi9gnFH6.js", + "bytes": 19238, + "transfer": 7675 + }, + { + "name": "/assets/today-tracker-Bgf7zsww.js", + "bytes": 2349, + "transfer": 1484 + }, + { + "name": "/assets/test-screenshot-BLnrMuiq.js", + "bytes": 4898, + "transfer": 2604 + }, + { + "name": "/assets/quote-report-CqoAU8jd.js", + "bytes": 163, + "transfer": 453 + }, + { + "name": "/assets/completion.client-py8Tupf4.js", + "bytes": 6348, + "transfer": 3293 + }, + { + "name": "/assets/result-saving-BzV5NqD0.js", + "bytes": 4902, + "transfer": 2525 + }, + { + "name": "/assets/ResultChart-CoVlHRBs.js", + "bytes": 6050, + "transfer": 2766 + }, + { + "name": "/assets/ChartJs-CYa-nlCR.js", + "bytes": 74721, + "transfer": 22585 + }, + { + "name": "/assets/endOfWeek-yDY4_qaX.js", + "bytes": 1860, + "transfer": 1085 + }, + { + "name": "/assets/compareAsc-BJhcP0hn.js", + "bytes": 105, + "transfer": 422 + }, + { + "name": "/assets/chart-Bx03QiIs.js", + "bytes": 177979, + "transfer": 61565 + } + ] +} diff --git a/docs/rust-engine/baseline-readiness.json b/docs/rust-engine/baseline-readiness.json new file mode 100644 index 000000000..2a5df4452 --- /dev/null +++ b/docs/rust-engine/baseline-readiness.json @@ -0,0 +1,17 @@ +{ + "fixture": "production assets, guest API fixtures, desktop Chromium, HTTP cache cold/warm, SW bypass", + "cold": [ + 745.9000000059605, 752.2999999970198, 755.7000000029802, 714.2999999970198, + 742.0999999940395, 670.1000000089407, 1259.7999999970198, + 1400.6000000089407, 713.7000000029802, 1347.6000000089407 + ], + "warm": [ + 728.8999999910593, 625.7000000029802, 609.0999999940395, 638.7000000029802, + 644.7000000029802, 711.0999999940395, 744.4000000059605, 634.3999999910593, + 662.8000000119209, 722.9000000059605 + ], + "settings": [ + 522, 494.6000000089407, 498.90000000596046, 485.8999999910593, 716.5, 536.5, + 690.1000000089407, 508, 519.2999999970198, 558.1000000089407 + ] +} diff --git a/docs/rust-engine/baseline.md b/docs/rust-engine/baseline.md new file mode 100644 index 000000000..cf4ddce55 --- /dev/null +++ b/docs/rust-engine/baseline.md @@ -0,0 +1,48 @@ +# Website baseline + +Source engine: `7b1ae05f204be53b5d9f2714f21c6eea08b25935`. No rule changes. + +Host: Linux aarch64, 7.1.13-402.asahi.fc44.aarch64+16k; Node 24.21.0. +Browser: bundled Playwright Chromium 153.0.8010.12, Desktop Chrome profile. +No CPU/network throttle. Shared development machine; no exclusive CPU reservation. +Cloudflare Vite + TanStack Start production bundle, local Miniflare/workerd, seeded D1. +Browser HTTP cache cold/warm as described in readiness fixture; service worker bypassed. + +| Measurement (ms) | Samples | Median | p95 | +| --- | --- | --- | --- | +| cold | 10 | 745.90 | 1400.60 | +| warm | 10 | 644.70 | 744.40 | +| settings | 10 | 519.30 | 716.50 | +| inputToPaint | 20 | 78.10 | 107.50 | +| restart | 20 | 423.50 | 451.59 | + +Readiness includes navigation → first accepted input/render, so measures usable test, +not only bundle initialization. Input-to-paint is a conservative double-rAF proxy +including Playwright assertion latency. Restart includes click + complete fade. +Raw samples are adjacent JSON, not replaced by summary numbers. + +Forced-GC JS heap: initial 8,628,096 B; active after 2,000 keyboard chars 13,696,236 B; after reset 13,135,484 B. +Recorded JS/Wasm decoded resource bytes: 1,717,897 B (resource list in JSON; repeated URLs count repeated loads). +This bounds one 1,000-word zen run; longer soak verification still required at 1.7/2.5. + +## Reproduce + +```sh +vp install --frozen-lockfile +vp run --filter @oxytype/frontend build:start +MIGRATION_BASELINE_OUTPUT="$PWD/docs/rust-engine/candidate-readiness.json" ENGINE_PERFORMANCE_OUTPUT="$PWD/docs/rust-engine/candidate-input.json" vp run --filter @oxytype/frontend benchmark-migration readiness.migration.ts engine-performance.migration.ts +``` + +The fixture server expects `dist/start`, not the `dist/start-test` runtime-entry build. +Initial attempt used the latter and failed ENOENT before browser work; corrected by +building `build:start`. Both build paths passed; production path browser tests passed. + +## Existing regression evidence + +- Core: 28 files / 349 tests passed. +- Frontend typing runtime: 27 files / 475 tests passed. +- Frontend input: 9 files / 204 tests passed. +- Browser baseline: 2 tests passed (53.4 s). +- Baseline benchmark type-aware lint passed. +- Production build emits pre-existing CSS sourcemap warning; build exits 0. + diff --git a/docs/rust-engine/features.md b/docs/rust-engine/features.md new file mode 100644 index 000000000..dc91d4fbd --- /dev/null +++ b/docs/rust-engine/features.md @@ -0,0 +1,88 @@ +# Feature parity checklist + +Every entry must pass before Stage 1 / web cutover. Pending means unported. + +| Feature | Rust | Native / Wasm / web evidence | +| --- | --- | --- | +| words: finite / zero infinite | Pending | Existing fixtures + targeted gaps required | +| time: presets / custom / zero infinite | Pending | Existing fixtures + targeted gaps required | +| quote: lengths / IDs / repeat / British text | Pending | Existing fixtures + targeted gaps required | +| custom: random / repeat / shuffle; pipe sections; word / time / section limits | Pending | Existing fixtures + targeted gaps required | +| zen: free typing / bailout / trailing idle | Pending | Existing fixtures + targeted gaps required | +| language: lazy accents / additional accents / noLazyMode | Pending | Existing fixtures + targeted gaps required | +| punctuation / original punctuation / numbers / numeral scripts | Pending | Existing fixtures + targeted gaps required | +| wordset: random / Zipf / shuffled / previous-two exclusion | Pending | Existing fixtures + targeted gaps required | +| funbox sections / polyglot language properties / asynchronous content | Pending | Existing fixtures + targeted gaps required | +| normal / expert / master difficulty | Pending | Existing fixtures + targeted gaps required | +| strictSpace / stopOnError letter and word | Pending | Existing fixtures + targeted gaps required | +| deleteOnError letter / word / hard; stopped-input interaction | Pending | Existing fixtures + targeted gaps required | +| confidence off / on / max; freedomMode; visible-word bound | Pending | Existing fixtures + targeted gaps required | +| UTF-16 split commits / partial surrogates / grapheme-independent scores | Pending | Existing fixtures + targeted gaps required | +| IME multi-character / composition quick finish / equivalent spaces | Pending | Existing fixtures + targeted gaps required | +| typography equivalents / ellipsis / Dutch ligature / opposite Shift | Pending | Existing fixtures + targeted gaps required | +| code automatic tabs / unindent / newline / Firefox deletion | Pending | Existing fixtures + targeted gaps required | +| timer drift / catchup / background stalls / simultaneous timestamps | Pending | Existing fixtures + targeted gaps required | +| reset / restart / repeat / abandon / completion once / stale commands | Pending | Existing fixtures + targeted gaps required | +| minimum burst / speed / accuracy; flex burst | Pending | Existing fixtures + targeted gaps required | +| pace correction / remembered speed / weak-spot learning | Pending | Existing fixtures + targeted gaps required | +| accuracy / WPM / raw / chars / consistency / burst | Pending | Existing fixtures + targeted gaps required | +| physical timing / synthetic observations / estimated release markers | Pending | Existing fixtures + targeted gaps required | +| histories / charts / idle / incomplete duration / validity | Pending | Existing fixtures + targeted gaps required | +| historical replay / seek / regression / clearedNextWord | Pending | Existing fixtures + targeted gaps required | +| CompletedEvent fields / schemas / submission hash / backend rejection | Pending | Existing fixtures + targeted gaps required | +| settings imports / presets / URL overrides / live rules | Pending | Existing fixtures + targeted gaps required | +| custom text progress / practice missed and slow words | Pending | Existing fixtures + targeted gaps required | +| SSR safety / Cloudflare asset MIME+CSP / stale tabs / rollback | Pending | Existing fixtures + targeted gaps required | + +## Every funbox + +| Name | Rule / host ownership | Evidence | +| --- | --- | --- | +| `58008` | Rust rules; retain any visual/input hooks in host | Pending | +| `mirror` | Host visual/audio/layout; Rust retains metadata effects on eligibility/config | Pending | +| `upside_down` | Host visual/audio/layout; Rust retains metadata effects on eligibility/config | Pending | +| `nausea` | Host visual/audio/layout; Rust retains metadata effects on eligibility/config | Pending | +| `round_round_baby` | Host visual/audio/layout; Rust retains metadata effects on eligibility/config | Pending | +| `simon_says` | Host visual/audio/layout; Rust retains metadata effects on eligibility/config | Pending | +| `tts` | Host visual/audio/layout; Rust retains metadata effects on eligibility/config | Pending | +| `choo_choo` | Host visual/audio/layout; Rust retains metadata effects on eligibility/config | Pending | +| `arrows` | Rust rules; retain any visual/input hooks in host | Pending | +| `rAnDoMcAsE` | Rust rules; retain any visual/input hooks in host | Pending | +| `sPoNgEcAsE` | Rust rules; retain any visual/input hooks in host | Pending | +| `capitals` | Rust rules; retain any visual/input hooks in host | Pending | +| `layout_mirror` | Host visual/audio/layout; Rust retains metadata effects on eligibility/config | Pending | +| `layoutfluid` | Host visual/audio/layout; Rust retains metadata effects on eligibility/config | Pending | +| `earthquake` | Host visual/audio/layout; Rust retains metadata effects on eligibility/config | Pending | +| `space_balls` | Host visual/audio/layout; Rust retains metadata effects on eligibility/config | Pending | +| `gibberish` | Rust rules; retain any visual/input hooks in host | Pending | +| `ascii` | Rust rules; retain any visual/input hooks in host | Pending | +| `specials` | Rust rules; retain any visual/input hooks in host | Pending | +| `plus_zero` | Host visual/audio/layout; Rust retains metadata effects on eligibility/config | Pending | +| `plus_one` | Host visual/audio/layout; Rust retains metadata effects on eligibility/config | Pending | +| `plus_two` | Host visual/audio/layout; Rust retains metadata effects on eligibility/config | Pending | +| `plus_three` | Host visual/audio/layout; Rust retains metadata effects on eligibility/config | Pending | +| `read_ahead_easy` | Host visual/audio/layout; Rust retains metadata effects on eligibility/config | Pending | +| `read_ahead` | Host visual/audio/layout; Rust retains metadata effects on eligibility/config | Pending | +| `read_ahead_hard` | Host visual/audio/layout; Rust retains metadata effects on eligibility/config | Pending | +| `memory` | Host visual/audio/layout; Rust retains metadata effects on eligibility/config | Pending | +| `nospace` | Rust rules; retain any visual/input hooks in host | Pending | +| `poetry` | Rust rules; retain any visual/input hooks in host | Pending | +| `wikipedia` | Rust rules; retain any visual/input hooks in host | Pending | +| `weakspot` | Rust rules; retain any visual/input hooks in host | Pending | +| `pseudolang` | Rust rules; retain any visual/input hooks in host | Pending | +| `IPv4` | Rust rules; retain any visual/input hooks in host | Pending | +| `IPv6` | Rust rules; retain any visual/input hooks in host | Pending | +| `binary` | Rust rules; retain any visual/input hooks in host | Pending | +| `hexadecimal` | Rust rules; retain any visual/input hooks in host | Pending | +| `zipf` | Rust rules; retain any visual/input hooks in host | Pending | +| `morse` | Rust rules; retain any visual/input hooks in host | Pending | +| `crt` | Host visual/audio/layout; Rust retains metadata effects on eligibility/config | Pending | +| `backwards` | Rust rules; retain any visual/input hooks in host | Pending | +| `ddoouubblleedd` | Rust rules; retain any visual/input hooks in host | Pending | +| `instant_messaging` | Rust rules; retain any visual/input hooks in host | Pending | +| `underscore_spaces` | Rust rules; retain any visual/input hooks in host | Pending | +| `ALL_CAPS` | Rust rules; retain any visual/input hooks in host | Pending | +| `polyglot` | Rust rules; retain any visual/input hooks in host | Pending | +| `asl` | Host visual/audio/layout; Rust retains metadata effects on eligibility/config | Pending | +| `rot13` | Rust rules; retain any visual/input hooks in host | Pending | +| `no_quit` | Rust rules; retain any visual/input hooks in host | Pending | diff --git a/docs/rust-engine/inventory.md b/docs/rust-engine/inventory.md new file mode 100644 index 000000000..2ce06c801 --- /dev/null +++ b/docs/rust-engine/inventory.md @@ -0,0 +1,277 @@ +# Migration inventory + +Frozen at `7b1ae05f204be53b5d9f2714f21c6eea08b25935`. Sole implementation owner. + +## Source ownership and destination + +| Current module | Classification | Destination | +| --- | --- | --- | +| `active-funboxes.ts` | Engine | crates/typing-engine | +| `arrays.ts` | Presentation/settings/transport | scoped TypeScript packages, SSR safe | +| `british-english-replacements.ts` | Content asset | shared replacement data | +| `british-english.ts` | Engine | crates/typing-engine | +| `command-matching.ts` | Presentation/settings/transport | scoped TypeScript packages, SSR safe | +| `completed-event.ts` | Engine | crates/typing-engine | +| `config/default-config.ts` | Presentation/settings/transport | scoped TypeScript packages, SSR safe | +| `config/funbox-validation.ts` | Presentation/settings/transport | scoped TypeScript packages, SSR safe | +| `config/metadata.ts` | Presentation/settings/transport | scoped TypeScript packages, SSR safe | +| `config/migrate.ts` | Presentation/settings/transport | scoped TypeScript packages, SSR safe | +| `config/setter.ts` | Presentation/settings/transport | scoped TypeScript packages, SSR safe | +| `custom-text.ts` | Presentation/settings/transport | scoped TypeScript packages, SSR safe | +| `ddr.ts` | Engine | crates/typing-engine | +| `english-punctuation.ts` | Engine | crates/typing-engine | +| `errors.ts` | Presentation/settings/transport | scoped TypeScript packages, SSR safe | +| `events/data.ts` | Engine | crates/typing-engine | +| `events/helpers.ts` | Engine | crates/typing-engine | +| `events/live-cache.ts` | Engine | crates/typing-engine | +| `events/stats/bursts.ts` | Engine | crates/typing-engine | +| `events/stats/characters.ts` | Engine | crates/typing-engine | +| `events/stats/intervals.ts` | Engine | crates/typing-engine | +| `events/stats/key-timing.ts` | Engine | crates/typing-engine | +| `events/stats/speed-history.ts` | Engine | crates/typing-engine | +| `events/stats/timing.ts` | Engine | crates/typing-engine | +| `events/stats/word-history.ts` | Engine | crates/typing-engine | +| `events/stats.ts` | Engine | crates/typing-engine | +| `events/types.ts` | Shared contract | generated bindings + checked web DTOs | +| `format.ts` | Presentation/settings/transport | scoped TypeScript packages, SSR safe | +| `funbox-word-functions.ts` | Engine | crates/typing-engine | +| `generate.ts` | Engine | crates/typing-engine | +| `index.ts` | Re-export | retire | +| `input/config.ts` | Shared contract | generated bindings + checked web DTOs | +| `input/engine.ts` | Engine | crates/typing-engine | +| `input/fail-or-finish.ts` | Engine | crates/typing-engine | +| `input/input-type.ts` | Shared contract | generated bindings + checked web DTOs | +| `input/util.ts` | Engine | crates/typing-engine | +| `input/validation.ts` | Engine | crates/typing-engine | +| `ip-addresses.ts` | Presentation/settings/transport | scoped TypeScript packages, SSR safe | +| `keys.ts` | Presentation/settings/transport | scoped TypeScript packages, SSR safe | +| `languages.ts` | Content host adapter | TypeScript content adapters; Rust selection/transforms | +| `layout-emulator.ts` | Presentation/settings/transport | scoped TypeScript packages, SSR safe | +| `lazy-mode.ts` | Engine | crates/typing-engine | +| `live-stats.ts` | Presentation/settings/transport | scoped TypeScript packages, SSR safe | +| `mode.ts` | Engine | crates/typing-engine | +| `numerals.ts` | Engine | crates/typing-engine | +| `pace-caret.ts` | Engine | crates/typing-engine | +| `poetry.ts` | Content host adapter | TypeScript content adapters; Rust selection/transforms | +| `practise-words.ts` | Engine | crates/typing-engine | +| `quick-restart.ts` | Engine | crates/typing-engine | +| `quote-source.ts` | Content host adapter | TypeScript content adapters; Rust selection/transforms | +| `quotes.ts` | Engine | crates/typing-engine | +| `replay.ts` | Engine | crates/typing-engine | +| `restart.ts` | Engine | crates/typing-engine | +| `result-fields.ts` | Engine | crates/typing-engine | +| `result-hash.ts` | Presentation/settings/transport | scoped TypeScript packages, SSR safe | +| `result-validity.ts` | Engine | crates/typing-engine | +| `session.ts` | Engine | crates/typing-engine | +| `stats-math.ts` | Engine | crates/typing-engine | +| `strings.ts` | Engine | crates/typing-engine | +| `themes.ts` | Presentation/settings/transport | scoped TypeScript packages, SSR safe | +| `timer.ts` | Engine | crates/typing-engine | +| `typing-speed-units.ts` | Presentation/settings/transport | scoped TypeScript packages, SSR safe | +| `typing-time.ts` | Engine | crates/typing-engine | +| `weak-spot.ts` | Engine | crates/typing-engine | +| `wikipedia.ts` | Content host adapter | TypeScript content adapters; Rust selection/transforms | +| `words-generator/funboxes.ts` | Engine | crates/typing-engine | +| `words-generator/limits.ts` | Engine | crates/typing-engine | +| `words-generator/next-word.ts` | Engine | crates/typing-engine | +| `words-generator/punctuation.ts` | Engine | crates/typing-engine | +| `words-generator/quotes.ts` | Engine | crates/typing-engine | +| `words-generator/state.ts` | Engine | crates/typing-engine | +| `words-generator/transforms.ts` | Engine | crates/typing-engine | +| `words-generator/types.ts` | Shared contract | generated bindings + checked web DTOs | +| `words-generator.ts` | Engine | crates/typing-engine | +| `wordset.ts` | Engine | crates/typing-engine | + +Split `strings`: display utilities stay TS; input normalization/countChars move to Rust. +Settings metadata/migration stays TS; rule policies/defaults generated from Rust. +`live-stats` formats display; score derivation moves to Rust. +`ip-addresses` includes funbox generation: move those rules to Rust. +`layout-emulator` is host key translation, supplied as observations. + +## Consumer map + +Includes mocks, scripts, type-only imports, server imports, re-exports, and manifests. + +```text +./frontend/src/ts/utils/result-hash.ts:1:export * from "@oxytype/typing-core/result-hash"; +./frontend/src/ts/utils/quick-restart.ts:1:export { canQuickRestart } from "@oxytype/typing-core/quick-restart"; +./frontend/src/ts/utils/misc.ts:72:export { getMode2 } from "@oxytype/typing-core/mode"; +./frontend/src/ts/utils/json-data.ts:9:} from "@oxytype/typing-core/languages"; +./frontend/src/ts/utils/ip-addresses.ts:1:export * from "@oxytype/typing-core/ip-addresses"; +./frontend/src/ts/utils/format.ts:1:export { Formatting } from "@oxytype/typing-core/format"; +./frontend/package.json:62: "@oxytype/typing-core": "workspace:*", +./frontend/src/ts/utils/arrays.ts:5:} from "@oxytype/typing-core/arrays"; +./frontend/src/ts/utils/typing-speed-units.ts:1:export { get } from "@oxytype/typing-core/typing-speed-units"; +./frontend/src/ts/utils/typing-speed-units.ts:2:export type { TypingSpeedUnitSettings } from "@oxytype/typing-core/typing-speed-units"; +./frontend/src/ts/input/helpers/word-navigation.ts:1:import { getPreviousWordInput } from "@oxytype/typing-core/input/engine"; +./frontend/src/ts/input/helpers/word-navigation.ts:21:import { getWordBurst } from "@oxytype/typing-core/events/stats"; +./frontend/src/ts/input/helpers/validation.ts:1:import * as Core from "@oxytype/typing-core/input/validation"; +./frontend/src/ts/utils/strings.ts:2:import { removeLanguageSize } from "@oxytype/typing-core/strings"; +./frontend/src/ts/utils/strings.ts:9:} from "@oxytype/typing-core/strings"; +./frontend/src/ts/utils/strings.ts:103:export { cleanTypographySymbols } from "@oxytype/typing-core/strings"; +./frontend/src/ts/utils/strings.ts:185:export { areCharactersVisuallyEqual } from "@oxytype/typing-core/strings"; +./frontend/src/ts/utils/strings.ts:205:export { isSpace } from "@oxytype/typing-core/strings"; +./frontend/src/ts/utils/strings.ts:215:export { countChars } from "@oxytype/typing-core/strings"; +./frontend/src/ts/input/helpers/util.ts:1:import * as Core from "@oxytype/typing-core/input/util"; +./frontend/src/ts/input/helpers/input-type.ts:1:export * from "@oxytype/typing-core/input/input-type"; +./frontend/src/ts/input/helpers/fail-or-finish.ts:1:import * as Core from "@oxytype/typing-core/input/fail-or-finish"; +./frontend/src/ts/input/handlers/insert-text.ts:1:import { evaluateInsert } from "@oxytype/typing-core/input/engine"; +./frontend/src/ts/input/handlers/before-insert-text.ts:1:import { shouldBlockInsertion } from "@oxytype/typing-core/input/engine"; +./frontend/src/ts/input/handlers/before-delete.ts:1:import { canDelete } from "@oxytype/typing-core/input/engine"; +./frontend/src/ts/states/test.ts:7:import { EventLog } from "@oxytype/typing-core/events/types"; +./frontend/src/ts/states/test.ts:16:import { QuoteWithTextSplit } from "@oxytype/typing-core/quotes"; +./frontend/src/ts/states/result.ts:10:import type { ReplayWord } from "@oxytype/typing-core/replay"; +./frontend/src/ts/states/live-stats.ts:3:import * as LiveStats from "@oxytype/typing-core/live-stats"; +./frontend/src/ts/states/event-log-viewer.ts:6:} from "@oxytype/typing-core/events/types"; +./frontend/src/ts/controllers/quotes-controller.ts:4:import { QuotesController as CoreQuotesController } from "@oxytype/typing-core/quote-source"; +./frontend/src/ts/controllers/quotes-controller.ts:5:import { Quote } from "@oxytype/typing-core/quotes"; +./frontend/src/ts/controllers/quotes-controller.ts:6:export type { Quote } from "@oxytype/typing-core/quotes"; +./frontend/src/ts/constants/themes.ts:1:export * from "@oxytype/typing-core/themes"; +./frontend/src/ts/constants/keys.ts:1:import { Keycode } from "@oxytype/typing-core/keys"; +./frontend/src/ts/constants/keys.ts:2:export type { Keycode } from "@oxytype/typing-core/keys"; +./frontend/src/ts/constants/default-config.ts:2:import { getDefaultConfig as getBaseDefaultConfig } from "@oxytype/typing-core/config/default-config"; +./frontend/src/ts/constants/default-config.ts:6:} from "@oxytype/typing-core/themes"; +./frontend/src/ts/config/utils.ts:5:} from "@oxytype/typing-core/config/migrate"; +./frontend/src/ts/config/metadata.tsx:8:} from "@oxytype/typing-core/config/metadata"; +./frontend/src/ts/config/lifecycle.ts:22:import { lastConfigsToApply } from "@oxytype/typing-core/config/setter"; +./frontend/src/ts/config/funbox-validation.ts:5:} from "@oxytype/typing-core/config/funbox-validation"; +./frontend/src/ts/components/modals/EventLogViewerModal.tsx:1:import type { EventLog } from "@oxytype/typing-core/events/types"; +./frontend/src/ts/components/modals/event-log-viewer/video-sync.ts:2:import type { TestEventNoMs } from "@oxytype/typing-core/events/types"; +./frontend/src/ts/components/modals/event-log-viewer/timeline-lanes.ts:5:} from "@oxytype/typing-core/events/types"; +./frontend/src/ts/components/modals/event-log-viewer/PreviewContent.tsx:1:import type { EventLog } from "@oxytype/typing-core/events/types"; +./frontend/src/ts/components/modals/event-log-viewer/EventsPanel.tsx:4:} from "@oxytype/typing-core/events/types"; +./frontend/src/ts/components/modals/event-log-viewer/scatter-points.ts:1:import type { TestEventNoMs } from "@oxytype/typing-core/events/types"; +./frontend/src/ts/components/modals/event-log-viewer/DistributionPanel.tsx:1:import type { EventLog } from "@oxytype/typing-core/events/types"; +./frontend/src/ts/components/modals/event-log-viewer/DistributionPanel.tsx:6:} from "@oxytype/typing-core/events/stats"; +./frontend/src/ts/components/modals/event-log-viewer/event-log.ts:5:} from "@oxytype/typing-core/events/types"; +./frontend/src/ts/components/modals/event-log-viewer/event-log.ts:7:import { getInputFromDom } from "@oxytype/typing-core/events/helpers"; +./frontend/src/ts/components/modals/event-log-viewer/event-log.ts:8:import { EVENT_LOG_VERSION } from "@oxytype/typing-core/events/types"; +./frontend/src/ts/components/modals/event-log-viewer/createVideoSync.ts:9:import type { TestEventNoMs } from "@oxytype/typing-core/events/types"; +./frontend/src/ts/components/modals/event-log-viewer/createPlayback.ts:3:import type { TestEventNoMs } from "@oxytype/typing-core/events/types"; +./frontend/src/ts/components/modals/event-log-viewer/Timeline.tsx:1:import type { TestEventType } from "@oxytype/typing-core/events/types"; +./frontend/src/ts/components/modals/event-log-viewer/ScatterPanel.tsx:1:import type { EventLog } from "@oxytype/typing-core/events/types"; +./frontend/src/ts/components/modals/event-log-viewer/ScatterPanel.tsx:6:} from "@oxytype/typing-core/events/stats"; +./frontend/src/ts/commandline/matching.ts:1:export { matchCommands } from "@oxytype/typing-core/command-matching"; +./frontend/src/ts/commandline/lists/result-screen.ts:13:import { getInputHistory } from "@oxytype/typing-core/events/stats"; +./frontend/src/app/theme-vars.ts:1:import type { Theme } from "@oxytype/typing-core/themes"; +./frontend/src/app/theme-bootstrap.ts:1:import { defaultThemes } from "@oxytype/typing-core/config/default-config"; +./frontend/src/app/theme-bootstrap.ts:2:import { themes } from "@oxytype/typing-core/themes"; +./frontend/src/features/typing/runtime/words-generator.ts:1:import { createWordsGenerator } from "@oxytype/typing-core/words-generator"; +./frontend/src/features/typing/runtime/words-generator.ts:10:import * as LazyMode from "@oxytype/typing-core/lazy-mode"; +./frontend/src/features/typing/runtime/words-generator.ts:11:import * as EnglishPunctuation from "@oxytype/typing-core/english-punctuation"; +./frontend/src/features/typing/runtime/wikipedia.ts:1:import { getSection as loadSection } from "@oxytype/typing-core/wikipedia"; +./frontend/src/features/typing/runtime/wikipedia.ts:2:import { Section } from "@oxytype/typing-core/languages"; +./frontend/src/features/typing/runtime/weak-spot.ts:1:import { createWeakSpot } from "@oxytype/typing-core/weak-spot"; +./frontend/src/features/typing/runtime/layout-emulator.ts:2:import { getCharFromLayout } from "@oxytype/typing-core/layout-emulator"; +./frontend/src/features/typing/runtime/today-tracker.ts:4:} from "@oxytype/typing-core/typing-time"; +./frontend/src/features/typing/runtime/initialization.ts:42:import { WordGenError } from "@oxytype/typing-core/errors"; +./frontend/src/features/typing/runtime/initialization.ts:54:import { hasKoreanCharacters } from "@oxytype/typing-core/strings"; +./frontend/src/features/typing/runtime/timer-scheduler.ts:1:import type { TestTimer } from "@oxytype/typing-core/timer"; +./frontend/src/features/typing/runtime/custom-text.ts:8:import { defaultCustomTextSettings } from "@oxytype/typing-core/custom-text"; +./frontend/src/features/typing/runtime/completion.client.ts:63:} from "@oxytype/typing-core/events/stats"; +./frontend/src/features/typing/runtime/completion.client.ts:68:} from "@oxytype/typing-core/result-validity"; +./frontend/src/features/typing/runtime/completion.client.ts:72:import { countInvalidResultFields } from "@oxytype/typing-core/result-fields"; +./frontend/src/features/typing/runtime/british-english.ts:1:import { replace as replaceWord } from "@oxytype/typing-core/british-english"; +./frontend/src/features/typing/runtime/test-timer.ts:3:import type { SessionEvents } from "@oxytype/typing-core/session"; +./frontend/src/features/typing/runtime/test-timer.ts:4:import { getTimeLimit } from "@oxytype/typing-core/timer"; +./frontend/src/app/default-theme.ts:2:import { defaultThemes } from "@oxytype/typing-core/config/default-config"; +./frontend/src/features/typing/runtime/result/history.ts:12:} from "@oxytype/typing-core/events/stats"; +./frontend/src/features/typing/runtime/test-logic.ts:76:import { shouldRepeatQuote } from "@oxytype/typing-core/restart"; +./frontend/src/features/typing/runtime/result/chart-data.ts:15:} from "@oxytype/typing-core/events/stats"; +./frontend/src/features/typing/runtime/result/accuracy.ts:2:import { getAccuracy } from "@oxytype/typing-core/events/stats"; +./frontend/src/server/operation.server.ts:19:import { migrateConfig } from "@oxytype/typing-core/config/migrate"; +./frontend/src/features/typing/runtime/replay.ts:5:import { getWpmHistory } from "@oxytype/typing-core/events/stats"; +./frontend/src/features/typing/runtime/replay.ts:12:} from "@oxytype/typing-core/replay"; +./frontend/src/features/typing/runtime/funbox/funbox-functions.ts:2:import { FunboxWordFunctions } from "@oxytype/typing-core/active-funboxes"; +./frontend/src/features/typing/runtime/funbox/funbox-functions.ts:3:import { createFunboxWordFunctions } from "@oxytype/typing-core/funbox-word-functions"; +./frontend/src/features/typing/runtime/practise-words.ts:1:import { buildPracticeWords } from "@oxytype/typing-core/practise-words"; +./frontend/src/features/typing/runtime/events/live-cache.ts:3:import { createLiveCache } from "@oxytype/typing-core/events/live-cache"; +./frontend/src/features/typing/runtime/events/data.ts:4:import { createTestSession } from "@oxytype/typing-core/session"; +./frontend/src/features/typing/runtime/events/data.ts:5:import { EventLogContext } from "@oxytype/typing-core/events/types"; +./frontend/src/features/typing/runtime/poetry.ts:1:import { getPoem as loadPoem } from "@oxytype/typing-core/poetry"; +./frontend/src/features/typing/runtime/poetry.ts:2:import { Section } from "@oxytype/typing-core/languages"; +./frontend/src/features/typing/components/result/ResultWordsHistory.tsx:5:} from "@oxytype/typing-core/events/stats"; +./frontend/src/features/typing/components/result/ResultReplay.tsx:1:import type { ReplayLetter } from "@oxytype/typing-core/replay"; +./frontend/src/features/typing/components/result/ResultChart.tsx:8:import { getWordIndexesForSecond } from "@oxytype/typing-core/events/stats"; +./frontend/src/features/settings/sidebar-skeleton.tsx:1:import { getDefaultConfig } from "@oxytype/typing-core/config/default-config"; +./frontend/scripts/measure-input-snapshots.ts:2:import { createEventRecorder } from "@oxytype/typing-core/events/data"; +./frontend/scripts/measure-input-snapshots.ts:3:import { createLiveCache } from "@oxytype/typing-core/events/live-cache"; +./frontend/__tests__/input/helpers/fail-or-finish.spec.ts:8:import * as Misc from "@oxytype/typing-core/mode"; +./frontend/__tests__/input/helpers/fail-or-finish.spec.ts:13:vi.mock("@oxytype/typing-core/mode", async (importOriginal) => { +./frontend/__tests__/input/helpers/fail-or-finish.spec.ts:15: await importOriginal(); +./frontend/__tests__/input/handlers/insert-text.spec.ts:143:} from "@oxytype/typing-core/events/helpers"; +./frontend/__tests__/input/handlers/insert-text.spec.ts:144:import type { InputEventNoMs } from "@oxytype/typing-core/events/types"; +./frontend/__tests__/test/timer-scheduler.spec.ts:1:import { createTestTimer, type TimerTick } from "@oxytype/typing-core/timer"; +./frontend/__tests__/test/test-logic.spec.ts:6:import type { QuoteWithTextSplit } from "@oxytype/typing-core/quotes"; +./frontend/__tests__/test/test-logic.spec.ts:251:vi.mock("@oxytype/typing-core/result-validity", () => ({ +./frontend/__tests__/test/result.spec.ts:89:vi.mock("@oxytype/typing-core/events/stats", () => ({ +./frontend/__tests__/test/events/stats.spec.ts:75:} from "@oxytype/typing-core/events/helpers"; +./frontend/__tests__/test/events/stats.spec.ts:95:} from "@oxytype/typing-core/events/stats"; +./frontend/__tests__/test/events/stats.spec.ts:102:} from "@oxytype/typing-core/events/types"; +./frontend/__tests__/test/events/helpers.spec.ts:14:} from "@oxytype/typing-core/events/helpers"; +./frontend/__tests__/test/events/helpers.spec.ts:16:import type { InputEvent } from "@oxytype/typing-core/events/types"; +./frontend/__tests__/test/events/data.spec.ts:22:} from "@oxytype/typing-core/events/types"; +./frontend/__tests__/test/events/data.spec.ts:24:import { getEventsPerWord } from "@oxytype/typing-core/events/helpers"; +./frontend/__tests__/test/events/anticheat.spec.ts:13:import { calculateWpm } from "@oxytype/typing-core/stats-math"; +./frontend/__tests__/test/events/anticheat.spec.ts:17:} from "@oxytype/typing-core/events/types"; +./frontend/__tests__/test/events/anticheat.spec.ts:31:} from "@oxytype/typing-core/events/stats"; +./frontend/__tests__/test/characterization/replay.spec.ts:4:import type { EventLog } from "@oxytype/typing-core/events/types"; +./frontend/__tests__/test/characterization/lifecycle.spec.ts:3:import type { EventLog } from "@oxytype/typing-core/events/types"; +./frontend/__tests__/components/pages/test/result/ResultWordsHistory.spec.tsx:32:vi.mock("@oxytype/typing-core/events/stats", () => ({ +./frontend/__tests__/components/pages/test/result/ResultReplay.spec.tsx:1:import type { EventLog } from "@oxytype/typing-core/events/types"; +./frontend/__tests__/components/pages/test/result/ResultReplay.spec.tsx:32: const { getReplaySteps } = await import("@oxytype/typing-core/replay"); +./frontend/__tests__/components/pages/test/result/ResultReplay.spec.tsx:50:vi.mock("@oxytype/typing-core/events/helpers", async (importOriginal) => ({ +./frontend/__tests__/components/pages/test/result/ResultReplay.spec.tsx:52: typeof import("@oxytype/typing-core/events/helpers") +./frontend/__tests__/components/pages/test/result/ResultReplay.spec.tsx:56:vi.mock("@oxytype/typing-core/events/stats", () => ({ +./frontend/__tests__/components/pages/test/result/ResultChart.spec.tsx:16:vi.mock("@oxytype/typing-core/events/stats", () => ({ +./frontend/__tests__/queries/quote-ownership.spec.ts:2:import type { Quote } from "@oxytype/typing-core/quotes"; +./frontend/__tests__/components/modals/event-log-viewer/video-sync.spec.ts:21:import type { TestEventNoMs } from "@oxytype/typing-core/events/types"; +./frontend/__tests__/components/modals/event-log-viewer/timeline-lanes.spec.ts:18:} from "@oxytype/typing-core/events/types"; +./frontend/__tests__/components/modals/event-log-viewer/scatter-points.spec.ts:15:import type { TestEventNoMs } from "@oxytype/typing-core/events/types"; +./frontend/__tests__/components/modals/event-log-viewer/event-log.spec.ts:13:import type { TestEventNoMs } from "@oxytype/typing-core/events/types"; +``` + +## Fixture readers + +```text +packages/typing-core/README.md:72:`__fixtures__/keystrokes` contains raw input/timings, event logs and result +packages/typing-core/__test__/words-generator.spec.ts:126: "../../__fixtures__/words", +backend/scripts/anticheat-calibrate.ts:25: ["recorded-web", "packages/typing-core/__fixtures__/keystrokes"], +backend/scripts/anticheat-calibrate.ts:27: ["", "packages/typing-core/__fixtures__/anticheat"], +packages/typing-core/__test__/fixtures.ts:11: "../__fixtures__/keystrokes", +packages/typing-core/scripts/record-fixtures.ts:378:const outDir = join(here, "../__fixtures__/keystrokes"); +packages/typing-core/scripts/record-bot-fixtures.ts:10: * vp fmt packages/typing-core/__fixtures__/anticheat +packages/typing-core/scripts/record-bot-fixtures.ts:239: "../__fixtures__/anticheat", +packages/typing-core/__test__/completed-event.spec.ts:28: "../../__fixtures__/keystrokes", +backend/__tests__/anticheat/detectors.spec.ts:17: "../../../packages/typing-core/__fixtures__", +frontend/__tests__/test/words-generator-parity.spec.ts:7:// (packages/typing-core/__fixtures__/words/cases.json) runs with a seeded +frontend/__tests__/test/words-generator-parity.spec.ts:105: "../../../../packages/typing-core/__fixtures__/words", +frontend/__tests__/test/characterization/replay.spec.ts:36: "../../../../../packages/typing-core/__fixtures__/keystrokes/words-10-errors.json", +frontend/__tests__/components/modals/LastSignedOutResultModal.spec.tsx:45:import fixture from "../../../../packages/typing-core/__fixtures__/keystrokes/words-10-clean.json"; +``` + +## Browser rule paths + +All six `frontend/src/ts/input/handlers/*.ts` require migration: +insertion overrides/normalization, deletion penalties, progression, finish/failure, +beforeinput gates/limits, confidence/freedom, code unindent, physical keys/modifiers. +Helpers `word-navigation`, `fail-or-finish`, `validation`, `util` also own rules. +Composition lifecycle, textarea sentinel, Firefox deletion, layout overflow, visible +word windows and key translation are host observations, explicitly supplied to Rust. + +Other consumers: runtime initialization/test-logic/test-timer/completion.client, +words-generator, funbox hooks, pace, weak-spot, practice, today-tracker, replay, +result/chart-data/history/accuracy, event-log viewer and result components. + +## Funboxes + +Inventory identifiers/properties/forced config in `packages/funbox/src/list.ts`. +Rule hooks in `funbox-word-functions.ts` and `words-generator/funboxes.ts`; +browser hooks in runtime `funbox/funbox-functions.ts`. +Rule hooks move to Rust; visual/audio effects, fetch and notifications stay host. + +## Reference provenance + +SHA-256 of all current fixtures: `reference-manifest.json`. Never regenerate +snapshots/hashes to make a port pass. Existing backend anticheat consumers retained. + diff --git a/docs/rust-engine/progress.md b/docs/rust-engine/progress.md index 2421ed6e2..6cb95b86d 100644 --- a/docs/rust-engine/progress.md +++ b/docs/rust-engine/progress.md @@ -12,14 +12,14 @@ empty commits to inflate count. Do not publish production or releases implicitly ## Current position -Stage 1, Phase 1.1 in progress. Read root AGENTS.md and the complete migration +Stage 1, Phase 1.1 complete; Phase 1.2 in progress. Read root AGENTS.md and the complete migration plan before edits. Installed workspace dependencies with frozen lockfile. -No acceptance gate passed yet. Existing website engine remains authoritative. +Phase 1.1 gate passed: inventory, feature map, reproducible browser/performance baseline. Existing website engine remains authoritative. | Phase | Status | Evidence / remaining gate | | --- | --- | --- | -| 1.1 Inventory / baselines | Active | Core baseline: 28 files, 349 tests pass. Browser/performance baseline pending. | -| 1.2 Contracts / fixtures | Pending | Requires inventory and recorded baseline. | +| 1.1 Inventory / baselines | Complete | See inventory.md, features.md, baseline.md and raw measurements. | +| 1.2 Contracts / fixtures | Active | Freeze neutral command contract and differential harness. | | 1.3 Native / browser proof | Pending | Requires frozen contracts. | | 1.4 Input / lifecycle / timing | Pending | Requires production browser proof. | | 1.5 Generation / configuration | Pending | Requires contract and proof. | diff --git a/docs/rust-engine/reference-manifest.json b/docs/rust-engine/reference-manifest.json new file mode 100644 index 000000000..7b1cdb1b4 --- /dev/null +++ b/docs/rust-engine/reference-manifest.json @@ -0,0 +1,94 @@ +{ + "sourceCommit": "7b1ae05f204be53b5d9f2714f21c6eea08b25935", + "sha256": { + "packages/typing-core/__fixtures__/anticheat/fixed-interval.json": "49e27ae3010cd6d42517d8001229ff70e4e5bb2311513599de1d13ac94e22778", + "packages/typing-core/__fixtures__/anticheat/independent-gaussian.json": "bcc29d7cdaf61974284c0c4bfc993492a7c27d216d0cd6772c14ade8b4527581", + "packages/typing-core/__fixtures__/anticheat/independent-lognormal.json": "7a4623459c5def31b98c7548a33dc932b8694f4ae032dffa082955fbe4f7584f", + "packages/typing-core/__fixtures__/anticheat/model-hand-coarse-clock.json": "abeda67a6eef503294a9d0584056b1b1604b8c487fc638a3860ddc2b8e9abc6b", + "packages/typing-core/__fixtures__/anticheat/model-hand.json": "60a1b44799f5044304a57611d8dc6c62159f7db1a3b149234f229f9a369a4344", + "packages/typing-core/__fixtures__/anticheat/ms-jitter.json": "2783156a7aaecd6dfd8e670acff38f0319e70c6772bc22265c14bd49aed7571f", + "packages/typing-core/__fixtures__/anticheat/replayed-recording.json": "6bf7a12c15baf67854c539db6e5896bdb243b23b515770672f11b61811773223", + "packages/typing-core/__fixtures__/anticheat/scripted-playwright.json": "4b545366bf8a83492b1bfa4b2dbb597dd1e558a1db110e35ddf705b8ab2f1f7f", + "packages/typing-core/__fixtures__/anticheat/sub-ms-jitter.json": "e49642c327f70f80e325ba5fd5cac7ae604fdcb2498224b7181dd512aa7b3d07", + "packages/typing-core/__fixtures__/anticheat/uniform-range.json": "50ea9e84f812700dc04bb2375e795caca1ce3da27422bb432a31d6718bc204ed", + "packages/typing-core/__fixtures__/anticheat/value-pool.json": "2e4dc5275563dabf1fd99ba3bc76ff352f24d20392fea31e8c73fd1cbc617973", + "packages/typing-core/__fixtures__/keystrokes/__snapshots__/custom-repeat.json": "0067176be6ef63625e68a3ec9c3e23bda6a27607963dba2c175b17c180659c25", + "packages/typing-core/__fixtures__/keystrokes/__snapshots__/quote-short.json": "a12ae9a7b863638b71d325cc948b4bf6bc6d7caa981be8860f52448f5d028b11", + "packages/typing-core/__fixtures__/keystrokes/__snapshots__/time-15.json": "a3d815ea3c0b067041f9536b890eb3b9128f7253cf79b6b7d0869ead1d95c933", + "packages/typing-core/__fixtures__/keystrokes/__snapshots__/words-10-clean.json": "0bd9ec98683da1b4194c485a8de5fe264708a9d335f05953f2db79a2f5110005", + "packages/typing-core/__fixtures__/keystrokes/__snapshots__/words-10-errors.json": "2c6f639663fcd09c06cdde88627f58c8970b414e23ae77ed5ef166db7429fba2", + "packages/typing-core/__fixtures__/keystrokes/__snapshots__/words-10-punctuation-numbers.json": "be6733c13da1cccd012c15fc0c95d6adace3fc193750258f0c5ec0de2690bb0c", + "packages/typing-core/__fixtures__/keystrokes/__snapshots__/words-10-stop-on-letter.json": "5400a17d7491269e7f3f32dd9e01c8a9a09aaab49569482f265ad01e0af66e36", + "packages/typing-core/__fixtures__/keystrokes/__snapshots__/zen.json": "82c02edadfb1cf4dd104c161681a0c8e5807437b058de6e4545dd306977eb6b6", + "packages/typing-core/__fixtures__/keystrokes/custom-repeat.json": "f1737b55a17ab65699244ea753b63892867ccedcd801f021469a11f187833699", + "packages/typing-core/__fixtures__/keystrokes/quote-short.json": "fd4137dcec65daae0d153cfa55efa6262d1fcf2b41ee371f61ea6bc5e56259d0", + "packages/typing-core/__fixtures__/keystrokes/time-15.json": "2748818c903bddac123d623a3224dcc8708172adc04e4a2243b9ad6fa76db1c6", + "packages/typing-core/__fixtures__/keystrokes/words-10-clean.json": "80af3b5f5c0b78e51f1bbe87a9f70e8da1139d366b1098c82e6297ee2fa6a0b3", + "packages/typing-core/__fixtures__/keystrokes/words-10-errors.json": "439b8fdb7aa00cfac96c51a808e28a4e82e04168edb1f436ebc818b10e0d04f4", + "packages/typing-core/__fixtures__/keystrokes/words-10-punctuation-numbers.json": "b531f395292294b34b01a1a7579c28783a040ef999dcd6c182f098de79414a56", + "packages/typing-core/__fixtures__/keystrokes/words-10-stop-on-letter.json": "c000ab190fd3f8d1dd3f1fc662bf0cf5dfd36777d3903b0b651969a791612e6e", + "packages/typing-core/__fixtures__/keystrokes/zen.json": "d82a6c76fb585a9c074050ae53db465fedb205ed2d0084f308b5389cb0899273", + "packages/typing-core/__fixtures__/words/__snapshots__/custom-newlines-tabs.json": "565eba8c001ac99e03252d644330dc0bf2b7f05ee90adadeb2627e3d7a94dc61", + "packages/typing-core/__fixtures__/words/__snapshots__/custom-random.json": "c6620165e3acd050db4a4389a3a751879d85aac00c5fc42e64da25fd895a06a6", + "packages/typing-core/__fixtures__/words/__snapshots__/custom-repeat.json": "68d427aeef4f9a607f240b8edb7c44f3ed011b890edf51d571c3418b27d900eb", + "packages/typing-core/__fixtures__/words/__snapshots__/custom-repeated.json": "2d78795cc96ece06d64b723f026a3e9da14dfbf5f5b3a52de86b3dac3a5a6790", + "packages/typing-core/__fixtures__/words/__snapshots__/custom-sections.json": "28c070d9f4d0af30ae5abdde2931c61b8bd720dc22a6b59a7616cff37b7bbe39", + "packages/typing-core/__fixtures__/words/__snapshots__/custom-shuffle.json": "3e35bf7c10c6f501c25c2a0964566a99bfbfe19cf034a039b92f009da4327128", + "packages/typing-core/__fixtures__/words/__snapshots__/custom-time.json": "91222001936a44cbfc3b7ead2215ba5229d2043b406e90b03d7c0b0bcf81a5ce", + "packages/typing-core/__fixtures__/words/__snapshots__/funbox-58008.json": "56a86f30ac6b6603ad3dd01501968d27351eb8d83a206f0e2193a85e46527b86", + "packages/typing-core/__fixtures__/words/__snapshots__/funbox-ALL_CAPS.json": "bf1c1b9636c1674e642285ca664694c62831a1f9b858ded8108ec27d5637da58", + "packages/typing-core/__fixtures__/words/__snapshots__/funbox-IPv4.json": "c3fa29c2b8c1489be1ff6e3d02e2ec132bf97370a7823b1453326df6fa009c0c", + "packages/typing-core/__fixtures__/words/__snapshots__/funbox-IPv6.json": "ca89e0fc491b3a49d6a0953c0594cb43d8b0cff9dfd75ead1409231d8926ebe7", + "packages/typing-core/__fixtures__/words/__snapshots__/funbox-arrows.json": "88a7b3afbde9ee75fb013fa00cb900bb8a89ce7328b0765726b474b0c591049b", + "packages/typing-core/__fixtures__/words/__snapshots__/funbox-ascii.json": "9d6404cb0a01559234a75511050ed4fc0f10c457be271bcbf97c3dfc9465da0b", + "packages/typing-core/__fixtures__/words/__snapshots__/funbox-backwards-quote.json": "85ad310b3b1f03b5e733a4ed6be034af9a4ef3070c66620bc68ef61b940ce36f", + "packages/typing-core/__fixtures__/words/__snapshots__/funbox-backwards.json": "bd3cc497b3b87b78ccb046b84f69d8efd55538a5bd41c8263dca518028bcac4b", + "packages/typing-core/__fixtures__/words/__snapshots__/funbox-binary.json": "1b4d2e7543d3b2d03d3436afd88dfd78797d4983a89895439759493ad656ae99", + "packages/typing-core/__fixtures__/words/__snapshots__/funbox-capitals.json": "523a438133c2b0e29fc0bd4a00653154871460258d844129793885191f8ca2b4", + "packages/typing-core/__fixtures__/words/__snapshots__/funbox-ddoouubblleedd.json": "2a695ac0034f045667d3efd6174af08fc4b7fdaa0481802b30391f53293c85d3", + "packages/typing-core/__fixtures__/words/__snapshots__/funbox-gibberish.json": "fd1097dfcce6bbcdd2b3d2368b50f5cda401c9d1b94fb6055bb4c45b31b9f6bd", + "packages/typing-core/__fixtures__/words/__snapshots__/funbox-hexadecimal.json": "45eeab435699b75e5fbdcb4d8e0fc53d73ae087616cc2fd75b04fc4b7b60c7ea", + "packages/typing-core/__fixtures__/words/__snapshots__/funbox-instant_messaging.json": "c3519cf54b63fa91010bcb25ed8808bee9264eeccc833fa5b687621447d1ab17", + "packages/typing-core/__fixtures__/words/__snapshots__/funbox-morse.json": "6286f6b6c62ab18160a6f6b7674e1f43bd8b79cec6b6683d674d9882b78dd14e", + "packages/typing-core/__fixtures__/words/__snapshots__/funbox-nospace.json": "e4f001ccb475a0f56f98d822abdb68e229b81bd1253341e3b6f057c702ba8c7e", + "packages/typing-core/__fixtures__/words/__snapshots__/funbox-plus_one.json": "4779a76d486989551a559e30639602359c96064a8e298a96f973cdbabc59aff6", + "packages/typing-core/__fixtures__/words/__snapshots__/funbox-plus_three.json": "e2eb89d5e0ad9969f8e2c01966283d6e1bfab644a379be26fbc77a20f45944d6", + "packages/typing-core/__fixtures__/words/__snapshots__/funbox-polyglot.json": "1ac08bd09a0e075af32e2d8a09f2c802819d0e2268056076648f196b59fa7668", + "packages/typing-core/__fixtures__/words/__snapshots__/funbox-pseudolang.json": "3e6b8ba74aa42118db1e69069bb3951fef895194f654d872dd1865e5bd07eaa6", + "packages/typing-core/__fixtures__/words/__snapshots__/funbox-rAnDoMcAsE.json": "ad9c60843cb1af5a5e7c4b768ee573d66413a6216b2f9ec487f2d8e20c295f37", + "packages/typing-core/__fixtures__/words/__snapshots__/funbox-rot13-capitals-time.json": "b0873b969918b510671d063031ad5386f24551afe41878974c21bc8e7a40cd36", + "packages/typing-core/__fixtures__/words/__snapshots__/funbox-rot13.json": "95c9ec480cb193fadbb2da7477a12bc495be59a96c8056d8b7b7a11080fa7b0c", + "packages/typing-core/__fixtures__/words/__snapshots__/funbox-sPoNgEcAsE.json": "a85fb44d7105ef305f2badeff10b4d9e8df0bcb4f657f50082ee7d9adb8f2162", + "packages/typing-core/__fixtures__/words/__snapshots__/funbox-specials.json": "1e09f3ed31bd08160b47bd8ad39e61a58501d0ec119382617005d6cac7abf202", + "packages/typing-core/__fixtures__/words/__snapshots__/funbox-underscore_spaces.json": "fdbdd1e47411ee371e33382e6cbd5c69ff234cd068c9d0983361bd82f70fd922", + "packages/typing-core/__fixtures__/words/__snapshots__/funbox-weakspot.json": "9da4a413a1b0d7a504277cc64847ce08f062a70896cd078777a230df53b628d3", + "packages/typing-core/__fixtures__/words/__snapshots__/funbox-zipf.json": "34814f4c7cdaa650fe040e5bd212e1ee5c2e5e4b56a99e9c90a9807efbbbed59", + "packages/typing-core/__fixtures__/words/__snapshots__/quote-all.json": "89e74de559900c8426693b3665b81c5470616a29d0354a7dfc8f2dd888387f71", + "packages/typing-core/__fixtures__/words/__snapshots__/quote-british-english.json": "849d721de7e377dd6a8e2384be0228d79ce6f5d696574ddadf084ab1765240cc", + "packages/typing-core/__fixtures__/words/__snapshots__/quote-by-id.json": "2b298c10f489044626e0e1f5df598257b9b9f2eba4522597ddebed8cd2987d45", + "packages/typing-core/__fixtures__/words/__snapshots__/quote-repeated.json": "f00eb1986a48e5ac305578814a549f7baddff3ec191e61f2f5398173f3a03965", + "packages/typing-core/__fixtures__/words/__snapshots__/quote-short.json": "e19f7bd17071c47892c2dbb0ab7dee5bd947e434b9794d658cc9d97e7894c1e6", + "packages/typing-core/__fixtures__/words/__snapshots__/time-15-english-1k.json": "51a46c8616aac29bb55cdf11cae8fad7fc1d6bbbd36925e2206fc5ea12415d12", + "packages/typing-core/__fixtures__/words/__snapshots__/time-30.json": "426cd3b6d1e16dc2ade7436931f2eb22ab5c82867f0578ba2147f4bf0c59fa56", + "packages/typing-core/__fixtures__/words/__snapshots__/words-0-infinite.json": "62a0c9f4a03407693012e805874db1ca95a21ac2b2150b7b64f4568e8994e313", + "packages/typing-core/__fixtures__/words/__snapshots__/words-10.json": "836409db9c33a425cc1e072fe11ab113535316f5725111aa18a1ff04fba2089b", + "packages/typing-core/__fixtures__/words/__snapshots__/words-100-show-all-lines.json": "391e260eca1fde988198f85809361c75e5f0edcc9d1bd6fecdbfa3cde4badb5f", + "packages/typing-core/__fixtures__/words/__snapshots__/words-20-repeated.json": "df9e3d128a9fadbcec435bc24001d842136cfd78d22858f2c11661df9382d0c5", + "packages/typing-core/__fixtures__/words/__snapshots__/words-25-numbers.json": "ff9b4379b0539b883ad970466ad0c80b4122419adf4af4d7abba327f0ce662b4", + "packages/typing-core/__fixtures__/words/__snapshots__/words-25-punctuation.json": "0b7e8f75247b462e43c8dc1e16712e843f68241ca10c208da8050b22f9a1030a", + "packages/typing-core/__fixtures__/words/__snapshots__/words-30-arabic-numbers.json": "d24d0ab9599cba6405492137a626f1f0931df7ec09ab34d582a09e85d1c3ffc5", + "packages/typing-core/__fixtures__/words/__snapshots__/words-30-code-javascript-punctuation.json": "50c97aa8b875fad4ba184c5581b0f626eaa8154dfb05a8b07b2767cb8a6eae5e", + "packages/typing-core/__fixtures__/words/__snapshots__/words-30-french-punctuation.json": "9cc12784ca7412ac3306888ac688e5f3e760c7f86b5087def63b3df3ac9c388b", + "packages/typing-core/__fixtures__/words/__snapshots__/words-30-german.json": "63798e2fc2642830187a6ac075791bc05b7de822808fa4e89f3568b5f736d401", + "packages/typing-core/__fixtures__/words/__snapshots__/words-30-lazy-french.json": "a717093b083c611f53390ef24b311bb515c41ea66366227fb841a11c2e3b66ea", + "packages/typing-core/__fixtures__/words/__snapshots__/words-30-lazy-polish-punctuation.json": "5361754e5f1616d988a2eb54cbdffd6d2650a7994aa4939839b04a7ef4905267", + "packages/typing-core/__fixtures__/words/__snapshots__/words-30-spanish-punctuation.json": "987f3d4f7c3c9616dcd494213d1755870b184dfcb3f1481a9e005099c1b8fac5", + "packages/typing-core/__fixtures__/words/__snapshots__/words-30-swiss-german.json": "921171aa48a1988c6bb2103036b71dddeab4a224715ddd49a29bb763fd525f26", + "packages/typing-core/__fixtures__/words/__snapshots__/words-30-turkish-punctuation.json": "d5c5ec3450530d987ba6d4268c549c6e8cd0970b3afc3cbaefb589df58472fe6", + "packages/typing-core/__fixtures__/words/__snapshots__/words-50-british-english.json": "8073ca58f8da6f91e763cbca20459f46291154f3f59b05ef51982a0dd23e603f", + "packages/typing-core/__fixtures__/words/__snapshots__/words-50-english-punctuation-contractions.json": "3ad490a62ac90349dc366e326689fdfc39c707784abb77cd269b95e4d822a83a", + "packages/typing-core/__fixtures__/words/__snapshots__/words-50-punctuation-numbers.json": "06467c350027e18b51f14024bc3533854defd39e5ccc4ce8f3116876241a195a", + "packages/typing-core/__fixtures__/words/__snapshots__/zen.json": "5589ce2c71d92f84583d8af5b0bcc2496124582b2856a6db9772a19a22e7e3dd", + "packages/typing-core/__fixtures__/words/cases.json": "c92f32144415228ea531c7beb517bc61ea8f239565153ec962278f3cd8400942" + } +} diff --git a/frontend/e2e/engine-performance.migration.ts b/frontend/e2e/engine-performance.migration.ts new file mode 100644 index 000000000..74ab85e76 --- /dev/null +++ b/frontend/e2e/engine-performance.migration.ts @@ -0,0 +1,87 @@ +import { writeFile } from "node:fs/promises"; +import { expect, test } from "playwright/test"; + +// Same built Worker fixture as the website regressions. Freeze before porting. +test("typing engine baseline: input paint, restart, retained heap", async ({ + page, + context, +}) => { + const output = process.env["ENGINE_PERFORMANCE_OUTPUT"]; + if (output === undefined) throw new Error("Set ENGINE_PERFORMANCE_OUTPUT"); + await page.addInitScript(() => { + localStorage.setItem( + "acceptedCookies", + JSON.stringify({ security: false, analytics: false, sentry: false }), + ); + localStorage.setItem( + "config", + JSON.stringify({ mode: "zen", smoothCaret: "off", quickRestart: "off" }), + ); + }); + await page.goto("/"); + await expect(page.locator("#typingTest")).toHaveCSS("opacity", "1"); + await page.locator("#words").click(); + const cdp = await context.newCDPSession(page); + await cdp.send("Performance.enable"); + await cdp.send("HeapProfiler.collectGarbage"); + const initialHeap = (await cdp.send("Runtime.getHeapUsage")).usedSize; + const inputToPaint: number[] = []; + const restart: number[] = []; + for (let sample = 0; sample < 20; sample++) { + // Capture host beforeinput time; include rendering through the next frame. + await page.evaluate(() => { + const input = document.querySelector("#wordsInput"); + if (input === null) throw new Error("Missing input"); + input.addEventListener( + "beforeinput", + () => performance.mark("engine-input"), + { once: true }, + ); + }); + await page.keyboard.type("a"); + await expect(page.locator("#wordsInput")).toHaveValue(" a"); + inputToPaint.push( + await page.evaluate(async () => { + await new Promise((resolve) => + requestAnimationFrame(() => requestAnimationFrame(() => resolve())), + ); + return ( + performance.now() - + (performance.getEntriesByName("engine-input").at(-1)?.startTime ?? + performance.now()) + ); + }), + ); + const start = performance.now(); + await page.locator("#restartTestButton").click(); + await expect(page.locator("#typingTest")).toHaveCSS("opacity", "1"); + await expect(page.locator("#wordsInput")).toHaveValue(" "); + restart.push(performance.now() - start); + await page.locator("#words").click(); + } + // Real keyboard observations; no engine-internal shortcut for the long run. + await page.keyboard.type("a ".repeat(1000)); + await cdp.send("HeapProfiler.collectGarbage"); + const activeHeap = (await cdp.send("Runtime.getHeapUsage")).usedSize; + await page.locator("#restartTestButton").click(); + await expect(page.locator("#typingTest")).toHaveCSS("opacity", "1"); + await cdp.send("HeapProfiler.collectGarbage"); + const resetHeap = (await cdp.send("Runtime.getHeapUsage")).usedSize; + const assets = await page.evaluate(() => + performance + .getEntriesByType("resource") + .filter((entry) => /\.(js|wasm)(\?|$)/.test(entry.name)) + .map((entry) => { + const resource = entry as PerformanceResourceTiming; + return { + name: new URL(resource.name).pathname, + bytes: resource.decodedBodySize, + transfer: resource.transferSize, + }; + }), + ); + await writeFile( + output, + `${JSON.stringify({ browser: context.browser()?.version(), fixture: "built Start Worker; desktop Chromium; zen; 20 input/restart samples; double-rAF paint; 2000 keyboard characters; forced GC", inputToPaint, restart, initialHeap, activeHeap, resetHeap, assets }, null, 2)}\n`, + ); +}); From e5a1c398c542c62de9587f81a6898d58a992bfd8 Mon Sep 17 00:00:00 2001 From: Lakshmi Tanmay Date: Thu, 22 Oct 2026 23:50:45 +0530 Subject: [PATCH 03/93] test(engine): preserve immutable fixtures in neutral package --- packages/typing-fixtures/README.md | 12 + packages/typing-fixtures/package.json | 6 + .../reference/anticheat/fixed-interval.json | 50 + .../anticheat/independent-gaussian.json | 79 + .../anticheat/independent-lognormal.json | 78 + .../anticheat/model-hand-coarse-clock.json | 99 + .../reference/anticheat/model-hand.json | 193 ++ .../reference/anticheat/ms-jitter.json | 78 + .../anticheat/replayed-recording.json | 205 ++ .../anticheat/scripted-playwright.json | 55 + .../reference/anticheat/sub-ms-jitter.json | 82 + .../reference/anticheat/uniform-range.json | 78 + .../reference/anticheat/value-pool.json | 51 + .../__snapshots__/custom-repeat.json | 108 + .../keystrokes/__snapshots__/quote-short.json | 122 + .../keystrokes/__snapshots__/time-15.json | 128 + .../__snapshots__/words-10-clean.json | 99 + .../__snapshots__/words-10-errors.json | 110 + .../words-10-punctuation-numbers.json | 102 + .../words-10-stop-on-letter.json | 112 + .../keystrokes/__snapshots__/zen.json | 70 + .../reference/keystrokes/custom-repeat.json | 1645 ++++++++++ .../reference/keystrokes/quote-short.json | 2523 +++++++++++++++ .../reference/keystrokes/time-15.json | 2856 +++++++++++++++++ .../reference/keystrokes/words-10-clean.json | 1560 +++++++++ .../reference/keystrokes/words-10-errors.json | 2152 +++++++++++++ .../words-10-punctuation-numbers.json | 1855 +++++++++++ .../keystrokes/words-10-stop-on-letter.json | 2168 +++++++++++++ .../reference/keystrokes/zen.json | 1106 +++++++ .../__snapshots__/custom-newlines-tabs.json | 10 + .../words/__snapshots__/custom-random.json | 44 + .../words/__snapshots__/custom-repeat.json | 20 + .../words/__snapshots__/custom-repeated.json | 23 + .../words/__snapshots__/custom-sections.json | 25 + .../words/__snapshots__/custom-shuffle.json | 21 + .../words/__snapshots__/custom-time.json | 144 + .../words/__snapshots__/funbox-58008.json | 33 + .../words/__snapshots__/funbox-ALL_CAPS.json | 33 + .../words/__snapshots__/funbox-IPv4.json | 33 + .../words/__snapshots__/funbox-IPv6.json | 33 + .../words/__snapshots__/funbox-arrows.json | 33 + .../words/__snapshots__/funbox-ascii.json | 33 + .../__snapshots__/funbox-backwards-quote.json | 31 + .../words/__snapshots__/funbox-backwards.json | 33 + .../words/__snapshots__/funbox-binary.json | 33 + .../words/__snapshots__/funbox-capitals.json | 33 + .../__snapshots__/funbox-ddoouubblleedd.json | 33 + .../words/__snapshots__/funbox-gibberish.json | 33 + .../__snapshots__/funbox-hexadecimal.json | 33 + .../funbox-instant_messaging.json | 33 + .../words/__snapshots__/funbox-morse.json | 33 + .../words/__snapshots__/funbox-nospace.json | 33 + .../words/__snapshots__/funbox-plus_one.json | 10 + .../__snapshots__/funbox-plus_three.json | 10 + .../words/__snapshots__/funbox-polyglot.json | 44 + .../__snapshots__/funbox-pseudolang.json | 33 + .../__snapshots__/funbox-rAnDoMcAsE.json | 33 + .../funbox-rot13-capitals-time.json | 144 + .../words/__snapshots__/funbox-rot13.json | 33 + .../__snapshots__/funbox-sPoNgEcAsE.json | 33 + .../words/__snapshots__/funbox-specials.json | 33 + .../funbox-underscore_spaces.json | 33 + .../words/__snapshots__/funbox-weakspot.json | 33 + .../words/__snapshots__/funbox-zipf.json | 33 + .../words/__snapshots__/quote-all.json | 28 + .../__snapshots__/quote-british-english.json | 26 + .../words/__snapshots__/quote-by-id.json | 29 + .../words/__snapshots__/quote-repeated.json | 30 + .../words/__snapshots__/quote-short.json | 31 + .../__snapshots__/time-15-english-1k.json | 118 + .../words/__snapshots__/time-30.json | 169 + .../words/__snapshots__/words-0-infinite.json | 144 + .../words/__snapshots__/words-10.json | 21 + .../words-100-show-all-lines.json | 171 + .../__snapshots__/words-20-repeated.json | 33 + .../words/__snapshots__/words-25-numbers.json | 39 + .../__snapshots__/words-25-punctuation.json | 39 + .../words-30-arabic-numbers.json | 45 + .../words-30-code-javascript-punctuation.json | 44 + .../words-30-french-punctuation.json | 44 + .../words/__snapshots__/words-30-german.json | 44 + .../__snapshots__/words-30-lazy-french.json | 44 + .../words-30-lazy-polish-punctuation.json | 44 + .../words-30-spanish-punctuation.json | 44 + .../__snapshots__/words-30-swiss-german.json | 44 + .../words-30-turkish-punctuation.json | 44 + .../words-50-british-english.json | 65 + ...s-50-english-punctuation-contractions.json | 65 + .../words-50-punctuation-numbers.json | 65 + .../reference/words/__snapshots__/zen.json | 10 + .../reference/words/cases.json | 687 ++++ 91 files changed, 21234 insertions(+) create mode 100644 packages/typing-fixtures/README.md create mode 100644 packages/typing-fixtures/package.json create mode 100644 packages/typing-fixtures/reference/anticheat/fixed-interval.json create mode 100644 packages/typing-fixtures/reference/anticheat/independent-gaussian.json create mode 100644 packages/typing-fixtures/reference/anticheat/independent-lognormal.json create mode 100644 packages/typing-fixtures/reference/anticheat/model-hand-coarse-clock.json create mode 100644 packages/typing-fixtures/reference/anticheat/model-hand.json create mode 100644 packages/typing-fixtures/reference/anticheat/ms-jitter.json create mode 100644 packages/typing-fixtures/reference/anticheat/replayed-recording.json create mode 100644 packages/typing-fixtures/reference/anticheat/scripted-playwright.json create mode 100644 packages/typing-fixtures/reference/anticheat/sub-ms-jitter.json create mode 100644 packages/typing-fixtures/reference/anticheat/uniform-range.json create mode 100644 packages/typing-fixtures/reference/anticheat/value-pool.json create mode 100644 packages/typing-fixtures/reference/keystrokes/__snapshots__/custom-repeat.json create mode 100644 packages/typing-fixtures/reference/keystrokes/__snapshots__/quote-short.json create mode 100644 packages/typing-fixtures/reference/keystrokes/__snapshots__/time-15.json create mode 100644 packages/typing-fixtures/reference/keystrokes/__snapshots__/words-10-clean.json create mode 100644 packages/typing-fixtures/reference/keystrokes/__snapshots__/words-10-errors.json create mode 100644 packages/typing-fixtures/reference/keystrokes/__snapshots__/words-10-punctuation-numbers.json create mode 100644 packages/typing-fixtures/reference/keystrokes/__snapshots__/words-10-stop-on-letter.json create mode 100644 packages/typing-fixtures/reference/keystrokes/__snapshots__/zen.json create mode 100644 packages/typing-fixtures/reference/keystrokes/custom-repeat.json create mode 100644 packages/typing-fixtures/reference/keystrokes/quote-short.json create mode 100644 packages/typing-fixtures/reference/keystrokes/time-15.json create mode 100644 packages/typing-fixtures/reference/keystrokes/words-10-clean.json create mode 100644 packages/typing-fixtures/reference/keystrokes/words-10-errors.json create mode 100644 packages/typing-fixtures/reference/keystrokes/words-10-punctuation-numbers.json create mode 100644 packages/typing-fixtures/reference/keystrokes/words-10-stop-on-letter.json create mode 100644 packages/typing-fixtures/reference/keystrokes/zen.json create mode 100644 packages/typing-fixtures/reference/words/__snapshots__/custom-newlines-tabs.json create mode 100644 packages/typing-fixtures/reference/words/__snapshots__/custom-random.json create mode 100644 packages/typing-fixtures/reference/words/__snapshots__/custom-repeat.json create mode 100644 packages/typing-fixtures/reference/words/__snapshots__/custom-repeated.json create mode 100644 packages/typing-fixtures/reference/words/__snapshots__/custom-sections.json create mode 100644 packages/typing-fixtures/reference/words/__snapshots__/custom-shuffle.json create mode 100644 packages/typing-fixtures/reference/words/__snapshots__/custom-time.json create mode 100644 packages/typing-fixtures/reference/words/__snapshots__/funbox-58008.json create mode 100644 packages/typing-fixtures/reference/words/__snapshots__/funbox-ALL_CAPS.json create mode 100644 packages/typing-fixtures/reference/words/__snapshots__/funbox-IPv4.json create mode 100644 packages/typing-fixtures/reference/words/__snapshots__/funbox-IPv6.json create mode 100644 packages/typing-fixtures/reference/words/__snapshots__/funbox-arrows.json create mode 100644 packages/typing-fixtures/reference/words/__snapshots__/funbox-ascii.json create mode 100644 packages/typing-fixtures/reference/words/__snapshots__/funbox-backwards-quote.json create mode 100644 packages/typing-fixtures/reference/words/__snapshots__/funbox-backwards.json create mode 100644 packages/typing-fixtures/reference/words/__snapshots__/funbox-binary.json create mode 100644 packages/typing-fixtures/reference/words/__snapshots__/funbox-capitals.json create mode 100644 packages/typing-fixtures/reference/words/__snapshots__/funbox-ddoouubblleedd.json create mode 100644 packages/typing-fixtures/reference/words/__snapshots__/funbox-gibberish.json create mode 100644 packages/typing-fixtures/reference/words/__snapshots__/funbox-hexadecimal.json create mode 100644 packages/typing-fixtures/reference/words/__snapshots__/funbox-instant_messaging.json create mode 100644 packages/typing-fixtures/reference/words/__snapshots__/funbox-morse.json create mode 100644 packages/typing-fixtures/reference/words/__snapshots__/funbox-nospace.json create mode 100644 packages/typing-fixtures/reference/words/__snapshots__/funbox-plus_one.json create mode 100644 packages/typing-fixtures/reference/words/__snapshots__/funbox-plus_three.json create mode 100644 packages/typing-fixtures/reference/words/__snapshots__/funbox-polyglot.json create mode 100644 packages/typing-fixtures/reference/words/__snapshots__/funbox-pseudolang.json create mode 100644 packages/typing-fixtures/reference/words/__snapshots__/funbox-rAnDoMcAsE.json create mode 100644 packages/typing-fixtures/reference/words/__snapshots__/funbox-rot13-capitals-time.json create mode 100644 packages/typing-fixtures/reference/words/__snapshots__/funbox-rot13.json create mode 100644 packages/typing-fixtures/reference/words/__snapshots__/funbox-sPoNgEcAsE.json create mode 100644 packages/typing-fixtures/reference/words/__snapshots__/funbox-specials.json create mode 100644 packages/typing-fixtures/reference/words/__snapshots__/funbox-underscore_spaces.json create mode 100644 packages/typing-fixtures/reference/words/__snapshots__/funbox-weakspot.json create mode 100644 packages/typing-fixtures/reference/words/__snapshots__/funbox-zipf.json create mode 100644 packages/typing-fixtures/reference/words/__snapshots__/quote-all.json create mode 100644 packages/typing-fixtures/reference/words/__snapshots__/quote-british-english.json create mode 100644 packages/typing-fixtures/reference/words/__snapshots__/quote-by-id.json create mode 100644 packages/typing-fixtures/reference/words/__snapshots__/quote-repeated.json create mode 100644 packages/typing-fixtures/reference/words/__snapshots__/quote-short.json create mode 100644 packages/typing-fixtures/reference/words/__snapshots__/time-15-english-1k.json create mode 100644 packages/typing-fixtures/reference/words/__snapshots__/time-30.json create mode 100644 packages/typing-fixtures/reference/words/__snapshots__/words-0-infinite.json create mode 100644 packages/typing-fixtures/reference/words/__snapshots__/words-10.json create mode 100644 packages/typing-fixtures/reference/words/__snapshots__/words-100-show-all-lines.json create mode 100644 packages/typing-fixtures/reference/words/__snapshots__/words-20-repeated.json create mode 100644 packages/typing-fixtures/reference/words/__snapshots__/words-25-numbers.json create mode 100644 packages/typing-fixtures/reference/words/__snapshots__/words-25-punctuation.json create mode 100644 packages/typing-fixtures/reference/words/__snapshots__/words-30-arabic-numbers.json create mode 100644 packages/typing-fixtures/reference/words/__snapshots__/words-30-code-javascript-punctuation.json create mode 100644 packages/typing-fixtures/reference/words/__snapshots__/words-30-french-punctuation.json create mode 100644 packages/typing-fixtures/reference/words/__snapshots__/words-30-german.json create mode 100644 packages/typing-fixtures/reference/words/__snapshots__/words-30-lazy-french.json create mode 100644 packages/typing-fixtures/reference/words/__snapshots__/words-30-lazy-polish-punctuation.json create mode 100644 packages/typing-fixtures/reference/words/__snapshots__/words-30-spanish-punctuation.json create mode 100644 packages/typing-fixtures/reference/words/__snapshots__/words-30-swiss-german.json create mode 100644 packages/typing-fixtures/reference/words/__snapshots__/words-30-turkish-punctuation.json create mode 100644 packages/typing-fixtures/reference/words/__snapshots__/words-50-british-english.json create mode 100644 packages/typing-fixtures/reference/words/__snapshots__/words-50-english-punctuation-contractions.json create mode 100644 packages/typing-fixtures/reference/words/__snapshots__/words-50-punctuation-numbers.json create mode 100644 packages/typing-fixtures/reference/words/__snapshots__/zen.json create mode 100644 packages/typing-fixtures/reference/words/cases.json diff --git a/packages/typing-fixtures/README.md b/packages/typing-fixtures/README.md new file mode 100644 index 000000000..aa2b5250c --- /dev/null +++ b/packages/typing-fixtures/README.md @@ -0,0 +1,12 @@ +# Typing reference fixtures + +`reference/` is a byte-for-byte copy of `packages/typing-core/__fixtures__/` at +`7b1ae05f204be53b5d9f2714f21c6eea08b25935`. Preserve original files and provenance; +never regenerate these snapshots to make a new implementation pass. + +`commands/` holds explicit config/content/time/command traces. Text fields are +UTF-16 unit arrays, permitting isolated surrogate halves. `oracle/` holds frozen +TypeScript output at every command. New captures refuse to overwrite output. + +See `docs/rust-engine/contract.md` and `reference-manifest.json`. Fixture schemas +remain data contracts, independent of either engine implementation. diff --git a/packages/typing-fixtures/package.json b/packages/typing-fixtures/package.json new file mode 100644 index 000000000..5f0e5e1df --- /dev/null +++ b/packages/typing-fixtures/package.json @@ -0,0 +1,6 @@ +{ + "name": "@oxytype/typing-fixtures", + "private": true, + "description": "Immutable language-neutral typing engine reference data", + "license": "GPL-3.0" +} diff --git a/packages/typing-fixtures/reference/anticheat/fixed-interval.json b/packages/typing-fixtures/reference/anticheat/fixed-interval.json new file mode 100644 index 000000000..9f1a06d6d --- /dev/null +++ b/packages/typing-fixtures/reference/anticheat/fixed-interval.json @@ -0,0 +1,50 @@ +{ + "name": "fixed-interval", + "label": "bot", + "client": "web", + "description": "Every gap 80 ms and every hold 45 ms.", + "samples": [ + { + "keySpacing": [ + 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, + 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, + 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, + 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, + 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, + 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, + 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, + 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, + 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, + 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, + 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, + 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, + 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, + 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, + 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, + 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, + 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, + 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80 + ], + "keyDuration": [ + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45 + ] + } + ] +} diff --git a/packages/typing-fixtures/reference/anticheat/independent-gaussian.json b/packages/typing-fixtures/reference/anticheat/independent-gaussian.json new file mode 100644 index 000000000..c9fc20a58 --- /dev/null +++ b/packages/typing-fixtures/reference/anticheat/independent-gaussian.json @@ -0,0 +1,79 @@ +{ + "name": "independent-gaussian", + "label": "bot", + "client": "web", + "description": "Independent normal gaps (85±18 ms) and holds (45±9 ms).", + "samples": [ + { + "keySpacing": [ + 60.51, 75.7, 67.3, 85.8, 104.51, 103.59, 88.54, 118.04, 97.95, 120.4, + 66.69, 87.05, 41.17, 59.22, 58.05, 108.58, 82.14, 76.74, 85.44, 79.06, + 107.9, 112.54, 69.28, 43.51, 97.38, 104.18, 45.62, 93.75, 103.13, + 123.02, 88.88, 132.91, 89.94, 62.07, 43.79, 76.76, 85.93, 84, 87.52, 76, + 100.46, 107.81, 70.14, 105.72, 59.24, 80.31, 68.47, 71.12, 91.77, 82.43, + 88.55, 69.09, 110.14, 43.28, 74.51, 98.5, 85.83, 95.45, 66.24, 83.15, + 56.35, 108.7, 90.34, 101.64, 81.43, 43.66, 77.91, 68.87, 106.57, 70.1, + 88.5, 100.57, 76.78, 89.45, 63.75, 89.63, 73.61, 84.6, 89.57, 114.09, + 86.99, 102.16, 144.33, 67.13, 96.38, 90.17, 78.24, 98.57, 69.04, 86.71, + 44.87, 61.28, 74.42, 78.11, 100.19, 90.78, 77.69, 98.16, 96.61, 76.96, + 67.99, 94.75, 81.61, 85.6, 88.73, 102.23, 89.96, 78.48, 42.62, 69.55, + 83.13, 63.96, 92.63, 112.78, 89.6, 82.64, 76.08, 73.91, 100.36, 112.4, + 74.94, 103.01, 62.69, 52.11, 90.04, 86.16, 88.84, 82.35, 70.62, 85.8, + 83.62, 69.55, 80.25, 100.57, 94.49, 101.41, 106.39, 62.91, 71.79, + 112.88, 66.15, 122, 66.33, 89.3, 87.53, 89.59, 54.08, 95.79, 89.46, + 49.7, 61.96, 113.11, 79.67, 103.18, 91.92, 94.43, 106.18, 85.6, 82.18, + 110.27, 78.82, 67.27, 83.52, 94.95, 78.23, 96.7, 69.85, 82.96, 97.88, + 49.5, 67.01, 96.09, 103.17, 102.09, 70.63, 80.82, 97.37, 68.53, 123.78, + 77, 58.34, 80.8, 94.38, 94.56, 95.01, 115.07, 74.77, 76.21, 105.16, + 88.7, 64.11, 104.13, 79.35, 48.89, 75, 70.57, 63.76, 103.61, 57.08, + 131.26, 74.21, 74.54, 87.03, 92.26, 110.97, 68.16, 68.23, 87.85, 81.87, + 107.44, 88.75, 108.91, 119.07, 90.48, 101.91, 82.97, 90.79, 85.96, + 70.01, 64.17, 95.16, 73.75, 76.99, 69.55, 94.35, 89.85, 90.14, 84.67, + 68.81, 81.32, 93.97, 60.7, 63, 110.36, 80.98, 65.45, 84.32, 84.97, + 108.04, 79.91, 81.64, 121.91, 92.05, 35.97, 88.57, 74.13, 98.65, 80.87, + 70.65, 81.86, 82.72, 68.26, 82.74, 93.28, 79.63, 102.77, 96.64, 76.24, + 60, 84.84, 60.19, 124.56, 73.61, 103.49, 61.04, 79.75, 76.23, 92.71, + 61.56, 91.46, 95.36, 86.3, 73.55, 93.66, 94.02, 68.55, 95.02, 102.82, + 101.62, 66.62, 97.41, 41.16, 120.4, 77.91, 72.57, 119.41, 84.49, 91.43, + 80.24, 76.16, 103.53, 70.09, 87, 112.05, 75.06, 101.81, 66.26, 51.98, + 82.3, 66.8, 88.88, 86.09, 89.14, 92.07, 64.84, 117.42, 72.1, 65.05, + 80.45, 103.57, 78.17, 114.88, 88.62, 73.19, 90.02, 99.22, 85.61, 72.66, + 108.94 + ], + "keyDuration": [ + 40.99, 45.19, 38.23, 45.69, 35.01, 49.47, 26.15, 46.48, 40.45, 51.58, + 42.92, 45.28, 37.04, 41.68, 45.53, 42.33, 57.81, 37.72, 56.44, 48.92, + 44.95, 49.05, 48.23, 52.55, 60.42, 25.87, 55.45, 47.28, 41.58, 50.52, + 54.62, 42.41, 48.05, 52.36, 45.69, 37.26, 28.56, 56.24, 45.63, 40.89, + 48.57, 56.12, 53.54, 60.07, 38.58, 61.88, 60.29, 44.01, 23.47, 52.23, + 44.56, 41.11, 31.12, 49.47, 42.04, 49.38, 41.48, 55.78, 41.36, 46.42, + 61.55, 46.71, 41.18, 40.68, 27.2, 43.07, 52.13, 38.82, 51.87, 47.1, + 58.01, 42.01, 36.77, 38.14, 30.62, 37.55, 41.2, 44.7, 48.34, 41.64, + 49.35, 34.89, 51.71, 47.75, 51.05, 55.04, 45.23, 31.87, 42.17, 45.7, + 62.42, 21.8, 39.27, 27.77, 47.92, 44.8, 36.4, 35.94, 45.95, 50.02, + 31.05, 36.12, 34.22, 52.57, 52.79, 57.36, 55.14, 42.6, 41.02, 64.72, + 48.8, 51.13, 43.19, 63.56, 52.63, 16.45, 30.58, 41.63, 32.8, 52.92, + 48.48, 48.52, 46.42, 35.66, 51.21, 44.55, 23.7, 30.59, 48.34, 43.7, + 38.56, 43.12, 40.93, 46.01, 48.19, 40.1, 46.13, 37.43, 57.7, 49.39, + 40.42, 40.81, 50.84, 42.3, 60.85, 50.06, 43.64, 57.42, 42.68, 55.53, + 53.45, 54.58, 47.41, 57.5, 35.12, 44.52, 23.69, 44.66, 27.38, 41.97, + 47.72, 56.42, 46.93, 48.71, 42.81, 43.15, 28.58, 54.98, 28.16, 43.69, + 52.51, 42.7, 32.17, 48.78, 33.88, 58.97, 44.3, 53.78, 48.09, 45.27, + 28.45, 43.96, 49.15, 36.48, 44.6, 39.39, 33.78, 40.91, 27.57, 48.09, + 41.21, 48.75, 53.63, 48.25, 39.3, 40.59, 61.38, 39.71, 52.69, 34.03, + 47.04, 37.62, 36.57, 31.88, 45.06, 28.17, 44.57, 55.95, 47.8, 54.04, + 31.06, 29.64, 56.44, 45.18, 53.54, 36.94, 44.66, 64.7, 62.02, 50.33, + 35.25, 41.27, 48.04, 35.34, 38.03, 32.57, 52.36, 50.83, 39.54, 41.53, + 44.86, 49.47, 44.29, 49.4, 48.99, 41.44, 44.63, 47.38, 37.67, 42.42, + 51.57, 42.27, 44.03, 69.01, 66.36, 37.14, 54.93, 40.49, 50.59, 34.68, + 53.36, 38.55, 44.77, 39.57, 52.12, 46.94, 39.57, 32.35, 49.31, 50.2, + 32.02, 50.11, 52.82, 38.8, 40.05, 54.07, 43.18, 52.92, 69.88, 50.59, + 37.93, 49.81, 35.46, 35.71, 47.81, 51.67, 48.23, 42.38, 42.36, 48, + 42.04, 42.57, 51.42, 40.15, 32.72, 46.4, 33.62, 47.52, 34.99, 29.23, + 32.4, 48.2, 48.95, 60.35, 42.1, 46.79, 43.03, 31.17, 43.48, 47.54, + 54.74, 55.6, 53.21, 41.91, 44.6, 60.67, 46.12, 49.92, 58.05, 48.47, + 45.91, 44.04, 50.84, 38.08, 34.78, 36.68, 54.4, 51.65, 35.4, 39.57 + ] + } + ] +} diff --git a/packages/typing-fixtures/reference/anticheat/independent-lognormal.json b/packages/typing-fixtures/reference/anticheat/independent-lognormal.json new file mode 100644 index 000000000..844a9d608 --- /dev/null +++ b/packages/typing-fixtures/reference/anticheat/independent-lognormal.json @@ -0,0 +1,78 @@ +{ + "name": "independent-lognormal", + "label": "bot", + "client": "web", + "description": "Human-shaped lognormal gaps and holds, but every draw independent.", + "samples": [ + { + "keySpacing": [ + 110.2, 103.56, 73.09, 122.86, 53.21, 64.39, 124.28, 75.45, 66.2, 104.59, + 68.52, 84.74, 70.05, 135.49, 47.22, 109.91, 75.96, 130.83, 70.14, 74.46, + 109.57, 94.9, 68.38, 88.67, 63.08, 66.41, 93.09, 72.82, 61.23, 105.16, + 90.51, 97.33, 67.34, 79.48, 83.02, 65.48, 85.26, 76.78, 71.25, 72.68, + 93.27, 43.92, 69.65, 74.55, 92.06, 74.83, 107.72, 95.03, 51.85, 87.02, + 117.71, 106.08, 109.51, 121.3, 97.74, 99.47, 114.8, 90.93, 118.86, + 110.49, 66.61, 123.85, 87.55, 62.75, 70.58, 94.33, 64.12, 94.57, 196.44, + 91.36, 91.75, 92.44, 52.56, 88.39, 54.88, 104.14, 37.26, 71.55, 65, + 89.11, 62.42, 114.34, 43.93, 103.15, 107.6, 109.3, 93.7, 80.62, 97.92, + 108.76, 98.08, 97.55, 73.44, 61.81, 91.73, 60.24, 82.94, 82.08, 43.82, + 57.45, 76.77, 55.74, 81.49, 89.04, 84.06, 83.17, 53.73, 127.74, 85.81, + 138.84, 96.76, 136.56, 61.19, 80.97, 78.56, 68.24, 99.13, 64.64, 64.97, + 80.45, 97.54, 87.96, 121.07, 65.15, 98.31, 70.38, 107.92, 84.88, 91.48, + 73.18, 37.34, 77.47, 72.16, 64.25, 91.89, 69.66, 117.35, 55.26, 64.51, + 139.94, 62.84, 57.86, 80.88, 86.22, 81.76, 57.19, 69.56, 95.6, 99.37, + 83.85, 87.82, 87.89, 110.22, 65.16, 39.12, 48.58, 56.21, 79.41, 109.26, + 55.25, 75.6, 73.53, 67.26, 67.26, 52.37, 126.35, 78.8, 98.56, 60.87, + 122.26, 72.85, 62.89, 45.72, 57.77, 50.6, 75.12, 112.57, 68.91, 92.6, + 79.83, 51.15, 72.8, 114.21, 96.61, 63.99, 109.89, 98.41, 85.23, 93.55, + 79.32, 104.55, 72.65, 84.95, 86.7, 110.43, 104.24, 68.87, 89.27, 71.86, + 75.66, 52.28, 62.38, 70.28, 81.25, 97.42, 96.35, 77.54, 115.46, 102.22, + 102.12, 90.38, 114.58, 90.14, 105.33, 125.56, 73.48, 85.2, 73.56, 52.14, + 79.13, 109.12, 105.15, 94.76, 69.28, 115.71, 53.15, 79.68, 72.26, 87.97, + 65.04, 56.63, 89.38, 59.91, 91.12, 100.2, 82.32, 61.39, 86.01, 57.14, + 59.39, 120.8, 95.78, 46.42, 92.45, 93.9, 58.39, 114.66, 75.91, 98.59, + 132.92, 57.51, 79.72, 79.21, 91.69, 79.43, 79.51, 71.48, 58.25, 45.74, + 113.57, 93.04, 66.41, 97, 67.86, 77.16, 70.79, 102.86, 99.47, 50.6, + 52.54, 93.23, 96.2, 81.92, 131.01, 78.61, 77.05, 56.92, 117.6, 79.59, + 92.43, 89.1, 68.27, 53.1, 66.44, 46.8, 44.67, 101.51, 81.9, 68.96, + 88.73, 79.31, 109.8, 87.93, 103.69, 56.66, 74.35, 52.61, 85.06, 37.7, + 81.6, 78.38, 62.5, 150.33, 125.78, 73.99, 50.51, 70.54, 54.44, 106, + 89.03, 65.17, 100.44, 61.49, 81.37, 67.39, 96.86, 51.74, 159.11, 80.12 + ], + "keyDuration": [ + 64.93, 91.3, 68.06, 69.35, 35.88, 57.56, 45.39, 54.67, 69.64, 104.58, + 60.56, 74.79, 69.08, 72.71, 81.32, 69.3, 70.91, 75.17, 58.29, 68.53, + 95.05, 81.54, 45.86, 57.07, 83.18, 66.91, 72.19, 54.46, 81.29, 84.55, + 45.92, 78.09, 89.63, 81.29, 77.53, 47.53, 54.43, 83.62, 88.11, 92.64, + 57.44, 72.23, 81.85, 73.4, 76.86, 72.37, 86.2, 82.85, 104.12, 74.07, + 69.73, 60.46, 74.84, 61.04, 56.71, 49.12, 65.5, 59.18, 76.71, 68.85, + 77.85, 91.18, 59.35, 78.48, 77.72, 56.54, 59.78, 68.7, 61.04, 78, 52.4, + 79.51, 49.59, 99.08, 68.46, 58.1, 69.4, 60.83, 71.33, 102.81, 68.53, + 63.31, 60.28, 64.71, 73.55, 80.71, 72.32, 69.87, 95.28, 73.77, 74.44, + 85.01, 79.06, 79.93, 84.78, 73.55, 70.67, 87.63, 73.3, 71.51, 73.86, + 54.4, 64.09, 34.31, 65.06, 59.97, 47.27, 71.35, 90.83, 65.16, 75.74, + 90.45, 60.57, 63.03, 57.74, 57.93, 70.73, 79.79, 72.15, 57.06, 61.3, + 52.33, 73.96, 66.1, 77.95, 84.13, 79.07, 77.57, 76.39, 86.3, 82.76, + 48.52, 59.08, 78.07, 79.02, 46.53, 74.2, 64.61, 103.18, 80.57, 73.32, + 60.12, 58.08, 86.37, 81.44, 83.29, 48.77, 56.17, 67.95, 86.69, 64.61, + 70.67, 75.36, 87.55, 72.88, 89.49, 82.05, 88.21, 70.78, 76.7, 86.56, + 57.1, 60.56, 79.37, 60.64, 77.25, 67.16, 70.47, 85.49, 53.58, 55.97, + 64.44, 82.94, 66.31, 77.59, 64.97, 58.88, 46.83, 55.99, 60.53, 49.26, + 83.65, 70.06, 77.76, 76.98, 79.95, 54.5, 83.69, 63.07, 64.08, 54.45, + 50.87, 75.31, 73.11, 87.06, 61.68, 56.85, 78.42, 67.39, 84.04, 57.44, + 80.13, 49.25, 50.67, 33.76, 78.54, 65.85, 69.58, 53.64, 87.58, 41.9, + 87.86, 87.78, 60.91, 62.38, 69.72, 82.88, 63.37, 58.75, 61.98, 63.93, + 101.04, 70.96, 66.18, 83.37, 59.78, 71.77, 49.34, 53.73, 56.99, 63.84, + 53.73, 42.4, 93.77, 88.46, 59.54, 56.23, 73.97, 50.48, 79.37, 74.12, + 64.68, 73.7, 79.66, 72.35, 54.45, 94.84, 53.5, 98.55, 88.75, 65.81, + 62.66, 62.15, 92.62, 67.29, 68.82, 76.89, 57.02, 77.65, 63.33, 63.93, + 98.14, 69.33, 52.7, 70.63, 71.33, 74.59, 47.01, 110.35, 74.35, 64.98, + 60.67, 61.78, 83.11, 59.77, 59.66, 69.71, 52.47, 75.79, 84.84, 67.37, + 63.63, 78.84, 65.11, 91.22, 58.73, 66.9, 58.53, 61.91, 58.3, 91.42, + 61.22, 57.28, 76.14, 99.69, 80.88, 76.45, 70.43, 77.26, 69.57, 66.96, + 90.1, 65.88, 84.14, 90.19, 62.89, 58.88, 64.7, 76.43, 73.63, 73.36, + 68.71, 75.91, 73.63, 58.24, 93.56, 76.83, 50.4, 68.87, 78.89 + ] + } + ] +} diff --git a/packages/typing-fixtures/reference/anticheat/model-hand-coarse-clock.json b/packages/typing-fixtures/reference/anticheat/model-hand-coarse-clock.json new file mode 100644 index 000000000..1387c1be3 --- /dev/null +++ b/packages/typing-fixtures/reference/anticheat/model-hand-coarse-clock.json @@ -0,0 +1,99 @@ +{ + "name": "model-hand-coarse-clock", + "label": "model", + "client": "web", + "description": "Modelled hands on a 1 ms clock, as some browsers coarsen it.", + "samples": [ + { + "keySpacing": [ + 81, 134, 92, 96, 45, 62, 82, 722, 52, 77, 100, 75, 51, 58, 872, 69, 60, + 60, 80, 78, 62, 92, 79, 80, 91, 96, 129, 77, 99, 61, 104, 91, 55, 74, + 71, 67, 44, 78, 106, 54, 65, 77, 142, 117, 510, 143, 113, 69, 1056, 93, + 80, 67, 61, 85, 92, 59, 81, 64, 83, 57, 52, 110, 69, 117, 42, 47, 66, + 161, 87, 117, 85, 84, 68, 88, 112, 100, 113, 162, 95, 132, 54, 82, 71, + 115, 157, 72, 815, 83, 91, 82, 126, 44, 106, 113, 114, 86, 87, 125, 73, + 55, 79, 89, 54, 70, 61, 86, 101, 123, 95, 95, 84, 114, 78, 68, 107, 80, + 74, 65, 143, 115, 63, 74, 110, 89, 65, 934, 41, 57, 68, 85, 76, 61, 76, + 68, 63, 63, 97, 100, 102, 135, 89, 101, 40, 67, 1020, 103, 138, 95, 75, + 114, 78, 74, 81, 62, 137, 135, 108, 121, 90, 52, 107, 73, 101, 65, 51, + 100, 91, 81, 97, 95, 91, 116, 73, 47, 55, 75, 115, 61, 102, 104, 99, + 122, 66, 148, 89, 141, 75, 99, 80, 77, 82, 77, 131, 97, 60, 93, 70, 98, + 70, 83, 82, 88, 81, 61, 95, 85, 61, 112, 79, 94, 63, 74, 88, 87, 94, 62, + 46, 73, 98, 72, 150, 92, 93, 160, 95, 117, 108, 70, 82, 86, 87, 80, 123, + 101, 92, 95, 160, 139, 178, 125, 79, 183, 73, 79, 59, 166, 82, 62, 66, + 41, 59, 57, 48, 108, 96, 130, 63, 63, 102, 72, 67, 71, 66, 82, 49, 83, + 67, 57, 46, 144, 55, 79, 65, 73, 106, 71, 58, 96, 58, 78, 101, 91, 106, + 91, 101, 65, 108, 621, 119, 141, 91, 47, 62, 71, 56, 118, 63, 94, 102, + 130, 68, 73, 81, 520, 56, 61, 66, 774, 84, 134, 92, 102, 69, 85, 116, + 91, 106, 101, 72 + ], + "keyDuration": [ + 58, 97, 74, 75, 85, 47, 53, 46, 54, 66, 89, 84, 64, 63, 90, 56, 67, 140, + 51, 77, 82, 63, 82, 65, 87, 82, 119, 97, 81, 94, 91, 61, 56, 62, 79, + 104, 58, 59, 56, 65, 74, 98, 145, 88, 76, 76, 73, 83, 88, 63, 68, 93, + 70, 72, 70, 67, 61, 83, 53, 55, 61, 83, 79, 80, 65, 63, 92, 62, 72, 103, + 122, 45, 63, 72, 102, 94, 89, 85, 103, 67, 84, 72, 88, 86, 68, 64, 58, + 55, 64, 96, 97, 75, 94, 100, 71, 63, 81, 60, 62, 69, 61, 71, 70, 80, 51, + 78, 53, 84, 92, 58, 70, 61, 73, 81, 85, 104, 79, 93, 87, 48, 82, 91, 66, + 71, 80, 60, 81, 90, 76, 68, 66, 67, 82, 70, 57, 48, 63, 72, 62, 65, 99, + 64, 58, 56, 51, 85, 74, 86, 61, 90, 92, 59, 72, 98, 70, 84, 106, 98, 87, + 101, 87, 88, 63, 74, 83, 57, 99, 57, 50, 74, 65, 98, 80, 79, 61, 85, 83, + 63, 65, 65, 68, 49, 87, 107, 63, 86, 67, 75, 69, 67, 83, 75, 97, 65, + 109, 90, 72, 80, 86, 83, 55, 74, 78, 93, 103, 66, 75, 43, 71, 63, 48, + 78, 71, 59, 106, 53, 60, 94, 85, 104, 61, 45, 67, 108, 106, 126, 88, 86, + 54, 68, 76, 60, 73, 94, 78, 91, 108, 103, 70, 79, 46, 75, 57, 64, 109, + 92, 85, 61, 41, 57, 65, 62, 81, 58, 81, 50, 58, 54, 69, 82, 56, 68, 59, + 58, 65, 75, 50, 50, 68, 87, 49, 70, 68, 86, 83, 94, 96, 69, 73, 74, 74, + 108, 116, 86, 74, 96, 94, 64, 88, 55, 83, 77, 74, 76, 72, 87, 65, 73, + 74, 87, 46, 75, 60, 59, 50, 59, 57, 67, 58, 85, 76, 55, 78, 95, 63, 73, + 93, 106, 70, 89 + ] + }, + { + "keySpacing": [ + 87, 86, 102, 718, 55, 76, 89, 70, 126, 167, 78, 114, 82, 215, 124, 97, + 106, 59, 114, 71, 697, 96, 109, 105, 109, 105, 108, 101, 179, 123, 114, + 84, 90, 97, 99, 120, 92, 67, 79, 88, 534, 112, 114, 90, 112, 992, 79, + 64, 90, 100, 72, 84, 169, 89, 88, 119, 678, 161, 150, 147, 142, 133, 70, + 109, 95, 88, 77, 87, 107, 75, 156, 76, 52, 104, 133, 96, 123, 64, 89, + 70, 94, 89, 109, 79, 154, 61, 107, 136, 98, 649, 93, 123, 114, 98, 128, + 134, 138, 100, 86, 78, 67, 106, 87, 89, 155, 168, 221, 151, 154, 124, + 100, 104, 83, 66, 92, 137, 124, 102, 102, 89, 728, 102, 101, 75, 71, + 135, 118, 126, 197, 587, 77, 106, 140, 81, 122, 94, 719, 118, 113, 99, + 80, 153, 149, 95, 88, 189, 120, 106, 111, 82, 148, 102, 119, 108, 97, + 75, 71, 91, 40, 716, 86, 56, 91, 127, 73, 92, 53, 93, 119, 64, 111, 99, + 78, 129, 71, 110, 172, 77, 105, 60, 77, 116, 97, 86, 83, 84, 84, 60, 68, + 114, 96, 81, 107, 136, 89, 115, 50, 90, 68, 88, 86, 92, 63, 116, 85, 97, + 77, 94, 104, 115, 72, 83, 133, 192, 86, 156, 70, 107, 112, 127, 102, + 178, 106, 168, 97, 97, 78, 115, 105, 96, 132, 84, 71, 97, 100, 197, 78, + 70, 102, 107, 100, 135, 84, 139, 65, 66, 50, 61, 100, 71, 48, 88, 92, + 78, 53, 69, 99, 131, 95, 131, 162, 137, 102, 88, 56, 57, 180, 101, 133, + 92, 133, 93, 87, 109, 90, 135, 127, 144, 85, 110, 99, 96, 95, 50, 1011, + 68, 89, 84, 63, 70, 93, 67, 155, 111, 104, 120, 76, 65, 133, 89, 79, + 100, 111, 74, 132, 134, 70, 117, 109, 118, 62, 88, 101, 85, 87, 80, 67, + 57, 80 + ], + "keyDuration": [ + 91, 80, 80, 50, 75, 92, 92, 85, 111, 100, 88, 110, 103, 127, 85, 76, 87, + 61, 93, 67, 70, 119, 94, 102, 125, 67, 100, 92, 105, 61, 85, 93, 87, 54, + 94, 87, 74, 67, 64, 90, 91, 87, 66, 82, 88, 93, 120, 75, 100, 71, 94, + 117, 87, 110, 108, 115, 128, 98, 107, 155, 138, 93, 86, 88, 56, 57, 109, + 94, 95, 88, 66, 107, 62, 78, 90, 96, 64, 63, 58, 62, 66, 70, 111, 72, + 93, 73, 102, 85, 71, 117, 84, 100, 114, 66, 91, 78, 77, 69, 101, 58, 63, + 65, 75, 159, 98, 90, 93, 79, 90, 91, 80, 131, 74, 75, 116, 76, 68, 99, + 70, 84, 112, 96, 91, 75, 98, 72, 80, 104, 181, 70, 127, 191, 127, 76, + 76, 62, 115, 157, 93, 89, 107, 118, 96, 81, 94, 94, 80, 78, 78, 78, 95, + 98, 79, 94, 85, 75, 134, 64, 87, 72, 86, 76, 66, 63, 62, 74, 77, 104, + 107, 136, 101, 77, 80, 134, 98, 73, 133, 73, 54, 67, 99, 74, 86, 95, 74, + 60, 99, 82, 80, 107, 122, 99, 89, 144, 102, 92, 94, 69, 82, 76, 57, 117, + 67, 86, 70, 98, 145, 74, 94, 77, 86, 80, 102, 98, 145, 85, 117, 101, 93, + 122, 95, 102, 102, 114, 115, 74, 108, 99, 62, 143, 103, 110, 102, 128, + 80, 72, 102, 69, 81, 73, 104, 92, 122, 75, 58, 88, 55, 94, 85, 65, 77, + 95, 69, 92, 59, 85, 102, 76, 76, 103, 89, 60, 122, 97, 83, 53, 69, 88, + 68, 97, 87, 96, 79, 69, 106, 104, 180, 78, 133, 112, 77, 69, 98, 68, 88, + 86, 56, 64, 63, 68, 71, 95, 74, 80, 73, 82, 72, 98, 86, 67, 93, 104, 94, + 53, 93, 106, 85, 66, 77, 104, 84, 63, 84, 63, 82, 95, 100, 70, 114, 119 + ] + } + ] +} diff --git a/packages/typing-fixtures/reference/anticheat/model-hand.json b/packages/typing-fixtures/reference/anticheat/model-hand.json new file mode 100644 index 000000000..b724d83f6 --- /dev/null +++ b/packages/typing-fixtures/reference/anticheat/model-hand.json @@ -0,0 +1,193 @@ +{ + "name": "model-hand", + "label": "model", + "client": "web", + "description": "Three modelled hands at 70, 85 and 100 ms per key with 0.1 ms timestamps.", + "samples": [ + { + "keySpacing": [ + 72.7, 69.2, 46.2, 66, 50, 42.8, 107, 70.8, 41.3, 38.4, 79.2, 92.1, + 971.3, 34.7, 44.6, 91.4, 68.5, 62.8, 73.4, 60.4, 123.2, 80.2, 117.8, + 48.4, 52.8, 84.1, 88.8, 130.2, 86.3, 811.6, 83.7, 46.4, 70.3, 112.8, + 59.9, 39, 41.3, 64.3, 55.4, 79.2, 54.1, 61.8, 54, 54, 65.6, 40.2, 62.5, + 86.8, 81.3, 64, 78.6, 102.8, 189.4, 93.6, 109.2, 83, 77.8, 96.4, 121.5, + 123.2, 69.5, 53.8, 51.9, 66.3, 70.3, 99, 108.5, 117.7, 84.5, 83.6, 97.5, + 92.1, 59, 51.4, 112.7, 81.4, 1056.8, 114.1, 117.1, 728.3, 84.4, 73.2, + 116.3, 73, 48.5, 94.2, 58.6, 51.2, 56.8, 43, 66.9, 55.9, 101.1, 67.8, + 64.7, 65.9, 62.7, 55.6, 64.1, 61.8, 65.1, 54.3, 51.5, 77.6, 78.2, 86.9, + 74.7, 70, 119.9, 117.1, 80, 83.4, 56.9, 88.9, 73.8, 66.6, 108.4, 69.5, + 76.3, 82.2, 35.9, 62.1, 44.5, 483.4, 57.3, 56.1, 70.5, 57.7, 97.9, 74.8, + 60.9, 62.8, 64.6, 97, 67.2, 111.2, 55.9, 52.3, 83, 80, 116, 967.3, 85.4, + 126.2, 83.7, 49.4, 105.7, 139.9, 132.5, 94.9, 110.4, 95.2, 67.1, 735.1, + 97.6, 126.7, 128.7, 66.7, 48.4, 58.8, 75.1, 48.3, 56.4, 115.8, 112, + 92.7, 103.1, 147.6, 103.9, 85.3, 171.3, 115.1, 76.1, 60.1, 119.2, 87.9, + 70, 100, 77.4, 56.9, 84, 126, 66.2, 98, 55.3, 52.1, 624.7, 53.6, 105.7, + 67.4, 63, 95.2, 83.6, 62.7, 62.5, 58.3, 66.1, 92.4, 68.7, 104.6, 51.5, + 47.8, 69.1, 72.6, 95.8, 81.4, 72.1, 158.3, 114.9, 83, 55.6, 57.9, 65.9, + 37.8, 74.4, 87.6, 61.6, 789.6, 65.6, 72.1, 87, 47.7, 83.2, 71.3, 111.5, + 73.5, 64, 51.7, 80.8, 77.7, 64, 87.8, 88.5, 62.6, 78, 72.2, 117.8, 79.1, + 103.3, 76.2, 57.5, 82.1, 87.4, 107, 79.9, 92.7, 90.7, 77.7, 95.6, 112.8, + 60.8, 53.6, 85.6, 65.7, 46.7, 67.5, 65.4, 37.7, 70.8, 71.8, 111.2, 84.4, + 109.8, 122.5, 60, 64.2, 68, 50.8, 49.6, 96.4, 68.9, 53.1, 78.5, 80.7, + 59.3, 113, 58, 53.1, 54.9, 48.5, 88.4, 44.7, 49.6, 53, 43.4, 73.8, 73, + 57.6, 68.9, 51.1, 67.8, 56.3, 103.8, 50.6, 57.9, 53, 73.8, 60.5, 82.1, + 94.2, 83.8, 75.1, 51, 39.3, 50.8, 70.3, 41.1, 64.3, 61.7, 95.1, 86.9, + 50.1, 73.1, 63.5, 60.3, 68.7, 86.5, 48.1, 47.7 + ], + "keyDuration": [ + 69.9, 48.1, 45.8, 52.7, 48.8, 55.4, 48.7, 66.6, 70.2, 61.2, 49.5, 51.4, + 79, 61.8, 42.1, 72.3, 60.7, 79.8, 67.8, 29.6, 65.4, 49.6, 58.8, 79.5, + 47.3, 70.2, 67.6, 72.5, 80.2, 74.3, 69.2, 73, 69.4, 56.7, 77.4, 53.6, + 82.7, 47.6, 62.6, 50.5, 46.7, 54.6, 72.1, 54.1, 55.2, 54.1, 88, 107.2, + 59.6, 73.1, 67.9, 97.6, 51, 98.8, 104.4, 73.3, 83.2, 76, 73.7, 55.5, + 56.8, 68.6, 41.5, 82.6, 69.5, 69.5, 72.3, 72.1, 78.1, 59.2, 59.6, 57.6, + 78.1, 69.9, 64.7, 126.7, 68.7, 58, 88.4, 60.3, 58.5, 83.4, 67.4, 47.1, + 41.8, 60.9, 75.6, 50.5, 66.3, 70.9, 80.8, 76.2, 56.2, 73.3, 57.2, 101.2, + 63, 69.6, 45.7, 46.5, 70.8, 74.6, 50.9, 56.6, 55.9, 75.2, 59.5, 69.1, + 99.9, 101.4, 92.4, 83.6, 102.5, 110, 71.9, 72.1, 56.9, 103.6, 99.6, + 45.4, 55.8, 62.6, 58.7, 59, 64, 60.3, 57.9, 52.8, 81.5, 58.3, 53.8, + 53.2, 70.9, 86.9, 52, 68.3, 88.4, 44.5, 64.9, 71.1, 71.3, 54.4, 53.1, + 86.6, 93.8, 74.8, 74.5, 95.8, 113.4, 75.6, 78.3, 63.9, 75.7, 46.8, 60.2, + 76.4, 91.8, 60.3, 59.6, 50.4, 56.5, 57.2, 72.1, 86.5, 72.3, 88.1, 95.2, + 70.1, 39.7, 58.6, 97.4, 101.1, 65.2, 76, 72.5, 67, 92, 74.6, 91.5, 83.9, + 86.7, 74, 70.6, 80.3, 94.1, 59.3, 53.5, 98.5, 61, 46.2, 44.1, 40.2, + 49.5, 38, 73.7, 75.6, 62.5, 92.1, 56.6, 63.1, 52.2, 64.4, 67.1, 69.3, + 56.5, 62.5, 72.1, 76.5, 88.9, 68, 61.1, 39.8, 61.2, 71.7, 74.3, 68.1, + 51.4, 55.6, 81, 75.1, 50.2, 48.6, 56.3, 55.2, 42.2, 51.6, 84.4, 56.2, + 88.1, 57.4, 56.9, 73.6, 97.2, 99.2, 72.3, 59.4, 74.9, 77, 83.6, 68, + 55.8, 128.3, 82.4, 99.3, 78.7, 62, 66.6, 69.8, 68.5, 75.2, 62.7, 76.3, + 55.2, 49.8, 50.4, 62.9, 52.6, 77.3, 76.6, 85.6, 65.9, 74.1, 73.1, 55.7, + 53, 78.9, 64, 77.7, 63.2, 62, 62.9, 74.7, 59.1, 71.1, 74.3, 89.1, 58.2, + 66.7, 54.3, 36.2, 60, 38.2, 53.4, 38.5, 61.5, 50.8, 70.6, 54.9, 80, + 70.5, 53.4, 42.5, 36.6, 45.2, 71.7, 51.5, 67.9, 57.9, 67.6, 62.5, 61.9, + 46.8, 44.4, 43.5, 66.3, 41.1, 38.4, 76.7, 49.8, 54.2, 59.6, 86.4, 51.8, + 57.9, 79.1, 43.1, 48.1, 45.7, 52.2, 53.2 + ] + }, + { + "keySpacing": [ + 102.8, 157.6, 98.1, 107.1, 53.6, 74.6, 59.4, 61.5, 179.9, 37.9, 69.5, + 76, 75.3, 50.5, 63, 95.6, 62.7, 53.4, 79.2, 161.7, 123.4, 126, 67.4, + 118.9, 98.1, 71.3, 576.7, 62.5, 61.5, 70.3, 114.6, 102, 56.9, 82.5, + 84.2, 70.5, 64.3, 99.5, 68.5, 76.8, 156.9, 142.6, 62.2, 75.5, 62.1, + 125.9, 47.5, 66.1, 71.4, 45.3, 95.3, 125.2, 48.3, 105.8, 85.3, 70, + 144.7, 86, 71, 91, 85.5, 547.9, 80.8, 121.8, 63, 106.4, 119, 88.4, 60.3, + 62.5, 790.6, 78.8, 84.7, 62.9, 114.4, 87.3, 109.7, 95.3, 77.6, 75.1, 92, + 69.1, 68.8, 88.4, 95.7, 111, 105.4, 87.5, 75.8, 87.3, 88.4, 45.3, 83.6, + 52.5, 82.2, 91.6, 146.2, 106.5, 63.7, 86.5, 66.2, 46, 594.5, 50.2, 54.1, + 74, 54, 53.5, 66.9, 66.8, 64.6, 145.4, 76.9, 108.9, 90.7, 119.4, 80.8, + 80.2, 109.4, 114.2, 127.2, 116.1, 66.5, 54.9, 96, 53.9, 76.1, 168.6, + 74.2, 65.8, 117.5, 51.9, 69.7, 74.9, 81.2, 58.1, 73.5, 62.1, 73.5, 58.9, + 61.6, 44.9, 86.5, 86.6, 107, 74, 79.3, 47.1, 99.5, 78.2, 105.9, 85.5, + 94.9, 68.2, 53.6, 81, 68.7, 102.9, 51.4, 99.7, 64.4, 49.6, 66.4, 44.9, + 69.5, 83.7, 111.5, 67.1, 125.1, 112.6, 89.9, 77.8, 139.7, 118.1, 130.6, + 108.5, 95.1, 924.3, 90.6, 94.9, 117.2, 124.3, 115.2, 119.2, 107.7, 85.4, + 112.9, 66.8, 90.5, 78.4, 90.4, 56.8, 51.1, 110.3, 79.8, 65, 66.6, 78.5, + 43.9, 84.3, 113.4, 68.5, 64.1, 65.2, 75.2, 43.6, 82.1, 53.5, 39.5, 82.9, + 113.8, 167.1, 112.7, 113.8, 179, 131.3, 70.5, 92.5, 906.8, 143.3, 47.5, + 97.2, 78.7, 123.7, 100.8, 58.2, 73.8, 72.8, 92.1, 75.5, 68.3, 156.1, + 36.9, 90.9, 40.6, 59.6, 72.7, 114.9, 63.5, 59.3, 99.9, 123.1, 158.3, + 108.9, 97.1, 99.6, 95.8, 87.3, 101.9, 106.5, 92.3, 77.9, 101.4, 95.9, + 107.7, 131.9, 90.4, 130.4, 133.9, 132.7, 88.4, 78.7, 103.5, 141.5, + 110.4, 86.2, 105.4, 92.8, 124.6, 110.9, 51.5, 66.3, 126.5, 98.2, 44.2, + 47.6, 54.6, 88.9, 68.5, 79.5, 72.3, 62.6, 49.1, 66.6, 53.2, 99.5, 60.4, + 74.4, 76.7, 82.5, 104.3, 66.1, 76.3, 91.5, 104.4, 95.4, 83.1, 60.7, + 73.4, 86.5, 81.1, 78.2, 66.1, 98.6, 69.5, 98.3, 81.8, 121.2, 565, 56.5, + 54.5, 94.1, 98.5, 114.6, 104.7, 108, 93.5, 124.4, 56.5 + ], + "keyDuration": [ + 70.4, 57.9, 66.9, 94.4, 68.4, 99.6, 76.2, 63.4, 57.3, 80.2, 68.5, 71.1, + 103, 81, 67.5, 64.6, 75.1, 93.5, 79.3, 102.6, 55, 59.8, 84.5, 72.1, + 59.8, 75.5, 53.5, 60.4, 72.1, 94.6, 93.1, 61.2, 109.2, 63.5, 70.8, 60.9, + 69.6, 95.1, 91.3, 70.1, 77.5, 99.9, 83.8, 115.6, 64.6, 64.1, 60, 70.5, + 45.9, 42.7, 60.6, 86.6, 80.3, 60.7, 123.3, 75.3, 94.5, 73.6, 69.1, 99.8, + 87, 67.5, 82.7, 86, 73.1, 72.1, 85.8, 73.2, 111.4, 53.4, 61.9, 79.5, + 63.6, 98.5, 86.3, 65.7, 80.9, 51.8, 99.4, 91.7, 75.4, 62.3, 66, 63, + 80.9, 65.7, 58.1, 89.8, 71.2, 62.4, 78.7, 52.3, 80.3, 91.6, 71.7, 73.7, + 85, 84.7, 65, 70.7, 62.4, 38.1, 57.2, 54.7, 44.3, 50.6, 50.9, 71.2, 63, + 74.1, 92.1, 105.3, 82.2, 69.8, 98.1, 61.4, 58.7, 116.8, 147.1, 118.9, + 59.5, 82, 50.7, 79.3, 95.9, 74.7, 105.9, 114.3, 76.3, 72.8, 87.9, 97.7, + 78.3, 63.9, 60.3, 59.7, 44.9, 52.4, 69.2, 62.2, 74.2, 48.1, 65.1, 97.4, + 78.3, 71, 63.5, 46.9, 73.9, 60.9, 68.4, 63.7, 71.8, 67.3, 47.9, 71.2, + 69.8, 86.3, 42.9, 75.3, 43.7, 69.8, 93.4, 70.8, 59.9, 47.4, 47.2, 111, + 59.8, 82.4, 118.1, 81.2, 72.9, 101.7, 78.9, 80.7, 96.5, 105.8, 77.3, + 74.3, 85.3, 120.8, 151.3, 96.5, 119.9, 103, 109.6, 97.8, 135, 79.9, + 67.7, 66.6, 76.2, 57.6, 55.9, 62.2, 48.3, 58.7, 78.3, 72.7, 95.3, 69.8, + 75.7, 65.2, 114.4, 85.2, 109.1, 83.9, 63.1, 82.4, 77, 108.4, 61.2, 88.5, + 88.9, 70.4, 89.3, 140.6, 63.1, 67.8, 97.1, 81.2, 62, 82.8, 89.6, 87, + 109.3, 52.6, 61.7, 66.3, 83.6, 86.6, 71.4, 61.2, 107.5, 70.8, 96.2, + 74.9, 90.4, 51.1, 80.1, 81.4, 99.6, 86.7, 87.6, 99.6, 60.6, 49.5, 77.5, + 72.4, 95.1, 104.5, 92.8, 106.8, 84, 95, 139.8, 159.5, 52, 91.6, 83.7, + 72.2, 77.8, 116.7, 97.9, 80.8, 91.4, 89.1, 84.1, 79, 99.8, 66.7, 52.6, + 89.3, 42.8, 73.8, 64.1, 46.1, 66.9, 56.9, 59.3, 65.4, 65.4, 63.1, 51.9, + 44.5, 78.9, 75.1, 65.6, 79.7, 57.4, 62.9, 67, 56, 56.7, 75.2, 72.6, + 89.3, 94.3, 89.7, 88.9, 95.1, 66.4, 84.6, 68.1, 81.6, 59.8, 61.3, 65.2, + 53.1, 41.4, 93, 121.4, 77.5, 73.1, 86.3, 71.2, 102.3, 60.1, 80.9 + ] + }, + { + "keySpacing": [ + 170.1, 83.9, 95.5, 68.1, 98.4, 50.2, 159, 157, 105.8, 75, 88.4, 82.9, + 187.3, 150.2, 112.3, 141.5, 139.6, 994.1, 111.4, 123.8, 94.5, 119.5, + 101.2, 93.7, 114.8, 82.4, 121, 131.5, 137.7, 129.3, 132.2, 196.4, 144.9, + 202, 194.7, 129.9, 163.7, 101.5, 620, 124.7, 173.5, 111.5, 186.5, 72.1, + 96.6, 95.2, 72.7, 53.4, 74.2, 144.5, 71.1, 826.5, 58.2, 89.5, 93, 99, + 61.8, 133.3, 109.1, 138.9, 63.2, 77.3, 133.4, 70, 119.5, 98.4, 76.8, + 146.4, 75.5, 152.1, 86.4, 133.2, 92.7, 96.4, 130.6, 138.6, 121.7, 91.8, + 64.6, 82.6, 61.5, 112, 171.7, 132.5, 82.9, 95.9, 78.4, 142.2, 84.9, + 67.8, 83.8, 122.2, 139.7, 128.9, 97.3, 106, 84.4, 155.3, 135.1, 81.5, + 86.1, 870.6, 97.5, 83.4, 103.6, 127.1, 123.8, 98.9, 77.4, 103.3, 150.6, + 87.7, 116.6, 784.8, 104.5, 79.7, 130.6, 141.2, 95.2, 207.8, 98.7, 111.6, + 182.9, 112.2, 91.4, 96, 655.7, 157.5, 103.9, 100.1, 84.1, 105.7, 50.4, + 137.8, 141.8, 174.4, 128.8, 144.5, 127.3, 81, 71.3, 103.3, 101, 92.5, + 90.2, 118.3, 728.2, 154.8, 167.7, 181.3, 104.2, 88.7, 59, 75.9, 77.7, + 132.2, 103.2, 84.8, 50.3, 101.4, 99.8, 108.8, 45.4, 84.4, 89.7, 56, + 113.8, 105.6, 44.2, 113.2, 135.5, 94.7, 106.8, 75.9, 156.5, 80.7, 96.9, + 103.8, 97.4, 96, 109.2, 71.4, 154.1, 89.3, 76.1, 109.6, 92.8, 210.8, + 150, 55, 102.2, 136.2, 115.3, 75.2, 82.3, 90.6, 46.8, 60.3, 64.2, 120.5, + 117.2, 122.9, 57.2, 72.9, 78.7, 117.4, 113.1, 67.8, 113.4, 159, 134.7, + 96.5, 1009.1, 159.9, 102.5, 125.3, 72.6, 79.2, 102.9, 125.6, 169.1, + 74.3, 847.2, 184.2, 89.7, 154.4, 121.2, 113.3, 107.5, 508.8, 82.8, + 122.6, 90.9, 80.7, 122.7, 88.6, 72.8, 937, 148.7, 105.5, 98, 116.7, + 80.7, 104.9, 802.3, 118.7, 106.3, 74.2, 67.3, 132.4, 97.5, 93.8, 111, + 118, 92.3, 142.7, 104.3, 88.5, 112.8, 85, 85, 78.8, 76.5, 102.5, 139.9, + 146.8, 105.2, 118.8, 112.8, 73.7, 114.3, 95.1, 101.2, 128, 122.6, 88.7, + 84.6, 74.2, 108.4, 92.7, 148.6, 73.4, 89.5, 111.5, 99.2, 132.4, 116.5, + 105.8, 120.7, 70.6, 90.6, 78.7, 160.6, 121, 100, 63.9, 97.5, 104.2, + 99.8, 86.4, 49.9, 92.9, 68.1, 62.1, 91.7, 85.8, 79.4, 59.6, 107.6, + 119.4, 98.9, 162.3, 177.5, 71.5, 103.8, 121.9, 86.1, 105.4, 130.2 + ], + "keyDuration": [ + 145, 90.6, 63.5, 63.1, 96.8, 106.4, 119.3, 99.3, 107.5, 103.8, 76.7, 75, + 94.1, 91.5, 118.1, 100.1, 106.1, 89.1, 103.8, 82.4, 95.2, 68.9, 95.5, + 88.1, 115.6, 81.7, 114.5, 90.7, 139.7, 134.4, 94, 132.9, 86, 130.8, + 131.9, 107.1, 96.5, 187.5, 102, 94.2, 78.4, 131.4, 84, 95.2, 124.3, + 92.1, 79.3, 85.9, 61.9, 50.8, 59.4, 114.5, 58.3, 66.4, 55.2, 66.9, 72.9, + 79.6, 118.7, 123.8, 102.4, 103.2, 111.8, 104.2, 115.6, 71.8, 92.5, 64, + 80.2, 95.4, 85.1, 115.2, 97.5, 123, 124.7, 112.3, 63.6, 94.5, 77.5, + 80.7, 77.4, 147, 94.1, 98.6, 80.6, 69.4, 103.9, 108.3, 90.1, 98.4, 87.9, + 94.4, 111.5, 95, 86.6, 89.3, 84.1, 99.4, 140.7, 82.1, 95.9, 94.8, 123.5, + 109.6, 102.9, 119.3, 165.5, 112.2, 75.3, 101.1, 68.1, 66, 114.1, 73.7, + 79.5, 83.2, 82.8, 115.9, 94.4, 109.9, 113.3, 106.5, 119.5, 92.5, 79.6, + 84, 62.8, 86.7, 81.7, 79.7, 93.8, 104.9, 76.5, 97.2, 88.6, 106.4, 73.2, + 150.5, 96.3, 91.2, 83.8, 86.4, 89.9, 73.2, 68.6, 73.6, 114.3, 86.5, + 75.5, 94.5, 92.8, 77, 105.1, 77.2, 88.2, 74.7, 94.9, 120.9, 109, 73.1, + 72.9, 85.7, 48.5, 67.7, 72.9, 61.5, 84.1, 83.6, 71.6, 85.9, 81.3, 99, + 79.8, 72.3, 87, 105.1, 103.8, 75.6, 118.1, 81.4, 113.9, 93.7, 86.5, + 110.6, 72.7, 121, 74.5, 126.4, 104.1, 111.4, 100.2, 101, 75, 82.8, + 101.3, 109.8, 68, 47.6, 62.1, 69.2, 99.7, 58, 83.3, 89.9, 105.6, 68.7, + 137.1, 83.6, 122.9, 136.3, 97.5, 106.2, 123.9, 87.1, 86.9, 91.4, 73.6, + 107.9, 78.1, 118.9, 120.8, 100.9, 113.3, 80.5, 139.1, 128.9, 83.9, + 143.2, 85.2, 65.4, 89.2, 127.8, 100.8, 92.7, 95.7, 78.8, 69.8, 111.9, + 116.1, 68.4, 123, 78.2, 105.8, 82.9, 74.3, 64.4, 102.8, 91.8, 84.8, 90, + 75.3, 120.3, 164.3, 123.1, 76.6, 135.8, 72.9, 88.2, 79.8, 109.1, 75.9, + 85.4, 115.4, 131.7, 121.4, 111.8, 101.4, 106.2, 99.8, 94.9, 95.9, 121.5, + 64.9, 72.6, 117.8, 113.2, 72.3, 70.7, 93.1, 100.7, 99, 102.8, 123, 77.2, + 67, 110.4, 70.2, 75.1, 109.8, 84.5, 68.9, 72.7, 64.8, 109.2, 116.7, + 70.8, 82.7, 75.8, 72.2, 43.3, 81.8, 80.9, 94, 56, 57.2, 65, 89.9, 79.7, + 82, 61.7, 106.4, 110.4, 108.3, 73.4, 94.4, 76.6, 136.6, 109.7, 85.2, + 79.9 + ] + } + ] +} diff --git a/packages/typing-fixtures/reference/anticheat/ms-jitter.json b/packages/typing-fixtures/reference/anticheat/ms-jitter.json new file mode 100644 index 000000000..0a8ea46c8 --- /dev/null +++ b/packages/typing-fixtures/reference/anticheat/ms-jitter.json @@ -0,0 +1,78 @@ +{ + "name": "ms-jitter", + "label": "bot", + "client": "web", + "description": "80/45 ms with ±1 ms uniform jitter, escaping the exact check.", + "samples": [ + { + "keySpacing": [ + 80.85, 79.67, 79.44, 79.16, 79.47, 80.02, 79.15, 80.95, 80.24, 80.38, + 80.54, 79.74, 80.46, 80.75, 80.06, 80.49, 79.84, 79.98, 80.47, 80.86, + 80.96, 79.94, 79.7, 80.58, 79.54, 80.5, 80.06, 79.69, 79.91, 79.02, + 79.97, 80.08, 80.53, 79.19, 80.61, 79.81, 79.66, 80.09, 79.12, 80.25, + 79.56, 80.11, 79.29, 80.91, 80.71, 80.84, 79.7, 79.37, 80.32, 80, 79.14, + 80.53, 80.91, 79.01, 79.96, 80.81, 80.38, 80.11, 80.97, 79.58, 80.53, + 79.72, 79.93, 80.4, 79.01, 79.48, 80.01, 80.58, 80.71, 79.14, 79.92, + 80.28, 80.96, 80.23, 79.56, 80.42, 80.54, 79.62, 80.91, 80.55, 79.36, + 80.97, 79.77, 80.96, 80.87, 79.92, 79.24, 79.16, 79.02, 79.23, 80.34, + 79.23, 80.8, 80.87, 80.36, 79.7, 79.77, 80.34, 80.82, 79.5, 80.26, + 80.46, 80.21, 80.73, 80.87, 80.42, 79.22, 80.33, 80.69, 79.3, 79.56, + 80.47, 79.75, 81, 80.83, 79.06, 80.53, 79.81, 80.8, 80.02, 79.1, 80.38, + 79.09, 80.8, 80.22, 80.13, 79.88, 80.56, 79.77, 79.57, 80.89, 81, 79.67, + 79.17, 80.51, 79.15, 80.65, 79.63, 79.91, 80.24, 79.28, 80.73, 79.86, + 80.78, 80.55, 80.59, 80.8, 79.69, 79.94, 80.1, 80.18, 79.86, 79.71, + 79.95, 80.16, 80.23, 80.5, 79.28, 80.5, 80.86, 80.18, 79.39, 79.73, + 80.38, 79.78, 79.67, 80.08, 80.77, 79.95, 80.29, 79.8, 80.65, 79.38, + 79.73, 80.76, 79.12, 79.17, 79.27, 79.32, 80.05, 80.29, 79.79, 80.43, + 79.32, 80.5, 80.26, 79.04, 79.23, 80.18, 80.88, 80.17, 79.08, 79.32, + 80.86, 80.35, 80.78, 80.75, 80.78, 80.25, 80.98, 80.48, 80.56, 80.95, + 79.1, 80.78, 80.98, 80.03, 80.54, 80.23, 80.76, 79.34, 80.43, 80.56, + 80.36, 80.62, 79.39, 79.37, 80.14, 79.45, 79.69, 80.69, 80.69, 80.9, + 79.83, 79.81, 79.28, 80.87, 80.3, 80.02, 80.74, 80.62, 80.39, 80.08, + 80.62, 80.49, 79.05, 80.62, 80.42, 79.63, 79.9, 79.67, 79.59, 79.55, + 79.99, 79.79, 80.57, 80.87, 79.49, 80.45, 79.94, 80.97, 79.23, 79.13, + 80.88, 80.95, 79.21, 80.52, 79.44, 80.51, 79.54, 79.7, 79.5, 79.69, + 80.42, 80.7, 79.23, 79.95, 80.85, 80.9, 80.3, 80.97, 80.19, 80.03, 80.3, + 80.75, 80.38, 80.95, 80.37, 79.97, 79.02, 80.92, 79.29, 80.63, 79.8, + 79.64, 79.63, 79.84, 79.12, 79.1, 80.6, 79.73, 80.87, 79.36, 80.07, + 79.89, 80.14, 80.39, 81, 80.47, 79.15, 80.99, 80.03, 80.71, 79.65, + 80.83, 79.98, 80.17, 80.93, 80.5, 80.09, 79.7, 79.79, 80.31, 79.52, + 80.25, 80.99, 79.05, 80.95, 79.27 + ], + "keyDuration": [ + 44.92, 45.79, 45.81, 44.48, 44.01, 44.88, 45.7, 44.04, 45.18, 45.72, + 45.32, 44.12, 45.07, 44.23, 45.97, 44.59, 44.46, 44.56, 44.55, 44.26, + 44.78, 44.01, 44.7, 45.11, 44.99, 44.08, 45.27, 45.68, 45.58, 45.66, + 45.65, 44.13, 45.78, 45.47, 45.22, 45.54, 44.34, 45.02, 45.86, 45.58, + 44.28, 45.37, 44.71, 45.05, 45.51, 44.72, 45.3, 44.19, 44.03, 44.77, + 45.42, 44.21, 45.17, 45.95, 45.6, 44.16, 45.46, 45.34, 44.36, 45.8, + 45.78, 44.77, 44.15, 45.89, 44.91, 45.95, 44.49, 45.31, 44.71, 45.35, + 45.48, 45.42, 45.9, 45.21, 45.62, 44.38, 44.83, 45.66, 45.63, 44.68, + 45.6, 45.78, 44.41, 45.93, 44.97, 45.12, 44.52, 44.19, 44.21, 45.97, + 45.54, 44.79, 45.14, 44.96, 44.31, 45.48, 44.27, 45.31, 44.52, 45.56, + 44.47, 45.71, 45.1, 45.5, 44.15, 45.51, 44.33, 45.64, 45.46, 45.34, + 44.46, 44.93, 45.69, 44.15, 45.47, 44.09, 45.17, 45.31, 45.73, 45.49, + 45.9, 44.08, 44.37, 44.4, 44.01, 44.54, 45.26, 44.43, 45.46, 45.89, + 45.62, 44.66, 45.23, 44.93, 44.55, 45.19, 44.17, 44.21, 44.78, 44.53, + 45.5, 44.45, 44.23, 44.03, 45.57, 44.16, 45.52, 44.25, 44.94, 45.74, + 44.47, 44.92, 44.69, 45.06, 45.89, 45.69, 44.45, 44.6, 44.61, 45.51, + 44.42, 45.23, 44.54, 44.79, 44.61, 45.79, 44.2, 45.4, 44.58, 45.12, + 45.27, 44.37, 44.88, 44.34, 45.15, 44.71, 44.06, 45.83, 44.25, 44.17, + 44.8, 45.71, 44.62, 44.44, 45.97, 45.33, 44.89, 45.88, 45.66, 45.5, + 45.49, 45.79, 44.35, 45.26, 45.5, 45.18, 44.65, 44.67, 45.91, 44.29, + 45.49, 45.33, 44.26, 45.45, 44.32, 45.33, 45.36, 44.11, 45.61, 44.97, + 45.26, 44.36, 44.08, 45.86, 45.99, 44.9, 44.78, 45.81, 45.16, 45, 44.56, + 45.05, 44.16, 45.41, 44.19, 45.99, 44.65, 45.25, 45.22, 44.15, 44.14, + 44.33, 44.15, 44.16, 45.91, 45, 44.79, 44.22, 44.74, 44.91, 45.72, + 45.89, 45.76, 44.3, 45.94, 44.42, 44.52, 44.28, 44.86, 45.83, 45.79, + 44.1, 45.11, 45.61, 44.27, 44.08, 45.39, 44.79, 45.49, 45.38, 44.44, + 45.47, 44.97, 44.89, 44.7, 44.31, 44.22, 45.51, 45.09, 45.74, 44.18, + 45.56, 45.09, 45.53, 45.73, 44.6, 45.93, 44.46, 45.45, 45.7, 45.27, + 44.13, 44.35, 44.84, 44.58, 45.58, 44.94, 44.14, 44.78, 45.1, 45.32, + 45.17, 44.48, 44.24, 45.39, 45.44, 45.45, 45.92, 45.08, 45.65, 45.91, + 44.63, 44.93, 44.56, 45.03, 45.45, 45.06, 45.53, 45.42, 44.48, 44.83, + 44.36, 44.89, 45.84, 45.74, 45.66, 44.24, 45, 45.57, 45.52 + ] + } + ] +} diff --git a/packages/typing-fixtures/reference/anticheat/replayed-recording.json b/packages/typing-fixtures/reference/anticheat/replayed-recording.json new file mode 100644 index 000000000..b65b6b7aa --- /dev/null +++ b/packages/typing-fixtures/reference/anticheat/replayed-recording.json @@ -0,0 +1,205 @@ +{ + "name": "replayed-recording", + "label": "bot", + "client": "web", + "description": "One modelled timeline uploaded by the same user, resubmitted exactly and then with ±0.3 ms noise.", + "samples": [ + { + "keySpacing": [ + 94.8, 85, 100.3, 57.8, 75.1, 100.1, 89.4, 72.6, 72, 45.7, 63.7, 52.6, + 117.5, 59.6, 84.6, 92.7, 161.6, 95.4, 110, 121.5, 69.9, 185.8, 71.5, + 58.3, 97.6, 55, 101.9, 90.8, 127.7, 85.4, 109.6, 73.9, 146.4, 67.9, + 107.7, 76, 73.6, 96.9, 124.9, 134, 76.2, 36.9, 77.1, 105.7, 70.5, 72.2, + 73.5, 76.8, 67.6, 60.8, 76.3, 53.1, 134.4, 79.3, 111.5, 130.1, 114.9, + 111.7, 81, 143.9, 133.2, 106.8, 103.8, 90.2, 74.3, 125.6, 832.5, 48.6, + 77, 77.1, 72.9, 89.8, 82.1, 87.8, 140, 128.4, 40.5, 50.5, 51, 67.5, + 133.4, 71.6, 92.1, 81.4, 113, 86.9, 84.4, 85.1, 72.1, 84.3, 106.7, + 124.1, 84.4, 105.4, 51.7, 57.4, 84.7, 61.5, 52.9, 86.8, 51.8, 66.3, 102, + 56.9, 80.8, 94.4, 67.1, 63.5, 121.9, 83.3, 60.2, 51.7, 103.2, 75.6, + 96.8, 79.4, 93.9, 91.9, 119.3, 178.3, 74.8, 72.1, 89.8, 80.3, 38.4, + 144.7, 98.2, 96.9, 61, 120.3, 133.8, 99.1, 77.5, 118.1, 84.5, 86.6, + 77.1, 68.1, 76.3, 107.9, 54.3, 106.5, 96.6, 174.8, 89.3, 105.7, 111, + 82.9, 120.5, 124.9, 82.4, 119.3, 178.1, 81.4, 115.5, 105.8, 90.7, 69.9, + 67.3, 85.8, 78, 120.7, 97.8, 51.3, 89.6, 87.7, 100, 106.6, 77.8, 93, + 80.6, 94.1, 109.1, 70, 91.5, 95.1, 88.5, 62.4, 63.7, 43, 65, 58.2, + 119.6, 617.5, 48.6, 56.8, 75, 112.3, 65.5, 95.2, 57.5, 92.5, 64.9, 68.6, + 66.7, 58.1, 76.2, 65.3, 89.4, 75.8, 88.7, 76.5, 71.2, 63.1, 82.9, 47.9, + 77.6, 88.4, 102.3, 90.4, 54.8, 624.4, 62, 68.2, 73.5, 86.8, 171, 93.9, + 125.5, 104.8, 76.7, 66, 84.5, 107.2, 155.9, 105, 125.4, 134.6, 92.7, + 92.2, 84.5, 109.4, 90.7, 92.2, 54.4, 93.3, 105.1, 78.4, 61.4, 56.7, + 62.6, 743.8, 56.3, 53.7, 76.2, 102.6, 87.7, 82.9, 75.6, 95.9, 215.3, + 81.3, 82.8, 85.7, 78.3, 71.3, 101.3, 97, 89.8, 78.9, 76.6, 120.4, 117.6, + 56.6, 96.9, 99.2, 65.1, 66.6, 90.4, 69.7, 70, 63.8, 75.6, 78.5, 81.3, + 931.8, 75.3, 169.4, 105.8, 150.4, 91, 103.8, 173.5, 104.2, 102.5, 88.8, + 85.2, 65.7, 67.6, 88.2, 109.5, 91.5, 69.4, 126.3, 102.2, 78.4, 63, + 107.1, 101.4, 140.4, 104.5, 106.3, 62.8, 82.4, 87.1, 107.2, 94.3, 98, + 82.1, 105.7, 121.4, 92.1, 81.5, 87.4, 153.9, 91.2, 126.1, 112.5, 68 + ], + "keyDuration": [ + 89.7, 107.6, 79.8, 111.5, 83.8, 73.6, 82.2, 70.5, 70.2, 47.1, 88.4, + 74.1, 45.7, 61.7, 133.1, 90.7, 75.3, 114.1, 76.1, 89.7, 108.4, 96.4, + 70.6, 89.8, 105.4, 80.5, 71.8, 69.3, 90.5, 60.6, 104.3, 90.4, 86.4, + 97.7, 69.5, 79.1, 85.6, 69.5, 110.5, 128.7, 54.1, 112.7, 122.3, 51.3, + 59.7, 67.8, 62.7, 75.2, 65.4, 64.3, 76, 70.9, 72.8, 106.4, 97.4, 100.3, + 69.2, 86.1, 90.8, 89.7, 72.4, 101.3, 65.2, 93.5, 55.5, 80.7, 91.5, 61.2, + 75.6, 53.1, 67.5, 70.6, 79.2, 88.2, 56.5, 83.5, 77.3, 68, 60.6, 70.5, + 46.1, 64.8, 85.8, 80.4, 54.7, 64.7, 73, 76.8, 81.7, 77, 84.1, 87.6, + 64.6, 67.6, 46.8, 73, 63.1, 64.7, 49.9, 49.7, 73.7, 85.6, 63.7, 84.9, + 70.5, 53.6, 57.9, 40.9, 55.9, 59, 73.5, 74.6, 71.3, 58.5, 67.3, 78.7, + 85.6, 113.7, 98.5, 92.1, 75, 110, 119.7, 67, 63.4, 99.3, 87.6, 80.9, + 77.4, 85, 94.3, 57.2, 76.3, 65, 65.2, 67, 78.1, 69.8, 77.2, 112.3, + 104.2, 64.7, 94, 81.7, 113.2, 101.1, 124.5, 76.5, 85.1, 100.2, 68.5, 83, + 104.2, 61.2, 91.6, 114, 103.8, 51.9, 77.2, 76.6, 54.7, 76.9, 85.4, 66.3, + 56.6, 62, 74.5, 62.1, 73.2, 73.3, 63.9, 96.2, 105.6, 84.3, 116.3, 86.3, + 79.9, 94.3, 73.3, 80.1, 69, 56, 56.3, 82.3, 58.9, 59.4, 65.3, 54.2, + 70.9, 72.5, 70.4, 97.5, 65.1, 71, 92.6, 65.8, 71, 69.1, 44.3, 69.2, + 63.7, 77, 118.4, 61.1, 58.8, 70.7, 91.9, 51.1, 51.9, 111.3, 54.7, 83.4, + 55, 59.2, 57.2, 67.5, 72.4, 52.5, 105.6, 86.8, 89.3, 91.5, 101, 74.1, + 91.3, 102.4, 86.6, 93.6, 102.6, 103.4, 94.2, 61.3, 66, 71.7, 54.9, 60.1, + 76.9, 65.9, 54.2, 90.6, 80, 40.3, 87.8, 59.2, 62, 76, 60.1, 83.4, 68.2, + 66.7, 91.1, 111.8, 69.9, 80.7, 102.6, 67.6, 62.4, 81.9, 83.3, 54.7, + 68.7, 89.5, 90.5, 77, 79.1, 83.7, 49.5, 56.1, 57.3, 62.5, 40.1, 62.6, + 72.7, 85.4, 89.7, 68.9, 92.4, 93.8, 112.1, 54.5, 111.8, 75.7, 98.3, + 67.5, 42.8, 57.4, 65.7, 53.6, 82.1, 70.2, 125.3, 84.6, 91.9, 64.2, 77.5, + 55.4, 89.3, 85.6, 92, 59.4, 56.3, 84, 87.7, 90.6, 95.1, 78.3, 107.2, + 50.3, 64.4, 97.9, 91, 70.6, 112.9, 86.7, 101.2, 127.4, 71, 73.1, 68.1, + 89.4 + ], + "label": "model" + }, + { + "keySpacing": [ + 94.8, 85, 100.3, 57.8, 75.1, 100.1, 89.4, 72.6, 72, 45.7, 63.7, 52.6, + 117.5, 59.6, 84.6, 92.7, 161.6, 95.4, 110, 121.5, 69.9, 185.8, 71.5, + 58.3, 97.6, 55, 101.9, 90.8, 127.7, 85.4, 109.6, 73.9, 146.4, 67.9, + 107.7, 76, 73.6, 96.9, 124.9, 134, 76.2, 36.9, 77.1, 105.7, 70.5, 72.2, + 73.5, 76.8, 67.6, 60.8, 76.3, 53.1, 134.4, 79.3, 111.5, 130.1, 114.9, + 111.7, 81, 143.9, 133.2, 106.8, 103.8, 90.2, 74.3, 125.6, 832.5, 48.6, + 77, 77.1, 72.9, 89.8, 82.1, 87.8, 140, 128.4, 40.5, 50.5, 51, 67.5, + 133.4, 71.6, 92.1, 81.4, 113, 86.9, 84.4, 85.1, 72.1, 84.3, 106.7, + 124.1, 84.4, 105.4, 51.7, 57.4, 84.7, 61.5, 52.9, 86.8, 51.8, 66.3, 102, + 56.9, 80.8, 94.4, 67.1, 63.5, 121.9, 83.3, 60.2, 51.7, 103.2, 75.6, + 96.8, 79.4, 93.9, 91.9, 119.3, 178.3, 74.8, 72.1, 89.8, 80.3, 38.4, + 144.7, 98.2, 96.9, 61, 120.3, 133.8, 99.1, 77.5, 118.1, 84.5, 86.6, + 77.1, 68.1, 76.3, 107.9, 54.3, 106.5, 96.6, 174.8, 89.3, 105.7, 111, + 82.9, 120.5, 124.9, 82.4, 119.3, 178.1, 81.4, 115.5, 105.8, 90.7, 69.9, + 67.3, 85.8, 78, 120.7, 97.8, 51.3, 89.6, 87.7, 100, 106.6, 77.8, 93, + 80.6, 94.1, 109.1, 70, 91.5, 95.1, 88.5, 62.4, 63.7, 43, 65, 58.2, + 119.6, 617.5, 48.6, 56.8, 75, 112.3, 65.5, 95.2, 57.5, 92.5, 64.9, 68.6, + 66.7, 58.1, 76.2, 65.3, 89.4, 75.8, 88.7, 76.5, 71.2, 63.1, 82.9, 47.9, + 77.6, 88.4, 102.3, 90.4, 54.8, 624.4, 62, 68.2, 73.5, 86.8, 171, 93.9, + 125.5, 104.8, 76.7, 66, 84.5, 107.2, 155.9, 105, 125.4, 134.6, 92.7, + 92.2, 84.5, 109.4, 90.7, 92.2, 54.4, 93.3, 105.1, 78.4, 61.4, 56.7, + 62.6, 743.8, 56.3, 53.7, 76.2, 102.6, 87.7, 82.9, 75.6, 95.9, 215.3, + 81.3, 82.8, 85.7, 78.3, 71.3, 101.3, 97, 89.8, 78.9, 76.6, 120.4, 117.6, + 56.6, 96.9, 99.2, 65.1, 66.6, 90.4, 69.7, 70, 63.8, 75.6, 78.5, 81.3, + 931.8, 75.3, 169.4, 105.8, 150.4, 91, 103.8, 173.5, 104.2, 102.5, 88.8, + 85.2, 65.7, 67.6, 88.2, 109.5, 91.5, 69.4, 126.3, 102.2, 78.4, 63, + 107.1, 101.4, 140.4, 104.5, 106.3, 62.8, 82.4, 87.1, 107.2, 94.3, 98, + 82.1, 105.7, 121.4, 92.1, 81.5, 87.4, 153.9, 91.2, 126.1, 112.5, 68 + ], + "keyDuration": [ + 89.7, 107.6, 79.8, 111.5, 83.8, 73.6, 82.2, 70.5, 70.2, 47.1, 88.4, + 74.1, 45.7, 61.7, 133.1, 90.7, 75.3, 114.1, 76.1, 89.7, 108.4, 96.4, + 70.6, 89.8, 105.4, 80.5, 71.8, 69.3, 90.5, 60.6, 104.3, 90.4, 86.4, + 97.7, 69.5, 79.1, 85.6, 69.5, 110.5, 128.7, 54.1, 112.7, 122.3, 51.3, + 59.7, 67.8, 62.7, 75.2, 65.4, 64.3, 76, 70.9, 72.8, 106.4, 97.4, 100.3, + 69.2, 86.1, 90.8, 89.7, 72.4, 101.3, 65.2, 93.5, 55.5, 80.7, 91.5, 61.2, + 75.6, 53.1, 67.5, 70.6, 79.2, 88.2, 56.5, 83.5, 77.3, 68, 60.6, 70.5, + 46.1, 64.8, 85.8, 80.4, 54.7, 64.7, 73, 76.8, 81.7, 77, 84.1, 87.6, + 64.6, 67.6, 46.8, 73, 63.1, 64.7, 49.9, 49.7, 73.7, 85.6, 63.7, 84.9, + 70.5, 53.6, 57.9, 40.9, 55.9, 59, 73.5, 74.6, 71.3, 58.5, 67.3, 78.7, + 85.6, 113.7, 98.5, 92.1, 75, 110, 119.7, 67, 63.4, 99.3, 87.6, 80.9, + 77.4, 85, 94.3, 57.2, 76.3, 65, 65.2, 67, 78.1, 69.8, 77.2, 112.3, + 104.2, 64.7, 94, 81.7, 113.2, 101.1, 124.5, 76.5, 85.1, 100.2, 68.5, 83, + 104.2, 61.2, 91.6, 114, 103.8, 51.9, 77.2, 76.6, 54.7, 76.9, 85.4, 66.3, + 56.6, 62, 74.5, 62.1, 73.2, 73.3, 63.9, 96.2, 105.6, 84.3, 116.3, 86.3, + 79.9, 94.3, 73.3, 80.1, 69, 56, 56.3, 82.3, 58.9, 59.4, 65.3, 54.2, + 70.9, 72.5, 70.4, 97.5, 65.1, 71, 92.6, 65.8, 71, 69.1, 44.3, 69.2, + 63.7, 77, 118.4, 61.1, 58.8, 70.7, 91.9, 51.1, 51.9, 111.3, 54.7, 83.4, + 55, 59.2, 57.2, 67.5, 72.4, 52.5, 105.6, 86.8, 89.3, 91.5, 101, 74.1, + 91.3, 102.4, 86.6, 93.6, 102.6, 103.4, 94.2, 61.3, 66, 71.7, 54.9, 60.1, + 76.9, 65.9, 54.2, 90.6, 80, 40.3, 87.8, 59.2, 62, 76, 60.1, 83.4, 68.2, + 66.7, 91.1, 111.8, 69.9, 80.7, 102.6, 67.6, 62.4, 81.9, 83.3, 54.7, + 68.7, 89.5, 90.5, 77, 79.1, 83.7, 49.5, 56.1, 57.3, 62.5, 40.1, 62.6, + 72.7, 85.4, 89.7, 68.9, 92.4, 93.8, 112.1, 54.5, 111.8, 75.7, 98.3, + 67.5, 42.8, 57.4, 65.7, 53.6, 82.1, 70.2, 125.3, 84.6, 91.9, 64.2, 77.5, + 55.4, 89.3, 85.6, 92, 59.4, 56.3, 84, 87.7, 90.6, 95.1, 78.3, 107.2, + 50.3, 64.4, 97.9, 91, 70.6, 112.9, 86.7, 101.2, 127.4, 71, 73.1, 68.1, + 89.4 + ] + }, + { + "keySpacing": [ + 94.8, 85.25, 100.41, 57.98, 75.07, 100.07, 89.22, 72.35, 71.72, 46, + 63.95, 52.38, 117.44, 59.53, 84.57, 92.96, 161.47, 95.58, 110.26, + 121.29, 69.83, 185.92, 71.51, 58.29, 97.48, 54.91, 101.83, 90.81, + 127.43, 85.41, 109.32, 73.64, 146.22, 67.69, 107.45, 75.79, 73.76, + 96.89, 124.85, 134.08, 76.38, 36.69, 77.07, 105.95, 70.34, 72.37, 73.61, + 76.58, 67.66, 61.03, 76.36, 52.81, 134.55, 79.43, 111.57, 130.28, + 114.93, 111.63, 80.89, 144.05, 132.97, 106.73, 103.63, 89.93, 74.53, + 125.56, 832.47, 48.39, 77.23, 77.05, 73.12, 90.06, 81.84, 87.6, 139.88, + 128.69, 40.77, 50.53, 50.7, 67.32, 133.29, 71.74, 92.33, 81.61, 113.21, + 86.8, 84.22, 84.98, 72.39, 84.54, 106.81, 124.26, 84.4, 105.35, 51.54, + 57.26, 84.77, 61.41, 53.07, 86.52, 51.83, 66.53, 102.18, 56.9, 80.75, + 94.59, 67.34, 63.49, 121.73, 83.16, 59.93, 51.94, 103.09, 75.71, 96.9, + 79.43, 93.77, 91.81, 119.18, 178.35, 74.78, 72.21, 89.67, 80.44, 38.67, + 144.76, 97.98, 96.62, 60.81, 120.48, 133.79, 99.21, 77.61, 118.28, + 84.22, 86.61, 76.97, 68.1, 76.04, 107.75, 54.12, 106.74, 96.75, 175.01, + 89.24, 105.53, 110.92, 83.08, 120.76, 124.72, 82.25, 119.3, 177.98, + 81.54, 115.73, 105.6, 90.62, 69.7, 67.51, 86.06, 78.15, 120.9, 97.74, + 51.06, 89.8, 87.92, 100.03, 106.49, 77.61, 93.01, 80.42, 94.1, 108.99, + 70.02, 91.54, 94.83, 88.23, 62.42, 63.68, 42.99, 64.93, 58.18, 119.8, + 617.32, 48.63, 57.02, 74.94, 112.56, 65.55, 94.93, 57.31, 92.41, 64.96, + 68.68, 66.88, 57.91, 76.32, 65.57, 89.61, 75.69, 88.91, 76.42, 70.99, + 63.16, 83.09, 48.08, 77.58, 88.26, 102.42, 90.39, 54.87, 624.35, 61.76, + 68.34, 73.59, 86.96, 171.23, 94.18, 125.66, 105.06, 76.81, 65.98, 84.44, + 107.34, 155.78, 104.77, 125.23, 134.56, 92.46, 92.3, 84.36, 109.16, + 90.78, 92.18, 54.48, 93.6, 104.85, 78.4, 61.18, 56.79, 62.6, 743.85, + 56.4, 53.48, 76.14, 102.65, 87.94, 82.81, 75.51, 96.11, 215.12, 81.55, + 82.85, 85.48, 78.56, 71.15, 101.12, 96.9, 89.64, 79.13, 76.48, 120.17, + 117.33, 56.88, 97.03, 99.16, 65.09, 66.77, 90.61, 69.55, 70.06, 63.95, + 75.74, 78.66, 81.16, 931.7, 75.06, 169.52, 106.03, 150.69, 91.14, 103.8, + 173.55, 104.08, 102.64, 89.03, 85.24, 65.69, 67.85, 88.12, 109.67, + 91.49, 69.4, 126.43, 102.1, 78.37, 63.12, 107.08, 101.11, 140.15, + 104.72, 106.35, 62.62, 82.64, 87.08, 107.29, 94.01, 97.8, 82.33, 105.97, + 121.13, 92.26, 81.61, 87.63, 153.8, 90.91, 125.89, 112.2, 67.82 + ], + "keyDuration": [ + 89.6, 107.77, 79.77, 111.61, 83.5, 73.63, 82.21, 70.2, 70.07, 47.26, + 88.12, 74.04, 45.93, 61.62, 132.87, 90.45, 75.21, 114.07, 75.84, 89.56, + 108.46, 96.44, 70.8, 89.72, 105.66, 80.5, 71.75, 69.49, 90.51, 60.65, + 104.39, 90.68, 86.67, 97.45, 69.66, 78.99, 85.49, 69.22, 110.29, 128.54, + 54.29, 112.94, 122.1, 51.6, 59.71, 67.51, 62.63, 75.34, 65.63, 64.16, + 75.72, 70.84, 72.86, 106.41, 97.18, 100.15, 69.05, 85.89, 90.96, 89.72, + 72.11, 101.54, 65.16, 93.44, 55.33, 80.96, 91.75, 61.36, 75.76, 53.24, + 67.47, 70.88, 79.18, 87.94, 56.75, 83.51, 77.52, 68.07, 60.69, 70.7, + 45.96, 64.86, 85.87, 80.6, 54.42, 64.71, 73.06, 77.05, 81.99, 76.75, + 83.96, 87.48, 64.6, 67.71, 46.85, 72.94, 63.18, 64.95, 49.81, 49.7, + 73.57, 85.71, 63.6, 84.96, 70.45, 53.52, 58.04, 40.99, 55.86, 58.88, + 73.44, 74.78, 71.46, 58.42, 67.14, 78.64, 85.73, 113.91, 98.22, 91.98, + 75.13, 109.95, 119.98, 67.2, 63.45, 99.46, 87.73, 81.02, 77.52, 85.22, + 94.08, 57.01, 76.47, 64.74, 65.49, 67.13, 78.33, 70.06, 77.18, 112.54, + 104.23, 64.85, 94.1, 81.66, 113.15, 101.06, 124.46, 76.55, 84.96, + 100.05, 68.24, 83.01, 104.15, 61.14, 91.57, 113.83, 103.69, 51.94, + 77.23, 76.54, 54.98, 76.77, 85.48, 66.39, 56.33, 61.99, 74.34, 62.1, + 72.99, 73.53, 64.1, 96.28, 105.57, 84.36, 116.24, 86.11, 79.62, 94.39, + 73.24, 80.13, 69.13, 55.8, 56, 82.47, 58.84, 59.58, 65.28, 54.35, 70.69, + 72.39, 70.2, 97.64, 65.32, 70.97, 92.63, 65.65, 70.81, 69.4, 44.04, 69, + 63.94, 77.18, 118.33, 61.22, 58.72, 70.64, 91.83, 50.88, 51.98, 111.2, + 54.57, 83.45, 54.85, 59.47, 57.48, 67.33, 72.19, 52.3, 105.41, 86.54, + 89.53, 91.38, 101.18, 73.85, 91.06, 102.29, 86.89, 93.43, 102.75, + 103.57, 94.14, 61.32, 66.25, 71.59, 54.94, 60.2, 76.75, 66.18, 54.48, + 90.57, 79.79, 40.38, 87.6, 59.22, 62.24, 75.93, 60.03, 83.21, 68.39, + 66.97, 91.24, 111.96, 70.04, 80.68, 102.66, 67.8, 62.57, 81.78, 83.11, + 54.67, 68.77, 89.8, 90.29, 77.29, 79.37, 83.96, 49.55, 56.38, 57.42, + 62.33, 39.9, 62.73, 72.92, 85.62, 89.64, 68.97, 92.68, 94.07, 111.96, + 54.33, 111.99, 75.58, 98.05, 67.21, 42.96, 57.17, 65.72, 53.35, 82.37, + 69.96, 125.43, 84.61, 91.62, 64.45, 77.61, 55.26, 89.04, 85.42, 92.04, + 59.14, 56.48, 84.22, 88, 90.67, 95.26, 78.37, 107.29, 50.24, 64.34, + 97.75, 90.81, 70.55, 113.16, 86.82, 101.38, 127.54, 71.23, 73.1, 68.17, + 89.17 + ] + } + ] +} diff --git a/packages/typing-fixtures/reference/anticheat/scripted-playwright.json b/packages/typing-fixtures/reference/anticheat/scripted-playwright.json new file mode 100644 index 000000000..267978ff5 --- /dev/null +++ b/packages/typing-fixtures/reference/anticheat/scripted-playwright.json @@ -0,0 +1,55 @@ +{ + "name": "scripted-playwright", + "label": "bot", + "client": "web", + "description": "The fast scripted presses record-fixtures.ts sends: uniform 60-120 ms gaps and 40-150 ms holds.", + "samples": [ + { + "keySpacing": [ + 100, 93, 60, 118, 116, 61, 87, 98, 84, 119, 76, 111, 102, 118, 73, 72, + 73, 84, 95, 94, 103, 76, 109, 85, 99, 80, 75, 76, 65, 65, 90, 89, 63, + 100, 77, 67, 95, 104, 70, 85, 69, 110, 66, 78, 90, 106, 76, 80, 70, 71, + 69, 104, 97, 114, 109, 116, 102, 83, 73, 66, 65, 107, 90, 118, 92, 105, + 76, 92, 111, 75, 64, 103, 117, 115, 90, 79, 84, 81, 95, 88, 117, 62, 84, + 86, 114, 61, 106, 98, 77, 81, 86, 84, 107, 114, 94, 83, 97, 67, 95, 109, + 97, 65, 115, 107, 81, 101, 79, 101, 75, 92, 107, 116, 73, 116, 96, 65, + 107, 116, 80, 119, 101, 77, 97, 78, 93, 63, 107, 113, 67, 98, 100, 100, + 102, 84, 94, 113, 84, 81, 80, 120, 68, 68, 119, 105, 79, 62, 110, 84, + 119, 64, 89, 67, 108, 119, 77, 105, 112, 100, 82, 113, 110, 100, 63, + 116, 89, 81, 87, 80, 87, 108, 102, 89, 116, 89, 71, 109, 106, 99, 120, + 110, 117, 71, 75, 101, 65, 100, 119, 73, 75, 79, 72, 86, 83, 64, 109, + 77, 116, 102, 62, 67, 63, 61, 114, 103, 95, 119, 91, 108, 77, 61, 76, + 106, 117, 98, 79, 111, 105, 91, 65, 81, 68, 106, 65, 93, 64, 62, 104, + 68, 78, 120, 120, 69, 89, 86, 87, 108, 79, 64, 63, 110, 110, 88, 108, + 111, 104, 71, 70, 120, 87, 70, 76, 74, 101, 104, 68, 116, 98, 85, 106, + 117, 97, 81, 72, 114, 94, 107, 114, 71, 101, 73, 105, 76, 102, 88, 115, + 112, 73, 77, 105, 85, 85, 102, 82, 79, 82, 66, 94, 103, 97, 103, 119, + 77, 101, 95, 96, 93, 63, 113, 103, 74, 103, 95, 66, 104, 103, 65, 92, + 118, 96, 111, 60, 111, 91, 67, 82, 93, 105, 115, 100 + ], + "keyDuration": [ + 40, 62, 48, 141, 63, 82, 46, 126, 93, 62, 144, 144, 148, 144, 144, 101, + 72, 131, 104, 137, 136, 103, 134, 62, 80, 42, 72, 73, 81, 80, 142, 53, + 92, 89, 72, 142, 58, 116, 139, 105, 120, 61, 49, 104, 121, 137, 61, 98, + 99, 53, 85, 122, 41, 46, 86, 131, 83, 42, 138, 116, 82, 61, 105, 70, + 142, 106, 72, 83, 119, 52, 135, 54, 98, 49, 77, 94, 66, 71, 96, 142, + 149, 125, 97, 110, 54, 43, 87, 59, 147, 88, 71, 146, 98, 70, 41, 44, + 139, 141, 87, 79, 148, 97, 83, 124, 60, 137, 65, 62, 101, 139, 81, 61, + 145, 115, 117, 96, 49, 72, 90, 146, 109, 42, 69, 83, 130, 60, 52, 65, + 60, 107, 109, 134, 135, 83, 51, 86, 69, 90, 144, 143, 115, 60, 51, 106, + 54, 149, 90, 75, 106, 133, 101, 40, 108, 68, 146, 146, 136, 111, 44, 42, + 96, 137, 55, 71, 112, 90, 127, 129, 67, 93, 80, 129, 83, 80, 45, 148, + 133, 130, 55, 45, 146, 69, 66, 131, 99, 142, 49, 79, 88, 49, 54, 58, 83, + 76, 65, 40, 104, 92, 126, 144, 136, 150, 75, 136, 111, 97, 136, 105, + 138, 57, 52, 118, 140, 119, 134, 81, 72, 56, 122, 139, 104, 46, 72, 55, + 53, 76, 127, 139, 88, 68, 46, 94, 126, 146, 132, 96, 93, 75, 127, 84, + 84, 98, 42, 55, 73, 146, 115, 105, 100, 67, 124, 148, 127, 86, 53, 120, + 55, 115, 86, 65, 128, 138, 85, 122, 125, 65, 113, 127, 61, 113, 127, 82, + 100, 109, 65, 60, 137, 138, 49, 137, 67, 60, 127, 54, 94, 106, 90, 145, + 101, 107, 92, 144, 47, 42, 53, 100, 143, 96, 59, 67, 145, 79, 108, 79, + 139, 105, 57, 130, 60, 103, 137, 90, 105, 102, 145, 90, 110, 138, 59, + 107 + ] + } + ] +} diff --git a/packages/typing-fixtures/reference/anticheat/sub-ms-jitter.json b/packages/typing-fixtures/reference/anticheat/sub-ms-jitter.json new file mode 100644 index 000000000..379510463 --- /dev/null +++ b/packages/typing-fixtures/reference/anticheat/sub-ms-jitter.json @@ -0,0 +1,82 @@ +{ + "name": "sub-ms-jitter", + "label": "bot", + "client": "web", + "description": "80/45 ms with jitter inside the 0.01 ms fixed-timing tolerance.", + "samples": [ + { + "keySpacing": [ + 80.002, 79.996, 80, 79.997, 80.002, 80, 80, 79.997, 79.998, 80, 79.998, + 79.997, 80.002, 80, 80.002, 80.003, 79.999, 80.002, 79.998, 80.001, 80, + 80.003, 80, 79.996, 80.003, 79.998, 80.003, 80.002, 79.999, 80.002, + 79.997, 79.999, 80.002, 79.996, 79.996, 80, 80.003, 80.001, 80.001, + 80.002, 80.001, 80.001, 79.997, 79.999, 80, 80.003, 79.999, 80, 80, + 80.001, 80.002, 80.003, 80.001, 79.999, 80.002, 80.002, 80, 80.004, + 80.003, 80.002, 80.001, 80.004, 80.002, 79.996, 79.997, 80, 80.003, + 79.999, 80, 79.998, 80, 80, 80.001, 79.999, 80.002, 79.997, 80.004, + 80.001, 80.002, 80.002, 79.996, 79.999, 79.997, 80.002, 80.002, 80.001, + 80.004, 80.004, 80.004, 80.001, 80.003, 80.004, 80.003, 79.999, 80.001, + 80.004, 79.999, 80.001, 79.997, 80.001, 80, 79.998, 80.003, 79.996, + 80.003, 79.997, 79.999, 80.003, 79.996, 80.001, 79.997, 79.998, 80.004, + 80, 79.997, 79.996, 79.997, 80.001, 80.001, 80.003, 80.002, 80.002, + 79.997, 79.998, 79.997, 80.001, 80.001, 80.001, 80.002, 79.999, 80.004, + 79.997, 80.004, 79.998, 79.999, 80, 79.999, 80, 79.998, 80.001, 79.996, + 80, 80.002, 79.999, 80, 79.999, 80.004, 79.997, 79.997, 79.997, 79.997, + 80.003, 80.002, 80.003, 79.999, 79.997, 79.999, 79.998, 79.997, 79.997, + 79.999, 79.998, 79.996, 80, 80, 79.999, 80.004, 80.003, 80.003, 79.998, + 79.999, 79.998, 79.998, 80.002, 79.996, 79.998, 79.999, 80.004, 79.998, + 80.001, 79.997, 80.001, 80.003, 79.996, 80.004, 79.999, 79.997, 79.996, + 80, 80.001, 80.003, 80, 80.001, 80.003, 80.001, 80.003, 80.003, 80.001, + 79.998, 80.001, 80.004, 80, 79.998, 79.997, 80, 79.998, 80.001, 80.003, + 79.997, 80.002, 80.001, 79.999, 79.998, 80.003, 80.002, 80, 80.003, + 79.999, 80.003, 80, 79.999, 80.002, 79.999, 80.001, 79.998, 80.002, 80, + 80.001, 80.003, 80.003, 80.003, 79.999, 79.998, 79.996, 79.998, 80.001, + 80.001, 80.004, 79.998, 80.002, 79.998, 80.003, 79.998, 80, 80.001, + 80.003, 80.002, 79.997, 80.003, 79.998, 80.003, 80.003, 80.002, 80.002, + 79.999, 79.997, 79.998, 80.001, 80, 80, 80.001, 79.997, 80.003, 80.002, + 80.001, 80.003, 80.003, 80.004, 80.004, 80.003, 79.996, 79.999, 80, + 79.997, 80.001, 80.001, 80.003, 80.002, 79.998, 80, 80, 79.997, 79.997, + 79.999, 80.002, 79.999, 79.999, 80.001, 80, 79.997, 79.997, 79.999, + 79.996, 79.999, 79.997, 79.999, 79.999, 80.002, 79.999, 80.002, 80, + 80.004, 80.004, 80.001, 80.002, 80.002, 80.002, 80.002, 80, 80.004, + 79.998, 79.999, 79.999, 79.998, 80.002, 80.002, 79.999, 80, 79.999 + ], + "keyDuration": [ + 44.999, 44.996, 44.998, 45, 45.002, 45.002, 45.003, 45, 44.997, 45.002, + 44.998, 45, 45, 45.001, 45, 45.001, 44.998, 45.003, 44.999, 44.999, + 45.002, 44.997, 44.998, 45.003, 44.998, 45.003, 44.999, 45.001, 44.996, + 44.997, 44.997, 45, 45.001, 44.996, 44.998, 45.001, 45.002, 44.999, + 44.998, 44.998, 44.997, 44.996, 44.999, 45.004, 44.998, 44.996, 44.997, + 44.998, 45.001, 45, 44.997, 45, 44.997, 44.996, 44.997, 44.996, 44.999, + 45.002, 44.997, 45.001, 44.997, 45.002, 44.997, 44.997, 45.003, 44.999, + 44.999, 45.003, 45.003, 45.002, 44.999, 45.002, 45.002, 44.998, 44.998, + 45.004, 45.001, 45.003, 45.002, 44.999, 45.001, 44.999, 44.998, 44.998, + 45.003, 45.001, 44.996, 44.997, 45, 45.002, 45.002, 44.999, 45.002, + 44.998, 45.002, 44.999, 44.996, 45.002, 44.997, 45, 45.003, 45, 45, 45, + 45.004, 44.999, 44.997, 44.999, 44.997, 44.996, 45.001, 44.997, 45.001, + 44.997, 45.001, 44.999, 45.001, 45, 44.999, 45, 45.002, 45.003, 44.996, + 44.997, 45.002, 45, 44.999, 45, 45, 45, 45.003, 44.997, 45.003, 45, + 44.997, 45.003, 45.004, 45.002, 45, 45, 44.998, 44.999, 44.997, 44.997, + 45.001, 45.001, 45.003, 45.003, 44.997, 45.002, 45, 44.999, 44.997, 45, + 45.003, 45, 45, 44.997, 45.003, 45.002, 45.003, 44.999, 44.999, 45.001, + 45.003, 44.997, 45.001, 45.001, 44.997, 44.998, 45.002, 45, 44.998, + 45.004, 44.998, 45.003, 44.997, 45.003, 44.997, 45, 44.998, 44.996, + 44.997, 44.999, 44.997, 44.998, 45.002, 44.999, 44.999, 44.999, 45.003, + 44.997, 45.004, 44.999, 45.002, 45, 45.002, 45.003, 45.001, 45.003, + 44.998, 45.004, 45.002, 45.001, 45.001, 45.002, 44.996, 45.002, 45.001, + 45.002, 44.996, 44.999, 44.999, 45.002, 45.001, 44.997, 45.001, 45, + 44.999, 44.999, 44.996, 45.004, 44.998, 44.999, 45.002, 44.997, 44.999, + 44.997, 45, 44.998, 45, 45.002, 44.997, 45.002, 45.004, 44.999, 45.003, + 45.004, 44.997, 44.999, 45.002, 45.002, 45.004, 44.999, 45.004, 44.998, + 44.997, 45, 45.003, 45.002, 45.001, 45.001, 44.999, 45, 45.002, 45.001, + 44.997, 45.001, 45.001, 44.999, 44.997, 44.998, 44.999, 44.998, 45.003, + 44.996, 44.997, 45.004, 45, 45.003, 45.003, 45.001, 44.998, 45.001, + 44.998, 45.001, 45, 45.002, 45.003, 45, 44.997, 45.001, 45.003, 45.002, + 44.996, 45.003, 44.997, 44.998, 44.996, 44.999, 45.002, 45, 45, 45.002, + 45.002, 44.999, 45.001, 44.998, 45.001, 45, 45, 45.002, 44.997, 45.002, + 45, 44.999, 44.996, 45.002, 44.997, 45.003, 44.999, 44.997, 44.999, + 44.997, 45.003, 44.998, 45.004, 44.999, 44.998, 45.001 + ] + } + ] +} diff --git a/packages/typing-fixtures/reference/anticheat/uniform-range.json b/packages/typing-fixtures/reference/anticheat/uniform-range.json new file mode 100644 index 000000000..9e2ceaf83 --- /dev/null +++ b/packages/typing-fixtures/reference/anticheat/uniform-range.json @@ -0,0 +1,78 @@ +{ + "name": "uniform-range", + "label": "bot", + "client": "web", + "description": "Independent uniform gaps 60-100 ms and holds 20-40 ms.", + "samples": [ + { + "keySpacing": [ + 87.59, 90.91, 68.79, 84.93, 63.41, 83.69, 88.8, 78.32, 96.22, 69.18, + 91.19, 89.62, 86.42, 60.55, 78.62, 72.02, 95.86, 71.52, 90.21, 88.94, + 70.14, 60.76, 91.74, 68.33, 70.9, 64.93, 78.8, 99.67, 69.82, 63.24, + 93.94, 72.68, 76.3, 99.43, 70.88, 75.44, 68.69, 94.77, 88.89, 95.15, + 80.46, 92.2, 85.97, 65.59, 85.44, 83.1, 72.61, 91.16, 84.38, 60.33, + 97.28, 73.66, 74, 94.98, 69.97, 67.01, 93.35, 71.16, 99.93, 60.55, + 74.04, 90.34, 72.57, 86.43, 63.07, 63.2, 89.4, 80.99, 70.14, 97.2, + 61.98, 89.39, 98.15, 71.41, 81.33, 94.26, 93.87, 83.16, 98.26, 88.1, + 81.61, 64.4, 71.76, 81.18, 66.2, 70.08, 90.67, 65.54, 63.2, 73.07, + 92.22, 85.73, 76.5, 63.9, 71.62, 98.28, 73.06, 88.11, 89.77, 64.09, + 91.34, 97.27, 84.23, 64.46, 74.47, 76.12, 62.07, 76.85, 96.71, 72.45, + 90.67, 62.08, 92.66, 64.03, 80.48, 93.07, 68.39, 83.86, 64.04, 74.55, + 86.54, 70.17, 87.25, 69.59, 70.13, 99.39, 85.73, 78.12, 89.69, 61.55, + 84.74, 86.23, 60.77, 65.79, 73.35, 73.4, 85.7, 97.95, 83.74, 94.74, + 90.94, 71.34, 62.55, 66.8, 61.66, 69.08, 95.64, 78.08, 61.69, 91.09, + 83.45, 71.44, 95.77, 90.98, 98.81, 67.32, 70.9, 92.2, 70.97, 71.75, + 90.63, 60.16, 86.33, 81.21, 70.82, 69.73, 86.61, 92.38, 82.57, 84.21, + 86.85, 77.05, 82.76, 67.97, 64.26, 74.87, 79.84, 62.52, 84.36, 78.16, + 78.61, 75.36, 80.02, 74.29, 70.09, 77.4, 81.05, 86.45, 74.35, 69.81, + 91.6, 62.7, 74.66, 87.59, 65.19, 65.46, 99.69, 63.23, 80.68, 75.67, + 65.33, 68.35, 99.33, 75.71, 61.23, 84.53, 87.5, 90.73, 80.73, 69.06, + 67.53, 94.63, 98.36, 80.32, 64.25, 87.19, 79.78, 72.39, 64.31, 79.47, + 99.43, 80.75, 67.27, 69.27, 98.2, 68.13, 91.54, 89.03, 83.76, 60.21, + 77.29, 96.85, 60.05, 90.62, 79.85, 82.26, 77.7, 72.74, 86.27, 94.41, + 70.78, 74.48, 60.15, 71.23, 92.59, 79.85, 94.12, 99.11, 80.18, 85.89, + 63.23, 64.36, 80.11, 80.37, 81.3, 85.32, 76.7, 60.18, 82.43, 99.38, + 64.25, 66.08, 74.41, 94.84, 97.36, 95.16, 78.54, 60.85, 90.46, 73.31, + 94.92, 87.79, 62.36, 83.63, 94.37, 98.58, 77.88, 62.67, 87.73, 82.96, + 91.58, 68.29, 83.88, 74.85, 84.54, 82.11, 75.22, 66.21, 75.65, 75.57, + 67.94, 72.97, 75.41, 90.96, 78.55, 68.89, 69.5, 87.68, 71.63, 92.14, + 78.25, 64.77, 83.23, 95.73, 65.7, 88.96, 82.33, 89.29, 80.37, 75.64, + 94.22, 79.84, 68.11, 63.81, 60.2, 71.58, 92.73, 76.85, 86.2 + ], + "keyDuration": [ + 20.64, 36.45, 22.7, 31.83, 30.92, 28.04, 24.77, 25.95, 34.28, 27.62, + 32.56, 26.69, 26.55, 24.88, 27.99, 37.73, 35.15, 28.83, 32.52, 37.4, + 25.14, 27.54, 31.55, 20.34, 31.42, 20.07, 20.97, 37, 38.65, 27.63, + 29.98, 37.42, 24.71, 24.29, 32.49, 25.38, 34.25, 27.15, 21.18, 39.35, + 33.07, 35.76, 35.62, 30.39, 30.22, 27.32, 26.78, 35.46, 38.25, 39.2, + 23.19, 35.39, 21.22, 22.17, 36.63, 39.36, 33.54, 30.26, 29.65, 27.74, + 30.81, 39.31, 21.27, 22.99, 32.12, 37.69, 23.82, 33.52, 37.7, 20.91, + 35.96, 35.44, 36.83, 28.83, 25.47, 22.95, 29.51, 25.86, 29.9, 34.96, + 24.18, 36.56, 23.59, 36.91, 36.58, 28.8, 24.88, 23.69, 20.37, 36.39, + 38.14, 21.25, 38.9, 27.01, 28, 26.97, 29.45, 28.23, 20.67, 20.28, 20.9, + 25.8, 28.47, 24.22, 28.37, 30.65, 38.99, 27.58, 37.84, 36.92, 37.46, + 29.97, 30.85, 31.97, 39.69, 24.23, 39.68, 23.23, 25.65, 24.1, 24.1, + 32.36, 20.77, 31.48, 30.43, 34.57, 25, 31.86, 28.37, 27.57, 34.51, + 34.21, 30.35, 35.37, 34.26, 20.19, 31.11, 25.46, 30.85, 38.28, 39.18, + 24.94, 33.61, 35.48, 29.19, 28.47, 34.24, 27.98, 30.65, 33.31, 29.11, + 27.67, 28.15, 31.89, 27.31, 35.36, 31.32, 21.37, 37.06, 22.86, 31.19, + 35.86, 39.63, 26.08, 20.91, 29.95, 29.73, 31.83, 25.12, 26.33, 35.75, + 36.35, 29.32, 32.93, 36.28, 35.15, 29.16, 34.95, 32.23, 39.83, 34, + 38.82, 39.27, 28.1, 38.03, 33.04, 21.13, 39.43, 27.71, 28.92, 38.88, + 31.44, 22.45, 21.88, 21.29, 37.96, 34.59, 23.76, 21.46, 28.25, 30.01, + 32.67, 30.87, 21.45, 27.07, 29.95, 23.76, 34.45, 33.57, 29.51, 28.85, + 20.49, 27.32, 36.09, 27.55, 22.49, 27.33, 22.91, 34.39, 21.86, 28.62, + 24.02, 29.98, 29.55, 35.6, 26.92, 26.47, 34.07, 28.36, 35.97, 30.4, + 21.42, 37.06, 23.01, 31.24, 29.91, 22.98, 36.08, 26.11, 24.5, 28.71, + 21.6, 38.47, 21.23, 38.74, 27.62, 35.12, 38.98, 29.2, 29.41, 22.66, + 29.08, 32.39, 30.73, 23.88, 20.5, 33.48, 23.09, 39.65, 27.37, 20.53, + 31.68, 26.79, 39.88, 25.11, 20.76, 37.81, 20.62, 30.28, 34.01, 37.88, + 37.66, 23.44, 23.2, 35.33, 27.54, 33.79, 38.84, 33.4, 27.97, 28.14, + 30.23, 30.13, 25.01, 22.84, 26.51, 26.62, 21.51, 32.1, 33.69, 32.75, + 27.17, 35.47, 26.46, 35.13, 23.52, 26.48, 35.51, 39.39, 36.94, 34.34, + 22.69, 24.81, 34.26, 33.41, 20.04, 38.11, 30.14, 29.66, 35.93, 31.41, + 22.67, 20.44, 22.99, 31.01, 20.39, 35.9, 20.01, 31.84, 20.56 + ] + } + ] +} diff --git a/packages/typing-fixtures/reference/anticheat/value-pool.json b/packages/typing-fixtures/reference/anticheat/value-pool.json new file mode 100644 index 000000000..12092e09d --- /dev/null +++ b/packages/typing-fixtures/reference/anticheat/value-pool.json @@ -0,0 +1,51 @@ +{ + "name": "value-pool", + "label": "bot", + "client": "web", + "description": "Delays drawn from small hardcoded pools.", + "samples": [ + { + "keySpacing": [ + 73, 73, 104, 95, 88, 88, 88, 81, 88, 95, 81, 73, 95, 88, 73, 81, 81, 88, + 81, 104, 81, 104, 73, 73, 73, 81, 88, 95, 73, 104, 88, 95, 95, 104, 95, + 104, 104, 73, 73, 81, 88, 73, 73, 95, 81, 88, 88, 104, 95, 95, 81, 81, + 73, 73, 104, 81, 73, 95, 81, 73, 95, 104, 88, 104, 95, 104, 73, 88, 95, + 104, 104, 104, 88, 104, 104, 88, 104, 73, 88, 81, 95, 88, 95, 104, 88, + 104, 88, 95, 95, 88, 81, 81, 73, 73, 104, 104, 95, 73, 95, 88, 95, 104, + 81, 104, 95, 104, 95, 104, 73, 104, 88, 88, 73, 81, 81, 104, 104, 95, + 95, 104, 95, 95, 104, 88, 81, 88, 95, 104, 73, 88, 88, 88, 73, 81, 88, + 88, 73, 73, 73, 73, 73, 104, 104, 73, 73, 88, 88, 95, 81, 81, 95, 95, + 104, 88, 95, 88, 88, 81, 88, 95, 73, 81, 104, 104, 95, 88, 81, 104, 81, + 81, 88, 104, 88, 104, 88, 88, 81, 88, 81, 104, 88, 104, 104, 88, 88, 95, + 104, 88, 95, 73, 95, 81, 95, 104, 81, 81, 73, 95, 95, 104, 81, 81, 73, + 95, 104, 95, 104, 104, 81, 104, 81, 88, 81, 95, 95, 95, 95, 95, 88, 81, + 104, 88, 73, 95, 88, 88, 88, 104, 104, 73, 73, 81, 88, 81, 104, 104, 88, + 104, 81, 88, 104, 95, 95, 81, 104, 104, 104, 73, 73, 95, 73, 73, 104, + 81, 88, 81, 95, 95, 73, 88, 104, 73, 73, 81, 95, 95, 81, 88, 73, 88, 88, + 88, 73, 95, 81, 95, 104, 73, 73, 95, 104, 81, 104, 95, 81, 73, 73, 104, + 95, 81, 88, 73, 88, 88, 95, 73, 95, 73, 81, 81, 104, 73, 73, 104, 104, + 88, 88, 95, 73, 88, 73, 88, 95, 104, 73, 88, 81, 73, 104 + ], + "keyDuration": [ + 41, 41, 41, 47, 52, 52, 52, 52, 52, 52, 47, 52, 52, 41, 47, 47, 41, 41, + 47, 47, 52, 47, 52, 41, 52, 47, 47, 47, 52, 41, 47, 47, 47, 47, 41, 47, + 52, 52, 47, 52, 41, 52, 41, 52, 47, 41, 47, 41, 52, 52, 47, 52, 41, 47, + 41, 41, 52, 52, 41, 41, 47, 47, 41, 47, 52, 41, 47, 52, 41, 41, 47, 41, + 47, 47, 52, 52, 52, 47, 52, 47, 52, 47, 47, 47, 52, 52, 52, 52, 52, 52, + 47, 41, 41, 52, 41, 52, 41, 52, 47, 52, 41, 41, 41, 52, 47, 41, 41, 41, + 52, 41, 41, 41, 52, 41, 52, 41, 52, 47, 52, 47, 47, 52, 41, 47, 47, 52, + 47, 52, 47, 47, 41, 41, 47, 41, 52, 41, 41, 41, 47, 52, 52, 47, 52, 52, + 41, 41, 41, 47, 47, 41, 47, 47, 41, 41, 47, 47, 41, 41, 47, 52, 52, 47, + 52, 52, 52, 47, 52, 52, 52, 52, 52, 52, 41, 41, 52, 47, 52, 41, 47, 47, + 41, 47, 47, 52, 41, 41, 47, 47, 41, 41, 47, 41, 52, 52, 52, 52, 52, 47, + 52, 52, 47, 52, 52, 47, 52, 52, 47, 47, 41, 47, 52, 52, 47, 52, 52, 47, + 52, 47, 52, 41, 52, 52, 41, 47, 52, 52, 47, 47, 52, 52, 41, 47, 41, 47, + 47, 47, 47, 52, 47, 52, 41, 41, 52, 41, 52, 41, 47, 47, 52, 41, 41, 47, + 47, 41, 52, 41, 47, 47, 41, 52, 52, 52, 41, 41, 47, 41, 41, 47, 41, 41, + 47, 52, 52, 47, 47, 47, 47, 41, 41, 47, 47, 52, 41, 41, 41, 47, 52, 52, + 41, 52, 52, 47, 52, 52, 47, 41, 41, 52, 41, 47, 52, 52, 47, 47, 52, 52, + 47, 41, 41, 52, 52, 47, 47, 47, 52, 41, 41, 41, 52, 52 + ] + } + ] +} diff --git a/packages/typing-fixtures/reference/keystrokes/__snapshots__/custom-repeat.json b/packages/typing-fixtures/reference/keystrokes/__snapshots__/custom-repeat.json new file mode 100644 index 000000000..7e39dab15 --- /dev/null +++ b/packages/typing-fixtures/reference/keystrokes/__snapshots__/custom-repeat.json @@ -0,0 +1,108 @@ +{ + "completedEvent": { + "wpm": 58.46, + "rawWpm": 69.52, + "charStats": [37, 1, 1, 0], + "charTotal": 44, + "acc": 95.45, + "language": "english", + "testDuration": 7.5954, + "lastKeyToEnd": 3.9, + "startToFirstKey": 0, + "afkDuration": 0, + "customText": { + "textLen": 9, + "mode": "repeat", + "pipeDelimiter": false, + "limit": { + "mode": "word", + "value": 9 + } + }, + "tags": [], + "punctuation": false, + "numbers": false, + "lazyMode": false, + "timestamp": 1791473740686, + "mode": "custom", + "mode2": "custom", + "bailedOut": false, + "funbox": [], + "difficulty": "normal", + "blindMode": false, + "stopOnLetter": false, + "restartCount": 0, + "incompleteTests": [], + "incompleteTestSeconds": 0, + "consistency": 90.69, + "wpmConsistency": 83.55, + "keyConsistency": 67.44, + "chartData": { + "wpm": [72, 72, 44, 48, 53, 56, 57, 58], + "burst": [72, 72, 72, 60, 72, 72, 60, 81], + "err": [0, 0, 2, 0, 0, 0, 0, 0] + }, + "keySpacing": [ + 105.4, 98.5, 178.79999999999998, 300.3, 183, 168.5999999999999, + 147.60000000000014, 197.79999999999995, 180.79999999999995, 247.5, 187.5, + 120.70000000000005, 145.69999999999982, 121.10000000000036, + 190.19999999999982, 139.0999999999999, 280.9000000000001, + 114.59999999999991, 178.20000000000027, 150.5999999999999, + 306.9000000000001, 167.29999999999973, 127.40000000000009, + 161.30000000000018, 204.69999999999982, 131.10000000000036, + 265.09999999999945, 190.69999999999982, 208.5, 131, 125.80000000000018, + 222.40000000000055, 176.69999999999982, 120.30000000000018, + 94.69999999999982, 336.09999999999945, 152.90000000000055, + 193.19999999999982, 205.30000000000018, 152.19999999999982, + 189.60000000000036, 105.09999999999945, 186.30000000000018 + ], + "keyDuration": [ + 119.80000000000001, 107.6, 107.9, 88.90000000000003, 64.79999999999995, + 121.70000000000005, 39.700000000000045, 72.29999999999995, + 72.70000000000005, 119, 42.799999999999955, 60.00000000000023, + 54.69999999999982, 148.5, 46.59999999999991, 75.30000000000018, + 147.80000000000018, 80.90000000000009, 137.80000000000018, + 137.0999999999999, 129.5, 55.09999999999991, 100.90000000000009, 126, + 42.399999999999636, 76.69999999999982, 135.09999999999945, + 57.69999999999982, 69.20000000000073, 42.80000000000018, + 122.30000000000018, 114.80000000000018, 47.899999999999636, 99, 142, + 122.30000000000018, 69.80000000000018, 134.89999999999964, + 102.89999999999964, 116.30000000000018, 54.19999999999982, + 96.59999999999945, 70.5, 53.19999999999982 + ], + "keyOverlap": 91.3, + "client": "web" + }, + "hash": "2ba5defbc48537b32b5ae97146795e49b659bbec", + "derived": { + "inputHistory": [ + "The ", + "quick ", + "browns ", + "fox ", + "jumps ", + "over ", + "the ", + "lazy ", + "dog" + ], + "missedWords": { + "brown": 2 + }, + "wordBurstHistory": [124, 82, 93, 108, 91, 92, 123, 85, 165], + "correctedWordsHistory": [ + "The ", + "quick ", + "browns ", + "fox ", + "jumps ", + "over ", + "the ", + "lazy ", + "dog" + ], + "rawHistory": [72, 72, 72, 69, 70, 70, 69, 70], + "keypressesPerSecond": [6, 6, 6, 5, 6, 6, 5, 4], + "incompleteTestSeconds": 7.6 + } +} diff --git a/packages/typing-fixtures/reference/keystrokes/__snapshots__/quote-short.json b/packages/typing-fixtures/reference/keystrokes/__snapshots__/quote-short.json new file mode 100644 index 000000000..b2f6805fb --- /dev/null +++ b/packages/typing-fixtures/reference/keystrokes/__snapshots__/quote-short.json @@ -0,0 +1,122 @@ +{ + "completedEvent": { + "wpm": 71.75, + "rawWpm": 71.75, + "charStats": [69, 0, 0, 0], + "charTotal": 69, + "acc": 98.57, + "language": "english", + "testDuration": 11.54, + "lastKeyToEnd": 2.4, + "startToFirstKey": 0, + "afkDuration": 0, + "quoteLength": 0, + "tags": [], + "punctuation": false, + "numbers": false, + "lazyMode": false, + "timestamp": 1791473755938, + "mode": "quote", + "mode2": "36", + "bailedOut": false, + "funbox": [], + "difficulty": "normal", + "blindMode": false, + "stopOnLetter": false, + "restartCount": 0, + "incompleteTests": [], + "incompleteTestSeconds": 0, + "consistency": 85.2, + "wpmConsistency": 94.8, + "keyConsistency": 66.97, + "chartData": { + "wpm": [60, 66, 68, 69, 72, 72, 72, 74, 71, 72, 71, 72], + "burst": [72, 72, 72, 72, 84, 72, 72, 84, 48, 84, 60, 89], + "err": [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + }, + "keySpacing": [ + 317.6, 91.59999999999997, 204.59999999999997, 203.9000000000001, + 91.29999999999995, 145.9000000000001, 165.39999999999986, + 137.10000000000014, 282, 119.89999999999986, 193.29999999999995, 335.5, + 205.0999999999999, 115.40000000000009, 115.40000000000009, + 116.19999999999982, 137.70000000000027, 158.5999999999999, + 160.0999999999999, 176.4000000000001, 122.09999999999991, + 189.20000000000027, 209.39999999999964, 170.60000000000036, 130, 204.5, + 121.89999999999964, 100.90000000000055, 99.39999999999964, + 148.39999999999964, 151.40000000000055, 196.89999999999964, + 169.40000000000055, 125, 104.39999999999964, 160.69999999999982, 175.5, + 108.90000000000055, 141.19999999999982, 240.39999999999964, + 192.30000000000018, 100.89999999999964, 238.70000000000073, 150, + 183.79999999999927, 121.90000000000055, 129.80000000000018, + 219.09999999999945, 112.69999999999982, 201.20000000000073, + 177.60000000000036, 164.59999999999854, 198.70000000000073, 284.5, + 202.79999999999927, 173.70000000000073, 136.29999999999927, + 114.30000000000109, 101.39999999999964, 99, 184, 263.7999999999993, + 207.40000000000146, 152.29999999999927, 297.60000000000036, + 119.79999999999927, 156, 189.3000000000011, 121.39999999999964 + ], + "keyDuration": [ + 54.5, 48.5, 141.50000000000006, 63.200000000000045, 81, 46.60000000000002, + 125.79999999999995, 92.90000000000009, 64.09999999999991, + 143.29999999999995, 144.9000000000001, 150.5, 144.20000000000027, + 143.70000000000027, 101.40000000000009, 71.90000000000009, + 132.4000000000001, 105.09999999999991, 136.9000000000001, + 136.30000000000018, 102.90000000000009, 135.20000000000027, 62, 83, 44, + 72.89999999999964, 72.89999999999964, 79.5, 78.89999999999964, + 142.89999999999964, 53.5, 91, 89.19999999999982, 72.29999999999927, 141.5, + 58.30000000000018, 114, 139.19999999999982, 105.89999999999964, + 122.39999999999964, 61.5, 45.19999999999982, 103.80000000000018, + 120.19999999999982, 136.79999999999927, 60, 98.30000000000018, + 99.39999999999964, 52.69999999999982, 84.80000000000018, + 121.39999999999964, 39.599999999998545, 46.30000000000109, + 84.20000000000073, 130, 82, 40.5, 136.40000000000146, 112.29999999999927, + 80.79999999999927, 61, 104.29999999999927, 70.10000000000036, + 141.59999999999854, 107.10000000000036, 72.79999999999927, 84, 117.5, + 53.19999999999891, 78.35000000000036 + ], + "keyOverlap": 152.1, + "client": "web" + }, + "hash": "80993c14b9fcec77e8f1acdfb86424af68182c93", + "derived": { + "inputHistory": [ + "Courage ", + "is ", + "resistance ", + "to ", + "fear, ", + "mastery ", + "of ", + "fear ", + "- ", + "not ", + "absence ", + "of ", + "fear." + ], + "missedWords": { + "Courage": 1 + }, + "wordBurstHistory": [ + 71, 115, 88, 120, 116, 98, 123, 103, 214, 89, 95, 100, 123 + ], + "correctedWordsHistory": [ + "xourage ", + "is ", + "resistance ", + "to ", + "fear, ", + "mastery ", + "of ", + "fear ", + "- ", + "not ", + "absence ", + "of ", + "fear." + ], + "rawHistory": [60, 66, 68, 69, 72, 72, 72, 74, 71, 72, 71, 72], + "keypressesPerSecond": [6, 6, 6, 6, 7, 6, 6, 7, 4, 7, 5, 4], + "incompleteTestSeconds": 11.54 + } +} diff --git a/packages/typing-fixtures/reference/keystrokes/__snapshots__/time-15.json b/packages/typing-fixtures/reference/keystrokes/__snapshots__/time-15.json new file mode 100644 index 000000000..b5308554c --- /dev/null +++ b/packages/typing-fixtures/reference/keystrokes/__snapshots__/time-15.json @@ -0,0 +1,128 @@ +{ + "completedEvent": { + "wpm": 54.36, + "rawWpm": 59.96, + "charStats": [68, 3, 2, 0], + "charTotal": 75, + "acc": 93.33, + "language": "english", + "testDuration": 15.01, + "lastKeyToEnd": 118.4, + "startToFirstKey": 0, + "afkDuration": 1, + "tags": [], + "punctuation": false, + "numbers": false, + "lazyMode": false, + "timestamp": 1791473720263, + "mode": "time", + "mode2": "15", + "bailedOut": false, + "funbox": [], + "difficulty": "normal", + "blindMode": false, + "stopOnLetter": false, + "restartCount": 0, + "incompleteTests": [], + "incompleteTestSeconds": 0, + "consistency": 64.99, + "wpmConsistency": 81.89, + "keyConsistency": 7.22, + "chartData": { + "wpm": [84, 72, 72, 60, 53, 54, 57, 50, 47, 47, 49, 51, 53, 53, 54], + "burst": [84, 60, 72, 60, 72, 60, 72, 0, 24, 48, 72, 72, 72, 60, 72], + "err": [0, 0, 0, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + }, + "keySpacing": [ + 135.2, 183.90000000000003, 147.79999999999995, 121.30000000000007, + 227.29999999999995, 176.79999999999995, 184.29999999999995, + 128.9000000000001, 121.5, 287.5, 154.29999999999995, 196.60000000000014, + 200.4000000000001, 139.0999999999999, 242.4000000000001, 179, + 87.89999999999964, 192.9000000000001, 173.5999999999999, + 298.3000000000002, 182.0999999999999, 158, 101.5, 154.9000000000001, + 137.10000000000036, 152.59999999999945, 287, 171.69999999999982, + 150.30000000000018, 190.80000000000018, 299.6999999999998, + 182.40000000000055, 98.5, 192.39999999999964, 157.19999999999982, + 227.30000000000018, 159.80000000000018, 181.5, 203.39999999999964, + 1901.5999999999995, 98.20000000000073, 108.60000000000036, + 183.6999999999989, 205.90000000000146, 372, 195, 188.79999999999927, + 164.79999999999927, 136.5, 222.40000000000146, 153.89999999999964, + 198.10000000000036, 192.09999999999854, 140.90000000000146, + 241.89999999999964, 104.29999999999927, 205.20000000000073, + 113.60000000000036, 109.89999999999964, 288.5, 171.5, 105.29999999999927, + 104.30000000000109, 288.09999999999854, 177, 204, 205.40000000000146, + 136.59999999999854, 141.20000000000073, 235.29999999999927, + 90.30000000000109, 184.5, 143.39999999999964, 208.60000000000036 + ], + "keyDuration": [ + 59.6, 48.400000000000006, 92.79999999999995, 58.39999999999998, + 100.89999999999998, 48.700000000000045, 90.40000000000009, + 134.20000000000005, 115.79999999999995, 116.5, 132.0999999999999, + 40.299999999999955, 73.29999999999973, 126.39999999999964, + 121.19999999999982, 81, 37, 92.60000000000036, 106.20000000000027, + 125.40000000000009, 103, 134.30000000000018, 107.59999999999991, + 80.90000000000009, 132.30000000000018, 92.39999999999964, + 102.40000000000055, 80, 118.90000000000055, 144.80000000000018, + 119.30000000000018, 145.5, 46.099999999999454, 98.5, 76.60000000000036, + 70.80000000000018, 104.30000000000018, 82.80000000000018, + 58.30000000000018, 111.10000000000036, 124.5, 83.5, 117.89999999999964, + 106.5, 150, 105.5, 146.59999999999854, 74.5, 144.5, 105.5, + 108.39999999999964, 70.39999999999964, 46.399999999999636, + 49.70000000000073, 137.39999999999964, 111.5, 68.80000000000109, + 99.89999999999964, 40.399999999999636, 110.39999999999964, + 130.10000000000036, 103.29999999999927, 21.899999999999636, + 106.09999999999854, 104.30000000000109, 75.30000000000109, + 49.80000000000109, 60, 94.70000000000073, 100.89999999999964, + 111.40000000000146, 90.10000000000036, 74.69999999999891, + 80.70000000000073, 49 + ], + "keyOverlap": 74, + "client": "web" + }, + "hash": "cef782e3482d51f43184a11f4308cf68a585acc5", + "derived": { + "inputHistory": [ + "into ", + "over ", + "have ", + "look ", + "hphere ", + "not ", + "mean ", + "for ", + "most ", + "want ", + "lead ", + "much ", + "off ", + "think ", + "year " + ], + "missedWords": { + "here": 5 + }, + "wordBurstHistory": [ + 102, 98, 87, 95, 95, 94, 95, 88, 101, 88, 88, 113, 126, 83, 96 + ], + "correctedWordsHistory": [ + "into ", + "over ", + "have ", + "look ", + "hphere ", + "not ", + "mean ", + "for ", + "most ", + "want ", + "lead ", + "much ", + "off ", + "think ", + "year " + ], + "rawHistory": [84, 72, 72, 69, 70, 68, 69, 60, 56, 55, 57, 58, 59, 59, 60], + "keypressesPerSecond": [7, 5, 6, 5, 6, 5, 6, 0, 2, 4, 6, 6, 6, 5, 6], + "incompleteTestSeconds": 14.01 + } +} diff --git a/packages/typing-fixtures/reference/keystrokes/__snapshots__/words-10-clean.json b/packages/typing-fixtures/reference/keystrokes/__snapshots__/words-10-clean.json new file mode 100644 index 000000000..c143c1116 --- /dev/null +++ b/packages/typing-fixtures/reference/keystrokes/__snapshots__/words-10-clean.json @@ -0,0 +1,99 @@ +{ + "completedEvent": { + "wpm": 109.79, + "rawWpm": 109.79, + "charStats": [43, 0, 0, 0], + "charTotal": 43, + "acc": 100, + "language": "english", + "testDuration": 4.7, + "lastKeyToEnd": 3.3, + "startToFirstKey": 0, + "afkDuration": 0, + "tags": [], + "punctuation": false, + "numbers": false, + "lazyMode": false, + "timestamp": 1791473680017, + "mode": "words", + "mode2": "10", + "bailedOut": false, + "funbox": [], + "difficulty": "normal", + "blindMode": false, + "stopOnLetter": false, + "restartCount": 0, + "incompleteTests": [], + "incompleteTestSeconds": 0, + "consistency": 84.03, + "wpmConsistency": 95.73, + "keyConsistency": 59.8, + "chartData": { + "wpm": [120, 108, 116, 108, 110], + "burst": [120, 96, 132, 84, 120], + "err": [0, 0, 0, 0, 0] + }, + "keySpacing": [ + 87.5, 113.30000000000001, 173.7, 87, 87.39999999999998, + 165.89999999999998, 75.70000000000005, 88.70000000000005, + 84.39999999999998, 77.60000000000002, 132.5999999999999, + 93.20000000000005, 80.29999999999995, 169.4000000000001, + 102.79999999999995, 105.29999999999995, 105.40000000000009, + 237.20000000000005, 67.19999999999982, 82.90000000000009, + 72.69999999999982, 93.40000000000009, 75.59999999999991, 154, + 73.60000000000036, 100.89999999999964, 78.60000000000036, + 115.29999999999973, 259.0999999999999, 110.40000000000009, + 67.09999999999991, 216.60000000000036, 113.19999999999982, + 117.40000000000009, 67.19999999999982, 117.70000000000027, + 119.80000000000018, 82.5, 144.89999999999964, 99.19999999999982, 92, + 106.40000000000055 + ], + "keyDuration": [ + 37, 143.8, 71.69999999999999, 119.69999999999999, 149.70000000000005, + 92.80000000000007, 84.5, 56.5, 46.59999999999991, 124.89999999999998, + 60.09999999999991, 86.70000000000005, 129.9000000000001, + 61.299999999999955, 99.79999999999995, 62.5, 87.79999999999995, + 150.0999999999999, 122.90000000000009, 75.80000000000018, + 69.09999999999991, 118.5, 55, 45.5, 134.60000000000036, 151, + 83.10000000000036, 114.09999999999991, 145.20000000000027, + 138.30000000000018, 157.4000000000001, 42.70000000000027, + 72.09999999999991, 96, 114.90000000000009, 115, 56.399999999999636, + 56.099999999999454, 51.5, 74.39999999999964, 95.5, 46.400000000000546, + 104.72999999999956 + ], + "keyOverlap": 549.4, + "client": "web" + }, + "hash": "fea534e2d7a9e8cbc02838fdf7f336aaf4566602", + "derived": { + "inputHistory": [ + "of ", + "by ", + "come ", + "as ", + "can ", + "never ", + "they ", + "by ", + "system ", + "most" + ], + "missedWords": {}, + "wordBurstHistory": [177, 206, 183, 208, 154, 184, 164, 203, 136, 202], + "correctedWordsHistory": [ + "of ", + "by ", + "come ", + "as ", + "can ", + "never ", + "they ", + "by ", + "system ", + "most" + ], + "rawHistory": [120, 108, 116, 108, 110], + "keypressesPerSecond": [10, 8, 11, 7, 7], + "incompleteTestSeconds": 4.7 + } +} diff --git a/packages/typing-fixtures/reference/keystrokes/__snapshots__/words-10-errors.json b/packages/typing-fixtures/reference/keystrokes/__snapshots__/words-10-errors.json new file mode 100644 index 000000000..411162781 --- /dev/null +++ b/packages/typing-fixtures/reference/keystrokes/__snapshots__/words-10-errors.json @@ -0,0 +1,110 @@ +{ + "completedEvent": { + "wpm": 52.94, + "rawWpm": 63.53, + "charStats": [45, 1, 2, 0], + "charTotal": 54, + "acc": 91.38, + "language": "english", + "testDuration": 10.2, + "lastKeyToEnd": 3.7, + "startToFirstKey": 0, + "afkDuration": 0, + "tags": [], + "punctuation": false, + "numbers": false, + "lazyMode": false, + "timestamp": 1791473701467, + "mode": "words", + "mode2": "10", + "bailedOut": false, + "funbox": [], + "difficulty": "normal", + "blindMode": false, + "stopOnLetter": false, + "restartCount": 0, + "incompleteTests": [], + "incompleteTestSeconds": 0, + "consistency": 83.63, + "wpmConsistency": 86.18, + "keyConsistency": 67.27, + "chartData": { + "wpm": [48, 60, 64, 42, 46, 44, 43, 48, 51, 52], + "burst": [72, 60, 72, 84, 60, 60, 48, 84, 72, 60], + "err": [1, 0, 0, 3, 0, 0, 1, 0, 0, 0] + }, + "keySpacing": [ + 115, 193.7, 151.60000000000002, 277.40000000000003, 182.19999999999993, + 300.6, 171.4000000000001, 206.89999999999986, 251.20000000000005, + 106.79999999999995, 91.10000000000014, 145.5, 207.29999999999973, + 146.20000000000027, 183.9000000000001, 97.29999999999973, 207.5, + 90.40000000000009, 102.09999999999991, 145.80000000000018, + 175.9000000000001, 182.89999999999964, 189.20000000000027, + 303.2000000000003, 175.59999999999945, 191.60000000000036, + 151.59999999999945, 192.90000000000055, 157.39999999999964, 278, + 144.40000000000055, 249.79999999999927, 186.20000000000073, + 200.39999999999964, 113.10000000000036, 284.59999999999945, + 202.69999999999982, 277.5, 125.80000000000018, 250.30000000000018, + 133.89999999999964, 138.40000000000055, 146.89999999999964, + 121.90000000000055, 174.79999999999927, 233.5, 165.60000000000036, + 128.89999999999964, 104.10000000000036, 176.39999999999964, + 325.89999999999964, 159, 93.30000000000109, 152.10000000000036, + 192.09999999999854, 150.5, 195 + ], + "keyDuration": [ + 72.4, 99.4, 111, 80.40000000000003, 52.19999999999993, 61.5, + 57.09999999999991, 78.39999999999986, 43.700000000000045, 46, + 59.90000000000009, 143.0999999999999, 107.69999999999982, + 66.20000000000027, 76.40000000000009, 95.69999999999982, + 48.20000000000027, 128.30000000000018, 99.5, 45.90000000000009, + 64.69999999999982, 44.79999999999973, 140.9000000000001, + 87.69999999999982, 48.099999999999454, 147.90000000000055, + 108.59999999999945, 44.600000000000364, 131, 71.19999999999982, + 142.10000000000036, 77.19999999999982, 89.10000000000036, + 134.89999999999964, 86.30000000000018, 111, 118.60000000000036, + 63.80000000000018, 43, 129.90000000000055, 124.80000000000018, + 88.40000000000055, 133.89999999999964, 149.60000000000036, + 48.099999999999454, 42.400000000000546, 124.20000000000073, 71.5, 102.5, + 4.799999999999272, 148.5, 51.600000000000364, 143.20000000000073, + 103.29999999999927, 117.59999999999854, 85.60000000000036, 47, 89.5 + ], + "keyOverlap": 115.5, + "client": "web" + }, + "hash": "cf702ec601438e277d54d39dcfb14db01cef6d88", + "derived": { + "inputHistory": [ + "all ", + "man ", + "would ", + "schoolxx ", + "where ", + "day ", + "do ", + "never ", + "well ", + "against" + ], + "missedWords": { + "man": 1, + "school": 3, + "do": 1 + }, + "wordBurstHistory": [104, 56, 103, 91, 83, 54, 59, 101, 104, 102], + "correctedWordsHistory": [ + "all ", + "mqn ", + "would ", + "schoolxx ", + "where ", + "day ", + "d ", + "never ", + "well ", + "against" + ], + "rawHistory": [72, 60, 64, 69, 67, 62, 58, 61, 63, 62], + "keypressesPerSecond": [6, 5, 6, 7, 5, 5, 4, 7, 6, 5], + "incompleteTestSeconds": 10.2 + } +} diff --git a/packages/typing-fixtures/reference/keystrokes/__snapshots__/words-10-punctuation-numbers.json b/packages/typing-fixtures/reference/keystrokes/__snapshots__/words-10-punctuation-numbers.json new file mode 100644 index 000000000..5f52f8e5f --- /dev/null +++ b/packages/typing-fixtures/reference/keystrokes/__snapshots__/words-10-punctuation-numbers.json @@ -0,0 +1,102 @@ +{ + "completedEvent": { + "wpm": 73.82, + "rawWpm": 73.82, + "charStats": [51, 0, 0, 0], + "charTotal": 51, + "acc": 100, + "language": "english", + "testDuration": 8.29, + "lastKeyToEnd": 3.8, + "startToFirstKey": 0, + "afkDuration": 0, + "tags": [], + "punctuation": true, + "numbers": true, + "lazyMode": false, + "timestamp": 1791473781136, + "mode": "words", + "mode2": "10", + "bailedOut": false, + "funbox": [], + "difficulty": "normal", + "blindMode": false, + "stopOnLetter": false, + "restartCount": 0, + "incompleteTests": [], + "incompleteTestSeconds": 0, + "consistency": 90.21, + "wpmConsistency": 97.63, + "keyConsistency": 63.72, + "chartData": { + "wpm": [72, 78, 76, 75, 77, 76, 74, 74], + "burst": [72, 84, 72, 72, 84, 72, 60, 72], + "err": [0, 0, 0, 0, 0, 0, 0, 0] + }, + "keySpacing": [ + 117.1, 159.6, 288.90000000000003, 150.79999999999995, 151.70000000000005, + 157.39999999999998, 104.90000000000009, 331.1999999999998, 104, + 159.60000000000014, 179.5, 94.09999999999991, 133.50000000000023, + 129.39999999999964, 208.5, 127.80000000000018, 198.30000000000018, 172.5, + 165, 204.29999999999973, 126.59999999999991, 157.60000000000036, + 96.19999999999982, 186.80000000000018, 119.79999999999973, + 98.59999999999991, 263.10000000000036, 139.69999999999982, + 101.80000000000018, 191.69999999999982, 93.10000000000036, + 316.6999999999998, 136.30000000000018, 91.59999999999945, 225, + 90.90000000000055, 133.39999999999964, 209.39999999999964, + 191.10000000000036, 170.39999999999964, 284.2000000000007, + 159.79999999999927, 129.80000000000018, 128.90000000000055, + 124.09999999999945, 282.1999999999998, 178, 176.30000000000018, + 134.60000000000036, 207.9999999999991 + ], + "keyDuration": [ + 105.39999999999999, 115.80000000000001, 89.30000000000001, + 65.10000000000002, 46.39999999999998, 47, 63.299999999999955, 149.5, + 50.200000000000045, 44, 87.79999999999995, 123, 90.10000000000014, 95.5, + 94.70000000000027, 131.9000000000001, 121, 94.89999999999964, 121, + 75.29999999999973, 51.5, 151.70000000000027, 84.79999999999973, + 90.40000000000009, 147.89999999999964, 134.70000000000027, 100.5, 60, + 52.100000000000364, 150.19999999999982, 60.899999999999636, 143.5, + 126.39999999999964, 90.09999999999945, 89.5, 116.30000000000018, + 122.69999999999982, 70.80000000000018, 138.20000000000073, + 104.80000000000018, 146.30000000000018, 49, 83.20000000000073, + 135.10000000000036, 98.79999999999927, 119.60000000000036, + 84.10000000000036, 96.80000000000018, 132.60000000000036, + 70.19999999999982, 46.30000000000109 + ], + "keyOverlap": 128.8, + "client": "web" + }, + "hash": "b0c6102597a3924a5c67d4b412d2c0896366bae1", + "derived": { + "inputHistory": [ + "25 ", + "for. ", + "Before ", + "turn ", + "school ", + "line ", + "34 ", + "early ", + "not, ", + "most." + ], + "missedWords": {}, + "wordBurstHistory": [129, 106, 105, 91, 107, 114, 159, 91, 111, 103], + "correctedWordsHistory": [ + "25 ", + "for. ", + "Before ", + "turn ", + "school ", + "line ", + "34 ", + "early ", + "not, ", + "most." + ], + "rawHistory": [72, 78, 76, 75, 77, 76, 74, 74], + "keypressesPerSecond": [6, 7, 6, 6, 7, 6, 5, 6], + "incompleteTestSeconds": 8.29 + } +} diff --git a/packages/typing-fixtures/reference/keystrokes/__snapshots__/words-10-stop-on-letter.json b/packages/typing-fixtures/reference/keystrokes/__snapshots__/words-10-stop-on-letter.json new file mode 100644 index 000000000..47fb81ee8 --- /dev/null +++ b/packages/typing-fixtures/reference/keystrokes/__snapshots__/words-10-stop-on-letter.json @@ -0,0 +1,112 @@ +{ + "completedEvent": { + "wpm": 65.79, + "rawWpm": 65.79, + "charStats": [54, 0, 0, 0], + "charTotal": 54, + "acc": 90, + "language": "english", + "testDuration": 9.85, + "lastKeyToEnd": 3.2, + "startToFirstKey": 0, + "afkDuration": 0, + "tags": [], + "punctuation": false, + "numbers": false, + "lazyMode": false, + "timestamp": 1791473769303, + "mode": "words", + "mode2": "10", + "bailedOut": false, + "funbox": [], + "difficulty": "normal", + "blindMode": false, + "stopOnLetter": true, + "restartCount": 0, + "incompleteTests": [], + "incompleteTestSeconds": 0, + "consistency": 79.41, + "wpmConsistency": 95.35, + "keyConsistency": 71.11, + "chartData": { + "wpm": [72, 72, 72, 72, 70, 68, 67, 65, 63, 66], + "burst": [72, 96, 72, 72, 84, 60, 60, 72, 48, 99], + "err": [0, 2, 0, 0, 2, 0, 0, 2, 0, 0] + }, + "keySpacing": [ + 197.6, 153.4, 145.5, 155.60000000000002, 251, 180.89999999999998, + 114.09999999999991, 124.40000000000009, 126.40000000000009, + 120.89999999999986, 113.90000000000009, 108.09999999999991, + 155.9000000000001, 234.89999999999986, 154.20000000000027, + 173.5999999999999, 164.5, 190.0999999999999, 110.19999999999982, + 141.60000000000036, 150, 132.5, 147.5999999999999, 172.5, + 129.5999999999999, 198.80000000000018, 200.09999999999945, + 112.40000000000055, 127.09999999999945, 175.60000000000036, + 154.89999999999964, 167.80000000000018, 255.10000000000036, + 168.89999999999964, 204.69999999999982, 161.30000000000018, + 206.60000000000036, 297.6999999999998, 152.5, 177, 179.69999999999982, + 155.19999999999982, 284.8000000000002, 180.80000000000018, + 126.60000000000036, 90.5, 204.09999999999945, 165, 213.59999999999945, + 176.10000000000036, 123.70000000000073, 326.60000000000036, + 164.29999999999927, 104.79999999999927, 150.10000000000036, + 109.20000000000073, 118.29999999999927, 192.89999999999964, + 168.90000000000146 + ], + "keyDuration": [ + 55, 117.00000000000003, 85.80000000000001, 66.39999999999998, + 120.39999999999998, 56.799999999999955, 96.79999999999995, + 79.30000000000018, 99, 148.29999999999995, 136.9000000000001, + 52.799999999999955, 73.40000000000009, 125.39999999999986, + 139.0999999999999, 108.59999999999991, 145.4000000000001, 141, + 42.80000000000018, 79.60000000000036, 40.79999999999973, + 122.89999999999964, 86, 143, 117.5, 81.5, 54.80000000000018, + 75.20000000000073, 111.09999999999945, 47.5, 141.5, 131.60000000000036, + 141.80000000000018, 96.69999999999982, 138.40000000000055, + 131.40000000000055, 146.69999999999982, 98.59999999999945, 39, 75, + 97.60000000000036, 147.30000000000018, 133.20000000000073, + 112.60000000000036, 101, 66.79999999999927, 42.19999999999982, + 131.69999999999982, 41.69999999999982, 107.30000000000109, + 148.89999999999964, 148.39999999999964, 127.69999999999891, + 129.79999999999927, 149.20000000000073, 105, 87.5, 129.5, + 121.30000000000109, 79 + ], + "keyOverlap": 100.6, + "client": "web" + }, + "hash": "d782f60da839d7fc27d0e0013c2f2cfc468f9087", + "derived": { + "inputHistory": [ + "will ", + "people ", + "become ", + "open ", + "such ", + "hold ", + "what ", + "one ", + "at ", + "possible" + ], + "missedWords": { + "people": 2, + "such": 2, + "one": 2 + }, + "wordBurstHistory": [92, 80, 90, 103, 64, 81, 90, 63, 120, 107], + "correctedWordsHistory": [ + "will ", + "people ", + "become ", + "open ", + "such ", + "hold ", + "what ", + "one ", + "at ", + "possible" + ], + "rawHistory": [72, 72, 72, 72, 70, 68, 67, 65, 63, 66], + "keypressesPerSecond": [6, 8, 6, 6, 7, 5, 5, 6, 4, 7], + "incompleteTestSeconds": 9.85 + } +} diff --git a/packages/typing-fixtures/reference/keystrokes/__snapshots__/zen.json b/packages/typing-fixtures/reference/keystrokes/__snapshots__/zen.json new file mode 100644 index 000000000..aa44ca54f --- /dev/null +++ b/packages/typing-fixtures/reference/keystrokes/__snapshots__/zen.json @@ -0,0 +1,70 @@ +{ + "completedEvent": { + "wpm": 68.42, + "rawWpm": 68.42, + "charStats": [26, 0, 0, 0], + "charTotal": 26, + "acc": 100, + "language": "english", + "testDuration": 4.56, + "lastKeyToEnd": 0, + "startToFirstKey": 0, + "afkDuration": 0, + "tags": [], + "punctuation": false, + "numbers": false, + "lazyMode": false, + "timestamp": 1791473729386, + "mode": "zen", + "mode2": "zen", + "bailedOut": false, + "funbox": [], + "difficulty": "normal", + "blindMode": false, + "stopOnLetter": false, + "restartCount": 0, + "incompleteTests": [], + "incompleteTestSeconds": 0, + "consistency": 86.36, + "wpmConsistency": 90.89, + "keyConsistency": 62.49, + "chartData": { + "wpm": [84, 84, 76, 69, 68], + "burst": [84, 84, 72, 60, 64], + "err": [0, 0, 0, 0, 0] + }, + "keySpacing": [ + 105.7, 117.8, 100.80000000000001, 165, 181.40000000000003, 180, + 149.89999999999998, 141.4999999999999, 178.80000000000018, 208, + 131.5999999999999, 122.59999999999991, 153.20000000000005, + 145.39999999999986, 148.10000000000036, 183, 186, 130.39999999999964, + 97.10000000000036, 423.0999999999999, 130.19999999999982, + 169.30000000000018, 94.69999999999982, 208.0999999999999, + 147.4000000000001, 174.4000000000001, 206.30000000000018, + 180.59999999999945 + ], + "keyDuration": [ + 73.7, 53.2, 98.60000000000002, 147.5, 116.09999999999997, + 82.09999999999991, 135.0999999999999, 122.89999999999998, + 95.90000000000009, 143.29999999999995, 92.59999999999991, 126.5, + 121.40000000000009, 78.70000000000005, 39.80000000000018, + 100.89999999999964, 48.29999999999973, 84.19999999999982, + 101.20000000000027, 108, 137.69999999999982, 60.40000000000009, + 93.69999999999982, 126.70000000000027, 39.30000000000018, + 142.50000000000045, 100.30000000000018, 72.89999999999964, + 22.400000000000546 + ], + "keyOverlap": 15.5, + "client": "web" + }, + "hash": "f84db70cdbf3cb6053a2cfb617cea4b16418ea51", + "derived": { + "inputHistory": ["hello ", "there ", "zen\n", "mode ", "rocks"], + "missedWords": {}, + "wordBurstHistory": [107, 89, 107, 53, 98], + "correctedWordsHistory": ["hello ", "there ", "zen\n", "moed ", "rocks"], + "rawHistory": [84, 84, 76, 69, 68], + "keypressesPerSecond": [7, 7, 6, 5, 3], + "incompleteTestSeconds": 4.56 + } +} diff --git a/packages/typing-fixtures/reference/keystrokes/custom-repeat.json b/packages/typing-fixtures/reference/keystrokes/custom-repeat.json new file mode 100644 index 000000000..01e194b4c --- /dev/null +++ b/packages/typing-fixtures/reference/keystrokes/custom-repeat.json @@ -0,0 +1,1645 @@ +{ + "name": "custom-repeat", + "description": "custom text, repeat, word limit 9", + "recordedWith": "web", + "config": { + "mode": "custom", + "time": 30, + "words": 50, + "language": "english", + "punctuation": false, + "numbers": false, + "lazyMode": false, + "funbox": [], + "difficulty": "normal", + "blindMode": false, + "stopOnError": "off", + "confidenceMode": "off", + "freedomMode": false, + "strictSpace": false, + "quickEnd": false, + "deleteOnError": "off", + "oppositeShiftMode": "off", + "britishEnglish": false, + "quoteLength": [1] + }, + "customText": { + "text": [ + "The", + "quick", + "brown", + "fox", + "jumps", + "over", + "the", + "lazy", + "dog" + ], + "mode": "repeat", + "limit": { + "value": 9, + "mode": "word" + }, + "pipeDelimiter": false + }, + "keystrokes": [ + { + "kind": "key", + "key": "T", + "gap": 173, + "hold": 125 + }, + { + "kind": "key", + "key": "h", + "gap": 116, + "hold": 109 + }, + { + "kind": "key", + "key": "e", + "gap": 100, + "hold": 105 + }, + { + "kind": "key", + "key": " ", + "gap": 176, + "hold": 90 + }, + { + "kind": "key", + "key": "q", + "gap": 199, + "hold": 65 + }, + { + "kind": "key", + "key": "u", + "gap": 184, + "hold": 121 + }, + { + "kind": "key", + "key": "i", + "gap": 169, + "hold": 42 + }, + { + "kind": "key", + "key": "c", + "gap": 146, + "hold": 73 + }, + { + "kind": "key", + "key": "k", + "gap": 198, + "hold": 72 + }, + { + "kind": "key", + "key": " ", + "gap": 181, + "hold": 120 + }, + { + "kind": "key", + "key": "b", + "gap": 120, + "hold": 42 + }, + { + "kind": "key", + "key": "r", + "gap": 185, + "hold": 63 + }, + { + "kind": "key", + "key": "o", + "gap": 123, + "hold": 54 + }, + { + "kind": "key", + "key": "w", + "gap": 146, + "hold": 149 + }, + { + "kind": "key", + "key": "n", + "gap": 119, + "hold": 49 + }, + { + "kind": "key", + "key": "s", + "gap": 192, + "hold": 75 + }, + { + "kind": "key", + "key": " ", + "gap": 139, + "hold": 148 + }, + { + "kind": "key", + "key": "f", + "gap": 123, + "hold": 82 + }, + { + "kind": "key", + "key": "o", + "gap": 116, + "hold": 136 + }, + { + "kind": "key", + "key": "x", + "gap": 177, + "hold": 137 + }, + { + "kind": "key", + "key": " ", + "gap": 151, + "hold": 129 + }, + { + "kind": "key", + "key": "j", + "gap": 168, + "hold": 55 + }, + { + "kind": "key", + "key": "u", + "gap": 166, + "hold": 104 + }, + { + "kind": "key", + "key": "m", + "gap": 128, + "hold": 126 + }, + { + "kind": "key", + "key": "p", + "gap": 163, + "hold": 41 + }, + { + "kind": "key", + "key": "s", + "gap": 202, + "hold": 78 + }, + { + "kind": "key", + "key": " ", + "gap": 132, + "hold": 136 + }, + { + "kind": "key", + "key": "o", + "gap": 120, + "hold": 59 + }, + { + "kind": "key", + "key": "v", + "gap": 190, + "hold": 71 + }, + { + "kind": "key", + "key": "e", + "gap": 210, + "hold": 42 + }, + { + "kind": "key", + "key": "r", + "gap": 132, + "hold": 123 + }, + { + "kind": "key", + "key": " ", + "gap": 128, + "hold": 113 + }, + { + "kind": "key", + "key": "t", + "gap": 99, + "hold": 49 + }, + { + "kind": "key", + "key": "h", + "gap": 178, + "hold": 98 + }, + { + "kind": "key", + "key": "e", + "gap": 120, + "hold": 142 + }, + { + "kind": "key", + "key": " ", + "gap": 96, + "hold": 121 + }, + { + "kind": "key", + "key": "l", + "gap": 204, + "hold": 71 + }, + { + "kind": "key", + "key": "a", + "gap": 154, + "hold": 134 + }, + { + "kind": "key", + "key": "z", + "gap": 192, + "hold": 104 + }, + { + "kind": "key", + "key": "y", + "gap": 205, + "hold": 117 + }, + { + "kind": "key", + "key": " ", + "gap": 155, + "hold": 52 + }, + { + "kind": "key", + "key": "d", + "gap": 125, + "hold": 98 + }, + { + "kind": "key", + "key": "o", + "gap": 109, + "hold": 68 + }, + { + "kind": "key", + "key": "g", + "gap": 182, + "hold": 55 + } + ], + "eventLog": { + "version": 1, + "events": [ + { + "type": "keydown", + "testMs": -6.9, + "data": { + "code": "KeyT" + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": "T", + "correct": true, + "wordIndex": 0, + "charIndex": 0, + "inputValue": "T" + } + }, + { + "type": "timer", + "testMs": 0, + "data": { + "event": "start", + "timer": 0, + "date": 1791473732956 + } + }, + { + "type": "keydown", + "testMs": 105.4, + "data": { + "code": "KeyH" + } + }, + { + "type": "input", + "testMs": 106.5, + "data": { + "inputType": "insertText", + "data": "h", + "correct": true, + "wordIndex": 0, + "charIndex": 1, + "inputValue": "Th" + } + }, + { + "type": "keyup", + "testMs": 112.9, + "data": { + "code": "KeyT" + } + }, + { + "type": "keydown", + "testMs": 203.9, + "data": { + "code": "KeyE" + } + }, + { + "type": "input", + "testMs": 205, + "data": { + "inputType": "insertText", + "data": "e", + "correct": true, + "wordIndex": 0, + "charIndex": 2, + "inputValue": "The" + } + }, + { + "type": "keyup", + "testMs": 213, + "data": { + "code": "KeyH" + } + }, + { + "type": "keyup", + "testMs": 311.8, + "data": { + "code": "KeyE" + } + }, + { + "type": "keydown", + "testMs": 382.7, + "data": { + "code": "Space" + } + }, + { + "type": "input", + "testMs": 385.9, + "data": { + "inputType": "insertText", + "data": " ", + "correct": true, + "wordIndex": 0, + "charIndex": 3, + "inputValue": "The ", + "commitsWord": true + } + }, + { + "type": "keyup", + "testMs": 471.6, + "data": { + "code": "Space" + } + }, + { + "type": "keydown", + "testMs": 683, + "data": { + "code": "KeyQ" + } + }, + { + "type": "input", + "testMs": 685.6, + "data": { + "inputType": "insertText", + "data": "q", + "correct": true, + "wordIndex": 1, + "charIndex": 0, + "inputValue": "q" + } + }, + { + "type": "keyup", + "testMs": 747.8, + "data": { + "code": "KeyQ" + } + }, + { + "type": "keydown", + "testMs": 866, + "data": { + "code": "KeyU" + } + }, + { + "type": "input", + "testMs": 867.4, + "data": { + "inputType": "insertText", + "data": "u", + "correct": true, + "wordIndex": 1, + "charIndex": 1, + "inputValue": "qu" + } + }, + { + "type": "keyup", + "testMs": 987.7, + "data": { + "code": "KeyU" + } + }, + { + "type": "timer", + "testMs": 1003.2, + "data": { + "event": "step", + "timer": 1, + "drift": 3.2 + } + }, + { + "type": "keydown", + "testMs": 1034.6, + "data": { + "code": "KeyI" + } + }, + { + "type": "input", + "testMs": 1036.4, + "data": { + "inputType": "insertText", + "data": "i", + "correct": true, + "wordIndex": 1, + "charIndex": 2, + "inputValue": "qui" + } + }, + { + "type": "keyup", + "testMs": 1074.3, + "data": { + "code": "KeyI" + } + }, + { + "type": "keydown", + "testMs": 1182.2, + "data": { + "code": "KeyC" + } + }, + { + "type": "input", + "testMs": 1184.7, + "data": { + "inputType": "insertText", + "data": "c", + "correct": true, + "wordIndex": 1, + "charIndex": 3, + "inputValue": "quic" + } + }, + { + "type": "keyup", + "testMs": 1254.5, + "data": { + "code": "KeyC" + } + }, + { + "type": "keydown", + "testMs": 1380, + "data": { + "code": "KeyK" + } + }, + { + "type": "input", + "testMs": 1382.7, + "data": { + "inputType": "insertText", + "data": "k", + "correct": true, + "wordIndex": 1, + "charIndex": 4, + "inputValue": "quick" + } + }, + { + "type": "keyup", + "testMs": 1452.7, + "data": { + "code": "KeyK" + } + }, + { + "type": "keydown", + "testMs": 1560.8, + "data": { + "code": "Space" + } + }, + { + "type": "input", + "testMs": 1563.1, + "data": { + "inputType": "insertText", + "data": " ", + "correct": true, + "wordIndex": 1, + "charIndex": 5, + "inputValue": "quick ", + "commitsWord": true + } + }, + { + "type": "keyup", + "testMs": 1679.8, + "data": { + "code": "Space" + } + }, + { + "type": "keydown", + "testMs": 1808.3, + "data": { + "code": "KeyB" + } + }, + { + "type": "input", + "testMs": 1811, + "data": { + "inputType": "insertText", + "data": "b", + "correct": true, + "wordIndex": 2, + "charIndex": 0, + "inputValue": "b" + } + }, + { + "type": "keyup", + "testMs": 1851.1, + "data": { + "code": "KeyB" + } + }, + { + "type": "keydown", + "testMs": 1995.8, + "data": { + "code": "KeyR" + } + }, + { + "type": "input", + "testMs": 1998.5, + "data": { + "inputType": "insertText", + "data": "r", + "correct": true, + "wordIndex": 2, + "charIndex": 1, + "inputValue": "br" + } + }, + { + "type": "timer", + "testMs": 2002.8, + "data": { + "event": "step", + "timer": 2, + "drift": 2.8 + } + }, + { + "type": "keyup", + "testMs": 2055.8, + "data": { + "code": "KeyR" + } + }, + { + "type": "keydown", + "testMs": 2116.5, + "data": { + "code": "KeyO" + } + }, + { + "type": "input", + "testMs": 2118.2, + "data": { + "inputType": "insertText", + "data": "o", + "correct": true, + "wordIndex": 2, + "charIndex": 2, + "inputValue": "bro" + } + }, + { + "type": "keyup", + "testMs": 2171.2, + "data": { + "code": "KeyO" + } + }, + { + "type": "keydown", + "testMs": 2262.2, + "data": { + "code": "KeyW" + } + }, + { + "type": "input", + "testMs": 2264.6, + "data": { + "inputType": "insertText", + "data": "w", + "correct": true, + "wordIndex": 2, + "charIndex": 3, + "inputValue": "brow" + } + }, + { + "type": "keydown", + "testMs": 2383.3, + "data": { + "code": "KeyN" + } + }, + { + "type": "input", + "testMs": 2385.4, + "data": { + "inputType": "insertText", + "data": "n", + "correct": true, + "wordIndex": 2, + "charIndex": 4, + "inputValue": "brown" + } + }, + { + "type": "keyup", + "testMs": 2410.7, + "data": { + "code": "KeyW" + } + }, + { + "type": "keyup", + "testMs": 2429.9, + "data": { + "code": "KeyN" + } + }, + { + "type": "keydown", + "testMs": 2573.5, + "data": { + "code": "KeyS" + } + }, + { + "type": "input", + "testMs": 2577.8, + "data": { + "inputType": "insertText", + "data": "s", + "correct": false, + "wordIndex": 2, + "charIndex": 5, + "inputValue": "browns" + } + }, + { + "type": "keyup", + "testMs": 2648.8, + "data": { + "code": "KeyS" + } + }, + { + "type": "keydown", + "testMs": 2712.6, + "data": { + "code": "Space" + } + }, + { + "type": "input", + "testMs": 2714.7, + "data": { + "inputType": "insertText", + "data": " ", + "correct": false, + "wordIndex": 2, + "charIndex": 6, + "inputValue": "browns ", + "commitsWord": true + } + }, + { + "type": "keyup", + "testMs": 2860.4, + "data": { + "code": "Space" + } + }, + { + "type": "keydown", + "testMs": 2993.5, + "data": { + "code": "KeyF" + } + }, + { + "type": "input", + "testMs": 2995.9, + "data": { + "inputType": "insertText", + "data": "f", + "correct": true, + "wordIndex": 3, + "charIndex": 0, + "inputValue": "f" + } + }, + { + "type": "timer", + "testMs": 3002.6, + "data": { + "event": "step", + "timer": 3, + "drift": 2.6 + } + }, + { + "type": "keyup", + "testMs": 3074.4, + "data": { + "code": "KeyF" + } + }, + { + "type": "keydown", + "testMs": 3108.1, + "data": { + "code": "KeyO" + } + }, + { + "type": "input", + "testMs": 3109.2, + "data": { + "inputType": "insertText", + "data": "o", + "correct": true, + "wordIndex": 3, + "charIndex": 1, + "inputValue": "fo" + } + }, + { + "type": "keyup", + "testMs": 3245.9, + "data": { + "code": "KeyO" + } + }, + { + "type": "keydown", + "testMs": 3286.3, + "data": { + "code": "KeyX" + } + }, + { + "type": "input", + "testMs": 3288.7, + "data": { + "inputType": "insertText", + "data": "x", + "correct": true, + "wordIndex": 3, + "charIndex": 2, + "inputValue": "fox" + } + }, + { + "type": "keyup", + "testMs": 3423.4, + "data": { + "code": "KeyX" + } + }, + { + "type": "keydown", + "testMs": 3436.9, + "data": { + "code": "Space" + } + }, + { + "type": "input", + "testMs": 3439, + "data": { + "inputType": "insertText", + "data": " ", + "correct": true, + "wordIndex": 3, + "charIndex": 3, + "inputValue": "fox ", + "commitsWord": true + } + }, + { + "type": "keyup", + "testMs": 3566.4, + "data": { + "code": "Space" + } + }, + { + "type": "keydown", + "testMs": 3743.8, + "data": { + "code": "KeyJ" + } + }, + { + "type": "input", + "testMs": 3745.1, + "data": { + "inputType": "insertText", + "data": "j", + "correct": true, + "wordIndex": 4, + "charIndex": 0, + "inputValue": "j" + } + }, + { + "type": "keyup", + "testMs": 3798.9, + "data": { + "code": "KeyJ" + } + }, + { + "type": "keydown", + "testMs": 3911.1, + "data": { + "code": "KeyU" + } + }, + { + "type": "input", + "testMs": 3913.6, + "data": { + "inputType": "insertText", + "data": "u", + "correct": true, + "wordIndex": 4, + "charIndex": 1, + "inputValue": "ju" + } + }, + { + "type": "timer", + "testMs": 4002.9, + "data": { + "event": "step", + "timer": 4, + "drift": 2.9 + } + }, + { + "type": "keyup", + "testMs": 4012, + "data": { + "code": "KeyU" + } + }, + { + "type": "keydown", + "testMs": 4038.5, + "data": { + "code": "KeyM" + } + }, + { + "type": "input", + "testMs": 4040.4, + "data": { + "inputType": "insertText", + "data": "m", + "correct": true, + "wordIndex": 4, + "charIndex": 2, + "inputValue": "jum" + } + }, + { + "type": "keyup", + "testMs": 4164.5, + "data": { + "code": "KeyM" + } + }, + { + "type": "keydown", + "testMs": 4199.8, + "data": { + "code": "KeyP" + } + }, + { + "type": "input", + "testMs": 4202, + "data": { + "inputType": "insertText", + "data": "p", + "correct": true, + "wordIndex": 4, + "charIndex": 3, + "inputValue": "jump" + } + }, + { + "type": "keyup", + "testMs": 4242.2, + "data": { + "code": "KeyP" + } + }, + { + "type": "keydown", + "testMs": 4404.5, + "data": { + "code": "KeyS" + } + }, + { + "type": "input", + "testMs": 4406.9, + "data": { + "inputType": "insertText", + "data": "s", + "correct": true, + "wordIndex": 4, + "charIndex": 4, + "inputValue": "jumps" + } + }, + { + "type": "keyup", + "testMs": 4481.2, + "data": { + "code": "KeyS" + } + }, + { + "type": "keydown", + "testMs": 4535.6, + "data": { + "code": "Space" + } + }, + { + "type": "input", + "testMs": 4537.6, + "data": { + "inputType": "insertText", + "data": " ", + "correct": true, + "wordIndex": 4, + "charIndex": 5, + "inputValue": "jumps ", + "commitsWord": true + } + }, + { + "type": "keyup", + "testMs": 4670.7, + "data": { + "code": "Space" + } + }, + { + "type": "keydown", + "testMs": 4800.7, + "data": { + "code": "KeyO" + } + }, + { + "type": "input", + "testMs": 4803, + "data": { + "inputType": "insertText", + "data": "o", + "correct": true, + "wordIndex": 5, + "charIndex": 0, + "inputValue": "o" + } + }, + { + "type": "keyup", + "testMs": 4858.4, + "data": { + "code": "KeyO" + } + }, + { + "type": "keydown", + "testMs": 4991.4, + "data": { + "code": "KeyV" + } + }, + { + "type": "input", + "testMs": 4993.9, + "data": { + "inputType": "insertText", + "data": "v", + "correct": true, + "wordIndex": 5, + "charIndex": 1, + "inputValue": "ov" + } + }, + { + "type": "timer", + "testMs": 5002.9, + "data": { + "event": "step", + "timer": 5, + "drift": 2.9 + } + }, + { + "type": "keyup", + "testMs": 5060.6, + "data": { + "code": "KeyV" + } + }, + { + "type": "keydown", + "testMs": 5199.9, + "data": { + "code": "KeyE" + } + }, + { + "type": "input", + "testMs": 5202.3, + "data": { + "inputType": "insertText", + "data": "e", + "correct": true, + "wordIndex": 5, + "charIndex": 2, + "inputValue": "ove" + } + }, + { + "type": "keyup", + "testMs": 5242.7, + "data": { + "code": "KeyE" + } + }, + { + "type": "keydown", + "testMs": 5330.9, + "data": { + "code": "KeyR" + } + }, + { + "type": "input", + "testMs": 5331.6, + "data": { + "inputType": "insertText", + "data": "r", + "correct": true, + "wordIndex": 5, + "charIndex": 3, + "inputValue": "over" + } + }, + { + "type": "keyup", + "testMs": 5453.2, + "data": { + "code": "KeyR" + } + }, + { + "type": "keydown", + "testMs": 5456.7, + "data": { + "code": "Space" + } + }, + { + "type": "input", + "testMs": 5457.4, + "data": { + "inputType": "insertText", + "data": " ", + "correct": true, + "wordIndex": 5, + "charIndex": 4, + "inputValue": "over ", + "commitsWord": true + } + }, + { + "type": "keyup", + "testMs": 5571.5, + "data": { + "code": "Space" + } + }, + { + "type": "keydown", + "testMs": 5679.1, + "data": { + "code": "KeyT" + } + }, + { + "type": "input", + "testMs": 5681.2, + "data": { + "inputType": "insertText", + "data": "t", + "correct": true, + "wordIndex": 6, + "charIndex": 0, + "inputValue": "t" + } + }, + { + "type": "keyup", + "testMs": 5727, + "data": { + "code": "KeyT" + } + }, + { + "type": "keydown", + "testMs": 5855.8, + "data": { + "code": "KeyH" + } + }, + { + "type": "input", + "testMs": 5856.9, + "data": { + "inputType": "insertText", + "data": "h", + "correct": true, + "wordIndex": 6, + "charIndex": 1, + "inputValue": "th" + } + }, + { + "type": "keyup", + "testMs": 5954.8, + "data": { + "code": "KeyH" + } + }, + { + "type": "keydown", + "testMs": 5976.1, + "data": { + "code": "KeyE" + } + }, + { + "type": "input", + "testMs": 5977.8, + "data": { + "inputType": "insertText", + "data": "e", + "correct": true, + "wordIndex": 6, + "charIndex": 2, + "inputValue": "the" + } + }, + { + "type": "timer", + "testMs": 6003, + "data": { + "event": "step", + "timer": 6, + "drift": 3 + } + }, + { + "type": "keydown", + "testMs": 6070.8, + "data": { + "code": "Space" + } + }, + { + "type": "input", + "testMs": 6071.7, + "data": { + "inputType": "insertText", + "data": " ", + "correct": true, + "wordIndex": 6, + "charIndex": 3, + "inputValue": "the ", + "commitsWord": true + } + }, + { + "type": "keyup", + "testMs": 6118.1, + "data": { + "code": "KeyE" + } + }, + { + "type": "keyup", + "testMs": 6193.1, + "data": { + "code": "Space" + } + }, + { + "type": "keydown", + "testMs": 6406.9, + "data": { + "code": "KeyL" + } + }, + { + "type": "input", + "testMs": 6409.2, + "data": { + "inputType": "insertText", + "data": "l", + "correct": true, + "wordIndex": 7, + "charIndex": 0, + "inputValue": "l" + } + }, + { + "type": "keyup", + "testMs": 6476.7, + "data": { + "code": "KeyL" + } + }, + { + "type": "keydown", + "testMs": 6559.8, + "data": { + "code": "KeyA" + } + }, + { + "type": "input", + "testMs": 6562, + "data": { + "inputType": "insertText", + "data": "a", + "correct": true, + "wordIndex": 7, + "charIndex": 1, + "inputValue": "la" + } + }, + { + "type": "keyup", + "testMs": 6694.7, + "data": { + "code": "KeyA" + } + }, + { + "type": "keydown", + "testMs": 6753, + "data": { + "code": "KeyZ" + } + }, + { + "type": "input", + "testMs": 6755.5, + "data": { + "inputType": "insertText", + "data": "z", + "correct": true, + "wordIndex": 7, + "charIndex": 2, + "inputValue": "laz" + } + }, + { + "type": "keyup", + "testMs": 6855.9, + "data": { + "code": "KeyZ" + } + }, + { + "type": "keydown", + "testMs": 6958.3, + "data": { + "code": "KeyY" + } + }, + { + "type": "input", + "testMs": 6960.8, + "data": { + "inputType": "insertText", + "data": "y", + "correct": true, + "wordIndex": 7, + "charIndex": 3, + "inputValue": "lazy" + } + }, + { + "type": "timer", + "testMs": 7002.9, + "data": { + "event": "step", + "timer": 7, + "drift": 2.9 + } + }, + { + "type": "keyup", + "testMs": 7074.6, + "data": { + "code": "KeyY" + } + }, + { + "type": "keydown", + "testMs": 7110.5, + "data": { + "code": "Space" + } + }, + { + "type": "input", + "testMs": 7111.9, + "data": { + "inputType": "insertText", + "data": " ", + "correct": true, + "wordIndex": 7, + "charIndex": 4, + "inputValue": "lazy ", + "commitsWord": true + } + }, + { + "type": "keyup", + "testMs": 7164.7, + "data": { + "code": "Space" + } + }, + { + "type": "keydown", + "testMs": 7300.1, + "data": { + "code": "KeyD" + } + }, + { + "type": "input", + "testMs": 7302.6, + "data": { + "inputType": "insertText", + "data": "d", + "correct": true, + "wordIndex": 8, + "charIndex": 0, + "inputValue": "d", + "lastWord": true + } + }, + { + "type": "keyup", + "testMs": 7396.7, + "data": { + "code": "KeyD" + } + }, + { + "type": "keydown", + "testMs": 7405.2, + "data": { + "code": "KeyO" + } + }, + { + "type": "input", + "testMs": 7406.3, + "data": { + "inputType": "insertText", + "data": "o", + "correct": true, + "wordIndex": 8, + "charIndex": 1, + "inputValue": "do", + "lastWord": true + } + }, + { + "type": "keyup", + "testMs": 7475.7, + "data": { + "code": "KeyO" + } + }, + { + "type": "keydown", + "testMs": 7591.5, + "data": { + "code": "KeyG" + } + }, + { + "type": "input", + "testMs": 7594, + "data": { + "inputType": "insertText", + "data": "g", + "correct": true, + "wordIndex": 8, + "charIndex": 2, + "inputValue": "dog", + "lastWord": true + } + }, + { + "type": "timer", + "testMs": 7595.4, + "data": { + "event": "end", + "timer": 7, + "date": 1791473740549 + } + }, + { + "type": "keyup", + "testMs": 7644.7, + "data": { + "code": "KeyG" + } + } + ], + "context": { + "targetWords": [ + "The ", + "quick ", + "brown ", + "fox ", + "jumps ", + "over ", + "the ", + "lazy ", + "dog" + ], + "mode": "custom", + "mode2": "custom", + "koreanStatus": false, + "bailedOut": false, + "customTextLimitMode": "word", + "customTextLimitValue": 9 + } + }, + "completedEvent": { + "wpm": 58.46, + "rawWpm": 69.52, + "charStats": [37, 1, 1, 0], + "charTotal": 44, + "acc": 95.45, + "language": "english", + "testDuration": 7.5954, + "lastKeyToEnd": 3.9, + "startToFirstKey": 0, + "afkDuration": 0, + "customText": { + "textLen": 9, + "mode": "repeat", + "pipeDelimiter": false, + "limit": { + "mode": "word", + "value": 9 + } + }, + "tags": [], + "punctuation": false, + "numbers": false, + "lazyMode": false, + "timestamp": 1791473740686, + "mode": "custom", + "mode2": "custom", + "bailedOut": false, + "funbox": [], + "difficulty": "normal", + "blindMode": false, + "stopOnLetter": false, + "restartCount": 0, + "incompleteTests": [], + "incompleteTestSeconds": 0, + "consistency": 90.69, + "wpmConsistency": 83.55, + "keyConsistency": 67.44, + "chartData": { + "wpm": [72, 72, 44, 48, 53, 56, 57, 58], + "burst": [72, 72, 72, 60, 72, 72, 60, 81], + "err": [0, 0, 2, 0, 0, 0, 0, 0] + }, + "keySpacing": [ + 105.4, 98.5, 178.79999999999998, 300.3, 183, 168.5999999999999, + 147.60000000000014, 197.79999999999995, 180.79999999999995, 247.5, 187.5, + 120.70000000000005, 145.69999999999982, 121.10000000000036, + 190.19999999999982, 139.0999999999999, 280.9000000000001, + 114.59999999999991, 178.20000000000027, 150.5999999999999, + 306.9000000000001, 167.29999999999973, 127.40000000000009, + 161.30000000000018, 204.69999999999982, 131.10000000000036, + 265.09999999999945, 190.69999999999982, 208.5, 131, 125.80000000000018, + 222.40000000000055, 176.69999999999982, 120.30000000000018, + 94.69999999999982, 336.09999999999945, 152.90000000000055, + 193.19999999999982, 205.30000000000018, 152.19999999999982, + 189.60000000000036, 105.09999999999945, 186.30000000000018 + ], + "keyDuration": [ + 119.80000000000001, 107.6, 107.9, 88.90000000000003, 64.79999999999995, + 121.70000000000005, 39.700000000000045, 72.29999999999995, + 72.70000000000005, 119, 42.799999999999955, 60.00000000000023, + 54.69999999999982, 148.5, 46.59999999999991, 75.30000000000018, + 147.80000000000018, 80.90000000000009, 137.80000000000018, + 137.0999999999999, 129.5, 55.09999999999991, 100.90000000000009, 126, + 42.399999999999636, 76.69999999999982, 135.09999999999945, + 57.69999999999982, 69.20000000000073, 42.80000000000018, + 122.30000000000018, 114.80000000000018, 47.899999999999636, 99, 142, + 122.30000000000018, 69.80000000000018, 134.89999999999964, + 102.89999999999964, 116.30000000000018, 54.19999999999982, + 96.59999999999945, 70.5, 53.19999999999982 + ], + "keyOverlap": 91.3, + "client": "web" + } +} diff --git a/packages/typing-fixtures/reference/keystrokes/quote-short.json b/packages/typing-fixtures/reference/keystrokes/quote-short.json new file mode 100644 index 000000000..723b4ddbd --- /dev/null +++ b/packages/typing-fixtures/reference/keystrokes/quote-short.json @@ -0,0 +1,2523 @@ +{ + "name": "quote-short", + "description": "short quote with one typo", + "recordedWith": "web", + "config": { + "mode": "quote", + "time": 30, + "words": 50, + "language": "english", + "punctuation": false, + "numbers": false, + "lazyMode": false, + "funbox": [], + "difficulty": "normal", + "blindMode": false, + "stopOnError": "off", + "confidenceMode": "off", + "freedomMode": false, + "strictSpace": false, + "quickEnd": false, + "deleteOnError": "off", + "oppositeShiftMode": "off", + "britishEnglish": false, + "quoteLength": [0] + }, + "keystrokes": [ + { + "kind": "key", + "key": "x", + "gap": 153, + "hold": 40 + }, + { + "kind": "key", + "key": "Backspace", + "gap": 170, + "hold": 62 + }, + { + "kind": "key", + "key": "C", + "gap": 155, + "hold": 48 + }, + { + "kind": "key", + "key": "o", + "gap": 91, + "hold": 141 + }, + { + "kind": "key", + "key": "u", + "gap": 206, + "hold": 63 + }, + { + "kind": "key", + "key": "r", + "gap": 202, + "hold": 82 + }, + { + "kind": "key", + "key": "a", + "gap": 93, + "hold": 46 + }, + { + "kind": "key", + "key": "g", + "gap": 144, + "hold": 126 + }, + { + "kind": "key", + "key": "e", + "gap": 166, + "hold": 93 + }, + { + "kind": "key", + "key": " ", + "gap": 139, + "hold": 62 + }, + { + "kind": "key", + "key": "i", + "gap": 209, + "hold": 144 + }, + { + "kind": "key", + "key": "s", + "gap": 122, + "hold": 144 + }, + { + "kind": "key", + "key": " ", + "gap": 192, + "hold": 148 + }, + { + "kind": "key", + "key": "r", + "gap": 174, + "hold": 144 + }, + { + "kind": "key", + "key": "e", + "gap": 205, + "hold": 144 + }, + { + "kind": "key", + "key": "s", + "gap": 116, + "hold": 101 + }, + { + "kind": "key", + "key": "i", + "gap": 115, + "hold": 72 + }, + { + "kind": "key", + "key": "s", + "gap": 116, + "hold": 131 + }, + { + "kind": "key", + "key": "t", + "gap": 139, + "hold": 104 + }, + { + "kind": "key", + "key": "a", + "gap": 159, + "hold": 137 + }, + { + "kind": "key", + "key": "n", + "gap": 159, + "hold": 136 + }, + { + "kind": "key", + "key": "c", + "gap": 176, + "hold": 103 + }, + { + "kind": "key", + "key": "e", + "gap": 123, + "hold": 134 + }, + { + "kind": "key", + "key": " ", + "gap": 187, + "hold": 62 + }, + { + "kind": "key", + "key": "t", + "gap": 140, + "hold": 80 + }, + { + "kind": "key", + "key": "o", + "gap": 169, + "hold": 42 + }, + { + "kind": "key", + "key": " ", + "gap": 130, + "hold": 72 + }, + { + "kind": "key", + "key": "f", + "gap": 121, + "hold": 73 + }, + { + "kind": "key", + "key": "e", + "gap": 122, + "hold": 81 + }, + { + "kind": "key", + "key": "a", + "gap": 101, + "hold": 80 + }, + { + "kind": "key", + "key": "r", + "gap": 100, + "hold": 142 + }, + { + "kind": "key", + "key": ",", + "gap": 150, + "hold": 53 + }, + { + "kind": "key", + "key": " ", + "gap": 149, + "hold": 92 + }, + { + "kind": "key", + "key": "m", + "gap": 97, + "hold": 89 + }, + { + "kind": "key", + "key": "a", + "gap": 170, + "hold": 72 + }, + { + "kind": "key", + "key": "s", + "gap": 125, + "hold": 142 + }, + { + "kind": "key", + "key": "t", + "gap": 104, + "hold": 58 + }, + { + "kind": "key", + "key": "e", + "gap": 160, + "hold": 116 + }, + { + "kind": "key", + "key": "r", + "gap": 178, + "hold": 139 + }, + { + "kind": "key", + "key": "y", + "gap": 109, + "hold": 105 + }, + { + "kind": "key", + "key": " ", + "gap": 141, + "hold": 120 + }, + { + "kind": "key", + "key": "o", + "gap": 108, + "hold": 61 + }, + { + "kind": "key", + "key": "f", + "gap": 191, + "hold": 49 + }, + { + "kind": "key", + "key": " ", + "gap": 102, + "hold": 104 + }, + { + "kind": "key", + "key": "f", + "gap": 126, + "hold": 121 + }, + { + "kind": "key", + "key": "e", + "gap": 150, + "hold": 137 + }, + { + "kind": "key", + "key": "a", + "gap": 183, + "hold": 61 + }, + { + "kind": "key", + "key": "r", + "gap": 123, + "hold": 98 + }, + { + "kind": "key", + "key": " ", + "gap": 130, + "hold": 99 + }, + { + "kind": "key", + "key": "-", + "gap": 109, + "hold": 53 + }, + { + "kind": "key", + "key": " ", + "gap": 113, + "hold": 85 + }, + { + "kind": "key", + "key": "n", + "gap": 107, + "hold": 122 + }, + { + "kind": "key", + "key": "o", + "gap": 177, + "hold": 41 + }, + { + "kind": "key", + "key": "t", + "gap": 165, + "hold": 46 + }, + { + "kind": "key", + "key": " ", + "gap": 198, + "hold": 86 + }, + { + "kind": "key", + "key": "a", + "gap": 189, + "hold": 131 + }, + { + "kind": "key", + "key": "b", + "gap": 202, + "hold": 83 + }, + { + "kind": "key", + "key": "s", + "gap": 174, + "hold": 42 + }, + { + "kind": "key", + "key": "e", + "gap": 137, + "hold": 138 + }, + { + "kind": "key", + "key": "n", + "gap": 115, + "hold": 116 + }, + { + "kind": "key", + "key": "c", + "gap": 101, + "hold": 82 + }, + { + "kind": "key", + "key": "e", + "gap": 100, + "hold": 61 + }, + { + "kind": "key", + "key": " ", + "gap": 183, + "hold": 105 + }, + { + "kind": "key", + "key": "o", + "gap": 150, + "hold": 70 + }, + { + "kind": "key", + "key": "f", + "gap": 205, + "hold": 142 + }, + { + "kind": "key", + "key": " ", + "gap": 155, + "hold": 106 + }, + { + "kind": "key", + "key": "f", + "gap": 181, + "hold": 72 + }, + { + "kind": "key", + "key": "e", + "gap": 121, + "hold": 83 + }, + { + "kind": "key", + "key": "a", + "gap": 155, + "hold": 119 + }, + { + "kind": "key", + "key": "r", + "gap": 192, + "hold": 52 + }, + { + "kind": "key", + "key": ".", + "gap": 120, + "hold": 135 + } + ], + "eventLog": { + "version": 1, + "events": [ + { + "type": "keydown", + "testMs": -5.8, + "data": { + "code": "KeyX" + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": "x", + "correct": false, + "wordIndex": 0, + "charIndex": 0, + "inputValue": "x" + } + }, + { + "type": "timer", + "testMs": 0, + "data": { + "event": "start", + "timer": 0, + "date": 1791473744269 + } + }, + { + "type": "keyup", + "testMs": 48.7, + "data": { + "code": "KeyX" + } + }, + { + "type": "input", + "testMs": 164.4, + "data": { + "inputType": "deleteContentBackward", + "wordIndex": 0, + "charIndex": 1, + "inputValue": "" + } + }, + { + "type": "keydown", + "testMs": 317.6, + "data": { + "code": "KeyC" + } + }, + { + "type": "input", + "testMs": 319.1, + "data": { + "inputType": "insertText", + "data": "C", + "correct": true, + "wordIndex": 0, + "charIndex": 0, + "inputValue": "C" + } + }, + { + "type": "keyup", + "testMs": 366.1, + "data": { + "code": "KeyC" + } + }, + { + "type": "keydown", + "testMs": 409.2, + "data": { + "code": "KeyO" + } + }, + { + "type": "input", + "testMs": 411.7, + "data": { + "inputType": "insertText", + "data": "o", + "correct": true, + "wordIndex": 0, + "charIndex": 1, + "inputValue": "Co" + } + }, + { + "type": "keyup", + "testMs": 550.7, + "data": { + "code": "KeyO" + } + }, + { + "type": "keydown", + "testMs": 613.8, + "data": { + "code": "KeyU" + } + }, + { + "type": "input", + "testMs": 615.1, + "data": { + "inputType": "insertText", + "data": "u", + "correct": true, + "wordIndex": 0, + "charIndex": 2, + "inputValue": "Cou" + } + }, + { + "type": "keyup", + "testMs": 677, + "data": { + "code": "KeyU" + } + }, + { + "type": "keydown", + "testMs": 817.7, + "data": { + "code": "KeyR" + } + }, + { + "type": "input", + "testMs": 820.2, + "data": { + "inputType": "insertText", + "data": "r", + "correct": true, + "wordIndex": 0, + "charIndex": 3, + "inputValue": "Cour" + } + }, + { + "type": "keyup", + "testMs": 898.7, + "data": { + "code": "KeyR" + } + }, + { + "type": "keydown", + "testMs": 909, + "data": { + "code": "KeyA" + } + }, + { + "type": "input", + "testMs": 910.2, + "data": { + "inputType": "insertText", + "data": "a", + "correct": true, + "wordIndex": 0, + "charIndex": 4, + "inputValue": "Coura" + } + }, + { + "type": "keyup", + "testMs": 955.6, + "data": { + "code": "KeyA" + } + }, + { + "type": "timer", + "testMs": 1006.1, + "data": { + "event": "step", + "timer": 1, + "drift": 6.1 + } + }, + { + "type": "keydown", + "testMs": 1054.9, + "data": { + "code": "KeyG" + } + }, + { + "type": "input", + "testMs": 1057.4, + "data": { + "inputType": "insertText", + "data": "g", + "correct": true, + "wordIndex": 0, + "charIndex": 5, + "inputValue": "Courag" + } + }, + { + "type": "keyup", + "testMs": 1180.7, + "data": { + "code": "KeyG" + } + }, + { + "type": "keydown", + "testMs": 1220.3, + "data": { + "code": "KeyE" + } + }, + { + "type": "input", + "testMs": 1222.6, + "data": { + "inputType": "insertText", + "data": "e", + "correct": true, + "wordIndex": 0, + "charIndex": 6, + "inputValue": "Courage" + } + }, + { + "type": "keyup", + "testMs": 1313.2, + "data": { + "code": "KeyE" + } + }, + { + "type": "keydown", + "testMs": 1357.4, + "data": { + "code": "Space" + } + }, + { + "type": "input", + "testMs": 1358.9, + "data": { + "inputType": "insertText", + "data": " ", + "correct": true, + "wordIndex": 0, + "charIndex": 7, + "inputValue": "Courage ", + "commitsWord": true + } + }, + { + "type": "keyup", + "testMs": 1421.5, + "data": { + "code": "Space" + } + }, + { + "type": "keydown", + "testMs": 1639.4, + "data": { + "code": "KeyI" + } + }, + { + "type": "input", + "testMs": 1640, + "data": { + "inputType": "insertText", + "data": "i", + "correct": true, + "wordIndex": 1, + "charIndex": 0, + "inputValue": "i" + } + }, + { + "type": "keydown", + "testMs": 1759.3, + "data": { + "code": "KeyS" + } + }, + { + "type": "input", + "testMs": 1759.8, + "data": { + "inputType": "insertText", + "data": "s", + "correct": true, + "wordIndex": 1, + "charIndex": 1, + "inputValue": "is" + } + }, + { + "type": "keyup", + "testMs": 1782.7, + "data": { + "code": "KeyI" + } + }, + { + "type": "keyup", + "testMs": 1904.2, + "data": { + "code": "KeyS" + } + }, + { + "type": "keydown", + "testMs": 1952.6, + "data": { + "code": "Space" + } + }, + { + "type": "input", + "testMs": 1953.6, + "data": { + "inputType": "insertText", + "data": " ", + "correct": true, + "wordIndex": 1, + "charIndex": 2, + "inputValue": "is ", + "commitsWord": true + } + }, + { + "type": "timer", + "testMs": 2006.1, + "data": { + "event": "step", + "timer": 2, + "drift": 6.1 + } + }, + { + "type": "keyup", + "testMs": 2103.1, + "data": { + "code": "Space" + } + }, + { + "type": "keydown", + "testMs": 2288.1, + "data": { + "code": "KeyR" + } + }, + { + "type": "input", + "testMs": 2289.2, + "data": { + "inputType": "insertText", + "data": "r", + "correct": true, + "wordIndex": 2, + "charIndex": 0, + "inputValue": "r" + } + }, + { + "type": "keyup", + "testMs": 2432.3, + "data": { + "code": "KeyR" + } + }, + { + "type": "keydown", + "testMs": 2493.2, + "data": { + "code": "KeyE" + } + }, + { + "type": "input", + "testMs": 2494.1, + "data": { + "inputType": "insertText", + "data": "e", + "correct": true, + "wordIndex": 2, + "charIndex": 1, + "inputValue": "re" + } + }, + { + "type": "keydown", + "testMs": 2608.6, + "data": { + "code": "KeyS" + } + }, + { + "type": "input", + "testMs": 2609.5, + "data": { + "inputType": "insertText", + "data": "s", + "correct": true, + "wordIndex": 2, + "charIndex": 2, + "inputValue": "res" + } + }, + { + "type": "keyup", + "testMs": 2636.9, + "data": { + "code": "KeyE" + } + }, + { + "type": "keyup", + "testMs": 2710, + "data": { + "code": "KeyS" + } + }, + { + "type": "keydown", + "testMs": 2724, + "data": { + "code": "KeyI" + } + }, + { + "type": "input", + "testMs": 2725.1, + "data": { + "inputType": "insertText", + "data": "i", + "correct": true, + "wordIndex": 2, + "charIndex": 3, + "inputValue": "resi" + } + }, + { + "type": "keyup", + "testMs": 2795.9, + "data": { + "code": "KeyI" + } + }, + { + "type": "keydown", + "testMs": 2840.2, + "data": { + "code": "KeyS" + } + }, + { + "type": "input", + "testMs": 2841.3, + "data": { + "inputType": "insertText", + "data": "s", + "correct": true, + "wordIndex": 2, + "charIndex": 4, + "inputValue": "resis" + } + }, + { + "type": "keyup", + "testMs": 2972.6, + "data": { + "code": "KeyS" + } + }, + { + "type": "keydown", + "testMs": 2977.9, + "data": { + "code": "KeyT" + } + }, + { + "type": "input", + "testMs": 2979.1, + "data": { + "inputType": "insertText", + "data": "t", + "correct": true, + "wordIndex": 2, + "charIndex": 5, + "inputValue": "resist" + } + }, + { + "type": "timer", + "testMs": 3005.2, + "data": { + "event": "step", + "timer": 3, + "drift": 5.2 + } + }, + { + "type": "keyup", + "testMs": 3083, + "data": { + "code": "KeyT" + } + }, + { + "type": "keydown", + "testMs": 3136.5, + "data": { + "code": "KeyA" + } + }, + { + "type": "input", + "testMs": 3137, + "data": { + "inputType": "insertText", + "data": "a", + "correct": true, + "wordIndex": 2, + "charIndex": 6, + "inputValue": "resista" + } + }, + { + "type": "keyup", + "testMs": 3273.4, + "data": { + "code": "KeyA" + } + }, + { + "type": "keydown", + "testMs": 3296.6, + "data": { + "code": "KeyN" + } + }, + { + "type": "input", + "testMs": 3297.3, + "data": { + "inputType": "insertText", + "data": "n", + "correct": true, + "wordIndex": 2, + "charIndex": 7, + "inputValue": "resistan" + } + }, + { + "type": "keyup", + "testMs": 3432.9, + "data": { + "code": "KeyN" + } + }, + { + "type": "keydown", + "testMs": 3473, + "data": { + "code": "KeyC" + } + }, + { + "type": "input", + "testMs": 3473.6, + "data": { + "inputType": "insertText", + "data": "c", + "correct": true, + "wordIndex": 2, + "charIndex": 8, + "inputValue": "resistanc" + } + }, + { + "type": "keyup", + "testMs": 3575.9, + "data": { + "code": "KeyC" + } + }, + { + "type": "keydown", + "testMs": 3595.1, + "data": { + "code": "KeyE" + } + }, + { + "type": "input", + "testMs": 3595.9, + "data": { + "inputType": "insertText", + "data": "e", + "correct": true, + "wordIndex": 2, + "charIndex": 9, + "inputValue": "resistance" + } + }, + { + "type": "keyup", + "testMs": 3730.3, + "data": { + "code": "KeyE" + } + }, + { + "type": "keydown", + "testMs": 3784.3, + "data": { + "code": "Space" + } + }, + { + "type": "input", + "testMs": 3786.4, + "data": { + "inputType": "insertText", + "data": " ", + "correct": true, + "wordIndex": 2, + "charIndex": 10, + "inputValue": "resistance ", + "commitsWord": true + } + }, + { + "type": "keyup", + "testMs": 3846.3, + "data": { + "code": "Space" + } + }, + { + "type": "keydown", + "testMs": 3993.7, + "data": { + "code": "KeyT" + } + }, + { + "type": "input", + "testMs": 3996.1, + "data": { + "inputType": "insertText", + "data": "t", + "correct": true, + "wordIndex": 3, + "charIndex": 0, + "inputValue": "t" + } + }, + { + "type": "timer", + "testMs": 4005.7, + "data": { + "event": "step", + "timer": 4, + "drift": 5.7 + } + }, + { + "type": "keyup", + "testMs": 4076.7, + "data": { + "code": "KeyT" + } + }, + { + "type": "keydown", + "testMs": 4164.3, + "data": { + "code": "KeyO" + } + }, + { + "type": "input", + "testMs": 4165.7, + "data": { + "inputType": "insertText", + "data": "o", + "correct": true, + "wordIndex": 3, + "charIndex": 1, + "inputValue": "to" + } + }, + { + "type": "keyup", + "testMs": 4208.3, + "data": { + "code": "KeyO" + } + }, + { + "type": "keydown", + "testMs": 4294.3, + "data": { + "code": "Space" + } + }, + { + "type": "input", + "testMs": 4295.7, + "data": { + "inputType": "insertText", + "data": " ", + "correct": true, + "wordIndex": 3, + "charIndex": 2, + "inputValue": "to ", + "commitsWord": true + } + }, + { + "type": "keyup", + "testMs": 4367.2, + "data": { + "code": "Space" + } + }, + { + "type": "keydown", + "testMs": 4498.8, + "data": { + "code": "KeyF" + } + }, + { + "type": "input", + "testMs": 4501, + "data": { + "inputType": "insertText", + "data": "f", + "correct": true, + "wordIndex": 4, + "charIndex": 0, + "inputValue": "f" + } + }, + { + "type": "keyup", + "testMs": 4571.7, + "data": { + "code": "KeyF" + } + }, + { + "type": "keydown", + "testMs": 4620.7, + "data": { + "code": "KeyE" + } + }, + { + "type": "input", + "testMs": 4623, + "data": { + "inputType": "insertText", + "data": "e", + "correct": true, + "wordIndex": 4, + "charIndex": 1, + "inputValue": "fe" + } + }, + { + "type": "keyup", + "testMs": 4700.2, + "data": { + "code": "KeyE" + } + }, + { + "type": "keydown", + "testMs": 4721.6, + "data": { + "code": "KeyA" + } + }, + { + "type": "input", + "testMs": 4723.6, + "data": { + "inputType": "insertText", + "data": "a", + "correct": true, + "wordIndex": 4, + "charIndex": 2, + "inputValue": "fea" + } + }, + { + "type": "keyup", + "testMs": 4800.5, + "data": { + "code": "KeyA" + } + }, + { + "type": "keydown", + "testMs": 4821, + "data": { + "code": "KeyR" + } + }, + { + "type": "input", + "testMs": 4821.9, + "data": { + "inputType": "insertText", + "data": "r", + "correct": true, + "wordIndex": 4, + "charIndex": 3, + "inputValue": "fear" + } + }, + { + "type": "keyup", + "testMs": 4963.9, + "data": { + "code": "KeyR" + } + }, + { + "type": "keydown", + "testMs": 4969.4, + "data": { + "code": "Comma" + } + }, + { + "type": "input", + "testMs": 4971.2, + "data": { + "inputType": "insertText", + "data": ",", + "correct": true, + "wordIndex": 4, + "charIndex": 4, + "inputValue": "fear," + } + }, + { + "type": "timer", + "testMs": 5005.4, + "data": { + "event": "step", + "timer": 5, + "drift": 5.4 + } + }, + { + "type": "keyup", + "testMs": 5022.9, + "data": { + "code": "Comma" + } + }, + { + "type": "keydown", + "testMs": 5120.8, + "data": { + "code": "Space" + } + }, + { + "type": "input", + "testMs": 5123, + "data": { + "inputType": "insertText", + "data": " ", + "correct": true, + "wordIndex": 4, + "charIndex": 5, + "inputValue": "fear, ", + "commitsWord": true + } + }, + { + "type": "keyup", + "testMs": 5211.8, + "data": { + "code": "Space" + } + }, + { + "type": "keydown", + "testMs": 5317.7, + "data": { + "code": "KeyM" + } + }, + { + "type": "input", + "testMs": 5319.7, + "data": { + "inputType": "insertText", + "data": "m", + "correct": true, + "wordIndex": 5, + "charIndex": 0, + "inputValue": "m" + } + }, + { + "type": "keyup", + "testMs": 5406.9, + "data": { + "code": "KeyM" + } + }, + { + "type": "keydown", + "testMs": 5487.1, + "data": { + "code": "KeyA" + } + }, + { + "type": "input", + "testMs": 5489.6, + "data": { + "inputType": "insertText", + "data": "a", + "correct": true, + "wordIndex": 5, + "charIndex": 1, + "inputValue": "ma" + } + }, + { + "type": "keyup", + "testMs": 5559.4, + "data": { + "code": "KeyA" + } + }, + { + "type": "keydown", + "testMs": 5612.1, + "data": { + "code": "KeyS" + } + }, + { + "type": "input", + "testMs": 5613.9, + "data": { + "inputType": "insertText", + "data": "s", + "correct": true, + "wordIndex": 5, + "charIndex": 2, + "inputValue": "mas" + } + }, + { + "type": "keydown", + "testMs": 5716.5, + "data": { + "code": "KeyT" + } + }, + { + "type": "input", + "testMs": 5718.3, + "data": { + "inputType": "insertText", + "data": "t", + "correct": true, + "wordIndex": 5, + "charIndex": 3, + "inputValue": "mast" + } + }, + { + "type": "keyup", + "testMs": 5753.6, + "data": { + "code": "KeyS" + } + }, + { + "type": "keyup", + "testMs": 5774.8, + "data": { + "code": "KeyT" + } + }, + { + "type": "keydown", + "testMs": 5877.2, + "data": { + "code": "KeyE" + } + }, + { + "type": "input", + "testMs": 5879.4, + "data": { + "inputType": "insertText", + "data": "e", + "correct": true, + "wordIndex": 5, + "charIndex": 4, + "inputValue": "maste" + } + }, + { + "type": "keyup", + "testMs": 5991.2, + "data": { + "code": "KeyE" + } + }, + { + "type": "timer", + "testMs": 6005.9, + "data": { + "event": "step", + "timer": 6, + "drift": 5.9 + } + }, + { + "type": "keydown", + "testMs": 6052.7, + "data": { + "code": "KeyR" + } + }, + { + "type": "input", + "testMs": 6053.4, + "data": { + "inputType": "insertText", + "data": "r", + "correct": true, + "wordIndex": 5, + "charIndex": 5, + "inputValue": "master" + } + }, + { + "type": "keydown", + "testMs": 6161.6, + "data": { + "code": "KeyY" + } + }, + { + "type": "input", + "testMs": 6162.2, + "data": { + "inputType": "insertText", + "data": "y", + "correct": true, + "wordIndex": 5, + "charIndex": 6, + "inputValue": "mastery" + } + }, + { + "type": "keyup", + "testMs": 6191.9, + "data": { + "code": "KeyR" + } + }, + { + "type": "keyup", + "testMs": 6267.5, + "data": { + "code": "KeyY" + } + }, + { + "type": "keydown", + "testMs": 6302.8, + "data": { + "code": "Space" + } + }, + { + "type": "input", + "testMs": 6304, + "data": { + "inputType": "insertText", + "data": " ", + "correct": true, + "wordIndex": 5, + "charIndex": 7, + "inputValue": "mastery ", + "commitsWord": true + } + }, + { + "type": "keyup", + "testMs": 6425.2, + "data": { + "code": "Space" + } + }, + { + "type": "keydown", + "testMs": 6543.2, + "data": { + "code": "KeyO" + } + }, + { + "type": "input", + "testMs": 6545.3, + "data": { + "inputType": "insertText", + "data": "o", + "correct": true, + "wordIndex": 6, + "charIndex": 0, + "inputValue": "o" + } + }, + { + "type": "keyup", + "testMs": 6604.7, + "data": { + "code": "KeyO" + } + }, + { + "type": "keydown", + "testMs": 6735.5, + "data": { + "code": "KeyF" + } + }, + { + "type": "input", + "testMs": 6737.9, + "data": { + "inputType": "insertText", + "data": "f", + "correct": true, + "wordIndex": 6, + "charIndex": 1, + "inputValue": "of" + } + }, + { + "type": "keyup", + "testMs": 6780.7, + "data": { + "code": "KeyF" + } + }, + { + "type": "keydown", + "testMs": 6836.4, + "data": { + "code": "Space" + } + }, + { + "type": "input", + "testMs": 6838.5, + "data": { + "inputType": "insertText", + "data": " ", + "correct": true, + "wordIndex": 6, + "charIndex": 2, + "inputValue": "of ", + "commitsWord": true + } + }, + { + "type": "keyup", + "testMs": 6940.2, + "data": { + "code": "Space" + } + }, + { + "type": "timer", + "testMs": 7005.7, + "data": { + "event": "step", + "timer": 7, + "drift": 5.7 + } + }, + { + "type": "keydown", + "testMs": 7075.1, + "data": { + "code": "KeyF" + } + }, + { + "type": "input", + "testMs": 7077.4, + "data": { + "inputType": "insertText", + "data": "f", + "correct": true, + "wordIndex": 7, + "charIndex": 0, + "inputValue": "f" + } + }, + { + "type": "keyup", + "testMs": 7195.3, + "data": { + "code": "KeyF" + } + }, + { + "type": "keydown", + "testMs": 7225.1, + "data": { + "code": "KeyE" + } + }, + { + "type": "input", + "testMs": 7227.1, + "data": { + "inputType": "insertText", + "data": "e", + "correct": true, + "wordIndex": 7, + "charIndex": 1, + "inputValue": "fe" + } + }, + { + "type": "keyup", + "testMs": 7361.9, + "data": { + "code": "KeyE" + } + }, + { + "type": "keydown", + "testMs": 7408.9, + "data": { + "code": "KeyA" + } + }, + { + "type": "input", + "testMs": 7411.1, + "data": { + "inputType": "insertText", + "data": "a", + "correct": true, + "wordIndex": 7, + "charIndex": 2, + "inputValue": "fea" + } + }, + { + "type": "keyup", + "testMs": 7468.9, + "data": { + "code": "KeyA" + } + }, + { + "type": "keydown", + "testMs": 7530.8, + "data": { + "code": "KeyR" + } + }, + { + "type": "input", + "testMs": 7532.9, + "data": { + "inputType": "insertText", + "data": "r", + "correct": true, + "wordIndex": 7, + "charIndex": 3, + "inputValue": "fear" + } + }, + { + "type": "keyup", + "testMs": 7629.1, + "data": { + "code": "KeyR" + } + }, + { + "type": "keydown", + "testMs": 7660.6, + "data": { + "code": "Space" + } + }, + { + "type": "input", + "testMs": 7662.6, + "data": { + "inputType": "insertText", + "data": " ", + "correct": true, + "wordIndex": 7, + "charIndex": 4, + "inputValue": "fear ", + "commitsWord": true + } + }, + { + "type": "keyup", + "testMs": 7760, + "data": { + "code": "Space" + } + }, + { + "type": "keydown", + "testMs": 7879.7, + "data": { + "code": "Minus" + } + }, + { + "type": "input", + "testMs": 7882.1, + "data": { + "inputType": "insertText", + "data": "-", + "correct": true, + "wordIndex": 8, + "charIndex": 0, + "inputValue": "-" + } + }, + { + "type": "keyup", + "testMs": 7932.4, + "data": { + "code": "Minus" + } + }, + { + "type": "keydown", + "testMs": 7992.4, + "data": { + "code": "Space" + } + }, + { + "type": "input", + "testMs": 7994.4, + "data": { + "inputType": "insertText", + "data": " ", + "correct": true, + "wordIndex": 8, + "charIndex": 1, + "inputValue": "- ", + "commitsWord": true + } + }, + { + "type": "timer", + "testMs": 8006.4, + "data": { + "event": "step", + "timer": 8, + "drift": 6.4 + } + }, + { + "type": "keyup", + "testMs": 8077.2, + "data": { + "code": "Space" + } + }, + { + "type": "keydown", + "testMs": 8193.6, + "data": { + "code": "KeyN" + } + }, + { + "type": "input", + "testMs": 8196, + "data": { + "inputType": "insertText", + "data": "n", + "correct": true, + "wordIndex": 9, + "charIndex": 0, + "inputValue": "n" + } + }, + { + "type": "keyup", + "testMs": 8315, + "data": { + "code": "KeyN" + } + }, + { + "type": "keydown", + "testMs": 8371.2, + "data": { + "code": "KeyO" + } + }, + { + "type": "input", + "testMs": 8373.1, + "data": { + "inputType": "insertText", + "data": "o", + "correct": true, + "wordIndex": 9, + "charIndex": 1, + "inputValue": "no" + } + }, + { + "type": "keyup", + "testMs": 8410.8, + "data": { + "code": "KeyO" + } + }, + { + "type": "keydown", + "testMs": 8535.8, + "data": { + "code": "KeyT" + } + }, + { + "type": "input", + "testMs": 8538.3, + "data": { + "inputType": "insertText", + "data": "t", + "correct": true, + "wordIndex": 9, + "charIndex": 2, + "inputValue": "not" + } + }, + { + "type": "keyup", + "testMs": 8582.1, + "data": { + "code": "KeyT" + } + }, + { + "type": "keydown", + "testMs": 8734.5, + "data": { + "code": "Space" + } + }, + { + "type": "input", + "testMs": 8736.8, + "data": { + "inputType": "insertText", + "data": " ", + "correct": true, + "wordIndex": 9, + "charIndex": 3, + "inputValue": "not ", + "commitsWord": true + } + }, + { + "type": "keyup", + "testMs": 8818.7, + "data": { + "code": "Space" + } + }, + { + "type": "timer", + "testMs": 9006.6, + "data": { + "event": "step", + "timer": 9, + "drift": 6.6 + } + }, + { + "type": "keydown", + "testMs": 9019, + "data": { + "code": "KeyA" + } + }, + { + "type": "input", + "testMs": 9021.5, + "data": { + "inputType": "insertText", + "data": "a", + "correct": true, + "wordIndex": 10, + "charIndex": 0, + "inputValue": "a" + } + }, + { + "type": "keyup", + "testMs": 9149, + "data": { + "code": "KeyA" + } + }, + { + "type": "keydown", + "testMs": 9221.8, + "data": { + "code": "KeyB" + } + }, + { + "type": "input", + "testMs": 9224.5, + "data": { + "inputType": "insertText", + "data": "b", + "correct": true, + "wordIndex": 10, + "charIndex": 1, + "inputValue": "ab" + } + }, + { + "type": "keyup", + "testMs": 9303.8, + "data": { + "code": "KeyB" + } + }, + { + "type": "keydown", + "testMs": 9395.5, + "data": { + "code": "KeyS" + } + }, + { + "type": "input", + "testMs": 9397.8, + "data": { + "inputType": "insertText", + "data": "s", + "correct": true, + "wordIndex": 10, + "charIndex": 2, + "inputValue": "abs" + } + }, + { + "type": "keyup", + "testMs": 9436, + "data": { + "code": "KeyS" + } + }, + { + "type": "keydown", + "testMs": 9531.8, + "data": { + "code": "KeyE" + } + }, + { + "type": "input", + "testMs": 9534, + "data": { + "inputType": "insertText", + "data": "e", + "correct": true, + "wordIndex": 10, + "charIndex": 3, + "inputValue": "abse" + } + }, + { + "type": "keydown", + "testMs": 9646.1, + "data": { + "code": "KeyN" + } + }, + { + "type": "input", + "testMs": 9647.8, + "data": { + "inputType": "insertText", + "data": "n", + "correct": true, + "wordIndex": 10, + "charIndex": 4, + "inputValue": "absen" + } + }, + { + "type": "keyup", + "testMs": 9668.2, + "data": { + "code": "KeyE" + } + }, + { + "type": "keydown", + "testMs": 9747.5, + "data": { + "code": "KeyC" + } + }, + { + "type": "input", + "testMs": 9749.1, + "data": { + "inputType": "insertText", + "data": "c", + "correct": true, + "wordIndex": 10, + "charIndex": 5, + "inputValue": "absenc" + } + }, + { + "type": "keyup", + "testMs": 9758.4, + "data": { + "code": "KeyN" + } + }, + { + "type": "keyup", + "testMs": 9828.3, + "data": { + "code": "KeyC" + } + }, + { + "type": "keydown", + "testMs": 9846.5, + "data": { + "code": "KeyE" + } + }, + { + "type": "input", + "testMs": 9847.6, + "data": { + "inputType": "insertText", + "data": "e", + "correct": true, + "wordIndex": 10, + "charIndex": 6, + "inputValue": "absence" + } + }, + { + "type": "keyup", + "testMs": 9907.5, + "data": { + "code": "KeyE" + } + }, + { + "type": "timer", + "testMs": 10008.5, + "data": { + "event": "step", + "timer": 10, + "drift": 8.5 + } + }, + { + "type": "keydown", + "testMs": 10030.5, + "data": { + "code": "Space" + } + }, + { + "type": "input", + "testMs": 10032.7, + "data": { + "inputType": "insertText", + "data": " ", + "correct": true, + "wordIndex": 10, + "charIndex": 7, + "inputValue": "absence ", + "commitsWord": true + } + }, + { + "type": "keyup", + "testMs": 10134.8, + "data": { + "code": "Space" + } + }, + { + "type": "keydown", + "testMs": 10294.3, + "data": { + "code": "KeyO" + } + }, + { + "type": "input", + "testMs": 10295.7, + "data": { + "inputType": "insertText", + "data": "o", + "correct": true, + "wordIndex": 11, + "charIndex": 0, + "inputValue": "o" + } + }, + { + "type": "keyup", + "testMs": 10364.4, + "data": { + "code": "KeyO" + } + }, + { + "type": "keydown", + "testMs": 10501.7, + "data": { + "code": "KeyF" + } + }, + { + "type": "input", + "testMs": 10504, + "data": { + "inputType": "insertText", + "data": "f", + "correct": true, + "wordIndex": 11, + "charIndex": 1, + "inputValue": "of" + } + }, + { + "type": "keyup", + "testMs": 10643.3, + "data": { + "code": "KeyF" + } + }, + { + "type": "keydown", + "testMs": 10654, + "data": { + "code": "Space" + } + }, + { + "type": "input", + "testMs": 10656.2, + "data": { + "inputType": "insertText", + "data": " ", + "correct": true, + "wordIndex": 11, + "charIndex": 2, + "inputValue": "of ", + "commitsWord": true + } + }, + { + "type": "keyup", + "testMs": 10761.1, + "data": { + "code": "Space" + } + }, + { + "type": "keydown", + "testMs": 10951.6, + "data": { + "code": "KeyF" + } + }, + { + "type": "input", + "testMs": 10954.3, + "data": { + "inputType": "insertText", + "data": "f", + "correct": true, + "wordIndex": 12, + "charIndex": 0, + "inputValue": "f", + "lastWord": true + } + }, + { + "type": "timer", + "testMs": 11006.6, + "data": { + "event": "step", + "timer": 11, + "drift": 6.6 + } + }, + { + "type": "keyup", + "testMs": 11024.4, + "data": { + "code": "KeyF" + } + }, + { + "type": "keydown", + "testMs": 11071.4, + "data": { + "code": "KeyE" + } + }, + { + "type": "input", + "testMs": 11073.5, + "data": { + "inputType": "insertText", + "data": "e", + "correct": true, + "wordIndex": 12, + "charIndex": 1, + "inputValue": "fe", + "lastWord": true + } + }, + { + "type": "keyup", + "testMs": 11155.4, + "data": { + "code": "KeyE" + } + }, + { + "type": "keydown", + "testMs": 11227.4, + "data": { + "code": "KeyA" + } + }, + { + "type": "input", + "testMs": 11229.7, + "data": { + "inputType": "insertText", + "data": "a", + "correct": true, + "wordIndex": 12, + "charIndex": 2, + "inputValue": "fea", + "lastWord": true + } + }, + { + "type": "keyup", + "testMs": 11344.9, + "data": { + "code": "KeyA" + } + }, + { + "type": "keydown", + "testMs": 11416.7, + "data": { + "code": "KeyR" + } + }, + { + "type": "input", + "testMs": 11419, + "data": { + "inputType": "insertText", + "data": "r", + "correct": true, + "wordIndex": 12, + "charIndex": 3, + "inputValue": "fear", + "lastWord": true + } + }, + { + "type": "keyup", + "testMs": 11469.9, + "data": { + "code": "KeyR" + } + }, + { + "type": "keydown", + "testMs": 11538.1, + "data": { + "code": "Period" + } + }, + { + "type": "input", + "testMs": 11539.5, + "data": { + "inputType": "insertText", + "data": ".", + "correct": true, + "wordIndex": 12, + "charIndex": 4, + "inputValue": "fear.", + "lastWord": true + } + }, + { + "type": "timer", + "testMs": 11540.5, + "data": { + "event": "end", + "timer": 11, + "date": 1791473755808 + } + }, + { + "type": "keyup", + "testMs": 11616.45, + "data": { + "code": "Period", + "estimated": true + } + } + ], + "context": { + "targetWords": [ + "Courage ", + "is ", + "resistance ", + "to ", + "fear, ", + "mastery ", + "of ", + "fear ", + "- ", + "not ", + "absence ", + "of ", + "fear." + ], + "mode": "quote", + "mode2": "36", + "koreanStatus": false, + "bailedOut": false + } + }, + "completedEvent": { + "wpm": 71.75, + "rawWpm": 71.75, + "charStats": [69, 0, 0, 0], + "charTotal": 69, + "acc": 98.57, + "language": "english", + "testDuration": 11.54, + "lastKeyToEnd": 2.4, + "startToFirstKey": 0, + "afkDuration": 0, + "quoteLength": 0, + "tags": [], + "punctuation": false, + "numbers": false, + "lazyMode": false, + "timestamp": 1791473755938, + "mode": "quote", + "mode2": "36", + "bailedOut": false, + "funbox": [], + "difficulty": "normal", + "blindMode": false, + "stopOnLetter": false, + "restartCount": 0, + "incompleteTests": [], + "incompleteTestSeconds": 0, + "consistency": 85.2, + "wpmConsistency": 94.8, + "keyConsistency": 66.97, + "chartData": { + "wpm": [60, 66, 68, 69, 72, 72, 72, 74, 71, 72, 71, 72], + "burst": [72, 72, 72, 72, 84, 72, 72, 84, 48, 84, 60, 89], + "err": [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + }, + "keySpacing": [ + 317.6, 91.59999999999997, 204.59999999999997, 203.9000000000001, + 91.29999999999995, 145.9000000000001, 165.39999999999986, + 137.10000000000014, 282, 119.89999999999986, 193.29999999999995, 335.5, + 205.0999999999999, 115.40000000000009, 115.40000000000009, + 116.19999999999982, 137.70000000000027, 158.5999999999999, + 160.0999999999999, 176.4000000000001, 122.09999999999991, + 189.20000000000027, 209.39999999999964, 170.60000000000036, 130, 204.5, + 121.89999999999964, 100.90000000000055, 99.39999999999964, + 148.39999999999964, 151.40000000000055, 196.89999999999964, + 169.40000000000055, 125, 104.39999999999964, 160.69999999999982, 175.5, + 108.90000000000055, 141.19999999999982, 240.39999999999964, + 192.30000000000018, 100.89999999999964, 238.70000000000073, 150, + 183.79999999999927, 121.90000000000055, 129.80000000000018, + 219.09999999999945, 112.69999999999982, 201.20000000000073, + 177.60000000000036, 164.59999999999854, 198.70000000000073, 284.5, + 202.79999999999927, 173.70000000000073, 136.29999999999927, + 114.30000000000109, 101.39999999999964, 99, 184, 263.7999999999993, + 207.40000000000146, 152.29999999999927, 297.60000000000036, + 119.79999999999927, 156, 189.3000000000011, 121.39999999999964 + ], + "keyDuration": [ + 54.5, 48.5, 141.50000000000006, 63.200000000000045, 81, 46.60000000000002, + 125.79999999999995, 92.90000000000009, 64.09999999999991, + 143.29999999999995, 144.9000000000001, 150.5, 144.20000000000027, + 143.70000000000027, 101.40000000000009, 71.90000000000009, + 132.4000000000001, 105.09999999999991, 136.9000000000001, + 136.30000000000018, 102.90000000000009, 135.20000000000027, 62, 83, 44, + 72.89999999999964, 72.89999999999964, 79.5, 78.89999999999964, + 142.89999999999964, 53.5, 91, 89.19999999999982, 72.29999999999927, 141.5, + 58.30000000000018, 114, 139.19999999999982, 105.89999999999964, + 122.39999999999964, 61.5, 45.19999999999982, 103.80000000000018, + 120.19999999999982, 136.79999999999927, 60, 98.30000000000018, + 99.39999999999964, 52.69999999999982, 84.80000000000018, + 121.39999999999964, 39.599999999998545, 46.30000000000109, + 84.20000000000073, 130, 82, 40.5, 136.40000000000146, 112.29999999999927, + 80.79999999999927, 61, 104.29999999999927, 70.10000000000036, + 141.59999999999854, 107.10000000000036, 72.79999999999927, 84, 117.5, + 53.19999999999891, 78.35000000000036 + ], + "keyOverlap": 152.1, + "client": "web" + } +} diff --git a/packages/typing-fixtures/reference/keystrokes/time-15.json b/packages/typing-fixtures/reference/keystrokes/time-15.json new file mode 100644 index 000000000..762e678bc --- /dev/null +++ b/packages/typing-fixtures/reference/keystrokes/time-15.json @@ -0,0 +1,2856 @@ +{ + "name": "time-15", + "description": "time 15 with a pause, ends mid word", + "recordedWith": "web", + "config": { + "mode": "time", + "time": 15, + "words": 50, + "language": "english", + "punctuation": false, + "numbers": false, + "lazyMode": false, + "funbox": [], + "difficulty": "normal", + "blindMode": false, + "stopOnError": "off", + "confidenceMode": "off", + "freedomMode": false, + "strictSpace": false, + "quickEnd": false, + "deleteOnError": "off", + "oppositeShiftMode": "off", + "britishEnglish": false, + "quoteLength": [1] + }, + "keystrokes": [ + { + "kind": "key", + "key": "i", + "gap": 176, + "hold": 44 + }, + { + "kind": "key", + "key": "n", + "gap": 145, + "hold": 48 + }, + { + "kind": "key", + "key": "t", + "gap": 182, + "hold": 93 + }, + { + "kind": "key", + "key": "o", + "gap": 148, + "hold": 58 + }, + { + "kind": "key", + "key": " ", + "gap": 122, + "hold": 101 + }, + { + "kind": "key", + "key": "o", + "gap": 113, + "hold": 49 + }, + { + "kind": "key", + "key": "v", + "gap": 176, + "hold": 92 + }, + { + "kind": "key", + "key": "e", + "gap": 185, + "hold": 137 + }, + { + "kind": "key", + "key": "r", + "gap": 129, + "hold": 116 + }, + { + "kind": "key", + "key": " ", + "gap": 124, + "hold": 114 + }, + { + "kind": "key", + "key": "h", + "gap": 153, + "hold": 131 + }, + { + "kind": "key", + "key": "a", + "gap": 155, + "hold": 41 + }, + { + "kind": "key", + "key": "v", + "gap": 195, + "hold": 74 + }, + { + "kind": "key", + "key": "e", + "gap": 202, + "hold": 125 + }, + { + "kind": "key", + "key": " ", + "gap": 140, + "hold": 119 + }, + { + "kind": "key", + "key": "l", + "gap": 109, + "hold": 80 + }, + { + "kind": "key", + "key": "o", + "gap": 177, + "hold": 40 + }, + { + "kind": "key", + "key": "o", + "gap": 91, + "hold": 93 + }, + { + "kind": "key", + "key": "k", + "gap": 194, + "hold": 106 + }, + { + "kind": "key", + "key": " ", + "gap": 172, + "hold": 126 + }, + { + "kind": "key", + "key": "h", + "gap": 160, + "hold": 104 + }, + { + "kind": "key", + "key": "p", + "gap": 183, + "hold": 134 + }, + { + "kind": "key", + "key": "h", + "gap": 160, + "hold": 104 + }, + { + "kind": "key", + "key": "e", + "gap": 99, + "hold": 81 + }, + { + "kind": "key", + "key": "r", + "gap": 154, + "hold": 132 + }, + { + "kind": "key", + "key": "e", + "gap": 140, + "hold": 91 + }, + { + "kind": "key", + "key": " ", + "gap": 150, + "hold": 103 + }, + { + "kind": "key", + "key": "n", + "gap": 170, + "hold": 81 + }, + { + "kind": "key", + "key": "o", + "gap": 173, + "hold": 117 + }, + { + "kind": "key", + "key": "t", + "gap": 146, + "hold": 148 + }, + { + "kind": "key", + "key": " ", + "gap": 196, + "hold": 118 + }, + { + "kind": "key", + "key": "m", + "gap": 171, + "hold": 145 + }, + { + "kind": "key", + "key": "e", + "gap": 182, + "hold": 46 + }, + { + "kind": "key", + "key": "a", + "gap": 98, + "hold": 99 + }, + { + "kind": "key", + "key": "n", + "gap": 194, + "hold": 78 + }, + { + "kind": "key", + "key": " ", + "gap": 155, + "hold": 72 + }, + { + "kind": "key", + "key": "f", + "gap": 146, + "hold": 101 + }, + { + "kind": "key", + "key": "o", + "gap": 159, + "hold": 83 + }, + { + "kind": "key", + "key": "r", + "gap": 181, + "hold": 59 + }, + { + "kind": "key", + "key": " ", + "gap": 204, + "hold": 111 + }, + { + "kind": "pause", + "ms": 1600 + }, + { + "kind": "key", + "key": "m", + "gap": 180, + "hold": 125 + }, + { + "kind": "key", + "key": "o", + "gap": 97, + "hold": 85 + }, + { + "kind": "key", + "key": "s", + "gap": 109, + "hold": 116 + }, + { + "kind": "key", + "key": "t", + "gap": 183, + "hold": 108 + }, + { + "kind": "key", + "key": " ", + "gap": 208, + "hold": 149 + }, + { + "kind": "key", + "key": "w", + "gap": 208, + "hold": 104 + }, + { + "kind": "key", + "key": "a", + "gap": 196, + "hold": 145 + }, + { + "kind": "key", + "key": "n", + "gap": 188, + "hold": 75 + }, + { + "kind": "key", + "key": "t", + "gap": 165, + "hold": 144 + }, + { + "kind": "key", + "key": " ", + "gap": 137, + "hold": 106 + }, + { + "kind": "key", + "key": "l", + "gap": 107, + "hold": 109 + }, + { + "kind": "key", + "key": "e", + "gap": 156, + "hold": 68 + }, + { + "kind": "key", + "key": "a", + "gap": 196, + "hold": 45 + }, + { + "kind": "key", + "key": "d", + "gap": 192, + "hold": 50 + }, + { + "kind": "key", + "key": " ", + "gap": 141, + "hold": 138 + }, + { + "kind": "key", + "key": "m", + "gap": 93, + "hold": 114 + }, + { + "kind": "key", + "key": "u", + "gap": 102, + "hold": 72 + }, + { + "kind": "key", + "key": "c", + "gap": 209, + "hold": 98 + }, + { + "kind": "key", + "key": "h", + "gap": 112, + "hold": 41 + }, + { + "kind": "key", + "key": " ", + "gap": 109, + "hold": 111 + }, + { + "kind": "key", + "key": "o", + "gap": 166, + "hold": 131 + }, + { + "kind": "key", + "key": "f", + "gap": 173, + "hold": 129 + }, + { + "kind": "key", + "key": "f", + "gap": 105, + "hold": 61 + }, + { + "kind": "key", + "key": " ", + "gap": 105, + "hold": 107 + }, + { + "kind": "key", + "key": "t", + "gap": 171, + "hold": 104 + }, + { + "kind": "key", + "key": "h", + "gap": 177, + "hold": 75 + }, + { + "kind": "key", + "key": "i", + "gap": 204, + "hold": 50 + }, + { + "kind": "key", + "key": "n", + "gap": 206, + "hold": 61 + }, + { + "kind": "key", + "key": "k", + "gap": 136, + "hold": 95 + }, + { + "kind": "key", + "key": " ", + "gap": 142, + "hold": 101 + }, + { + "kind": "key", + "key": "y", + "gap": 123, + "hold": 114 + }, + { + "kind": "key", + "key": "e", + "gap": 91, + "hold": 91 + }, + { + "kind": "key", + "key": "a", + "gap": 184, + "hold": 77 + }, + { + "kind": "key", + "key": "r", + "gap": 144, + "hold": 81 + }, + { + "kind": "key", + "key": " ", + "gap": 209, + "hold": 49 + }, + { + "kind": "key", + "key": "t", + "gap": 102, + "hold": 60 + }, + { + "kind": "key", + "key": "h", + "gap": 105, + "hold": 135 + }, + { + "kind": "key", + "key": "r", + "gap": 183, + "hold": 132 + }, + { + "kind": "key", + "key": "o", + "gap": 136, + "hold": 47 + }, + { + "kind": "key", + "key": "u", + "gap": 130, + "hold": 69 + }, + { + "kind": "key", + "key": "g", + "gap": 106, + "hold": 54 + }, + { + "kind": "key", + "key": "h", + "gap": 139, + "hold": 65 + }, + { + "kind": "key", + "key": " ", + "gap": 92, + "hold": 100 + } + ], + "eventLog": { + "version": 1, + "events": [ + { + "type": "keydown", + "testMs": -6.5, + "data": { + "code": "KeyI" + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": "i", + "correct": true, + "wordIndex": 0, + "charIndex": 0, + "inputValue": "i" + } + }, + { + "type": "timer", + "testMs": 0, + "data": { + "event": "start", + "timer": 0, + "date": 1791473705135 + } + }, + { + "type": "keyup", + "testMs": 53.1, + "data": { + "code": "KeyI" + } + }, + { + "type": "keydown", + "testMs": 135.2, + "data": { + "code": "KeyN" + } + }, + { + "type": "input", + "testMs": 136.3, + "data": { + "inputType": "insertText", + "data": "n", + "correct": true, + "wordIndex": 0, + "charIndex": 1, + "inputValue": "in" + } + }, + { + "type": "keyup", + "testMs": 183.6, + "data": { + "code": "KeyN" + } + }, + { + "type": "keydown", + "testMs": 319.1, + "data": { + "code": "KeyT" + } + }, + { + "type": "input", + "testMs": 321, + "data": { + "inputType": "insertText", + "data": "t", + "correct": true, + "wordIndex": 0, + "charIndex": 2, + "inputValue": "int" + } + }, + { + "type": "keyup", + "testMs": 411.9, + "data": { + "code": "KeyT" + } + }, + { + "type": "keydown", + "testMs": 466.9, + "data": { + "code": "KeyO" + } + }, + { + "type": "input", + "testMs": 469, + "data": { + "inputType": "insertText", + "data": "o", + "correct": true, + "wordIndex": 0, + "charIndex": 3, + "inputValue": "into" + } + }, + { + "type": "keyup", + "testMs": 525.3, + "data": { + "code": "KeyO" + } + }, + { + "type": "keydown", + "testMs": 588.2, + "data": { + "code": "Space" + } + }, + { + "type": "input", + "testMs": 591.1, + "data": { + "inputType": "insertText", + "data": " ", + "correct": true, + "wordIndex": 0, + "charIndex": 4, + "inputValue": "into ", + "commitsWord": true + } + }, + { + "type": "keyup", + "testMs": 689.1, + "data": { + "code": "Space" + } + }, + { + "type": "keydown", + "testMs": 815.5, + "data": { + "code": "KeyO" + } + }, + { + "type": "input", + "testMs": 817.8, + "data": { + "inputType": "insertText", + "data": "o", + "correct": true, + "wordIndex": 1, + "charIndex": 0, + "inputValue": "o" + } + }, + { + "type": "keyup", + "testMs": 864.2, + "data": { + "code": "KeyO" + } + }, + { + "type": "keydown", + "testMs": 992.3, + "data": { + "code": "KeyV" + } + }, + { + "type": "input", + "testMs": 994.6, + "data": { + "inputType": "insertText", + "data": "v", + "correct": true, + "wordIndex": 1, + "charIndex": 1, + "inputValue": "ov" + } + }, + { + "type": "timer", + "testMs": 1007.4, + "data": { + "event": "step", + "timer": 1, + "drift": 7.4 + } + }, + { + "type": "keyup", + "testMs": 1082.7, + "data": { + "code": "KeyV" + } + }, + { + "type": "keydown", + "testMs": 1176.6, + "data": { + "code": "KeyE" + } + }, + { + "type": "input", + "testMs": 1178.7, + "data": { + "inputType": "insertText", + "data": "e", + "correct": true, + "wordIndex": 1, + "charIndex": 2, + "inputValue": "ove" + } + }, + { + "type": "keydown", + "testMs": 1305.5, + "data": { + "code": "KeyR" + } + }, + { + "type": "input", + "testMs": 1307.2, + "data": { + "inputType": "insertText", + "data": "r", + "correct": true, + "wordIndex": 1, + "charIndex": 3, + "inputValue": "over" + } + }, + { + "type": "keyup", + "testMs": 1310.8, + "data": { + "code": "KeyE" + } + }, + { + "type": "keyup", + "testMs": 1421.3, + "data": { + "code": "KeyR" + } + }, + { + "type": "keydown", + "testMs": 1427, + "data": { + "code": "Space" + } + }, + { + "type": "input", + "testMs": 1428.4, + "data": { + "inputType": "insertText", + "data": " ", + "correct": true, + "wordIndex": 1, + "charIndex": 4, + "inputValue": "over ", + "commitsWord": true + } + }, + { + "type": "keyup", + "testMs": 1543.5, + "data": { + "code": "Space" + } + }, + { + "type": "keydown", + "testMs": 1714.5, + "data": { + "code": "KeyH" + } + }, + { + "type": "input", + "testMs": 1716.5, + "data": { + "inputType": "insertText", + "data": "h", + "correct": true, + "wordIndex": 2, + "charIndex": 0, + "inputValue": "h" + } + }, + { + "type": "keyup", + "testMs": 1846.6, + "data": { + "code": "KeyH" + } + }, + { + "type": "keydown", + "testMs": 1868.8, + "data": { + "code": "KeyA" + } + }, + { + "type": "input", + "testMs": 1870.7, + "data": { + "inputType": "insertText", + "data": "a", + "correct": true, + "wordIndex": 2, + "charIndex": 1, + "inputValue": "ha" + } + }, + { + "type": "keyup", + "testMs": 1909.1, + "data": { + "code": "KeyA" + } + }, + { + "type": "timer", + "testMs": 2006.6, + "data": { + "event": "step", + "timer": 2, + "drift": 6.6 + } + }, + { + "type": "keydown", + "testMs": 2065.4, + "data": { + "code": "KeyV" + } + }, + { + "type": "input", + "testMs": 2067.9, + "data": { + "inputType": "insertText", + "data": "v", + "correct": true, + "wordIndex": 2, + "charIndex": 2, + "inputValue": "hav" + } + }, + { + "type": "keyup", + "testMs": 2138.7, + "data": { + "code": "KeyV" + } + }, + { + "type": "keydown", + "testMs": 2265.8, + "data": { + "code": "KeyE" + } + }, + { + "type": "input", + "testMs": 2267.9, + "data": { + "inputType": "insertText", + "data": "e", + "correct": true, + "wordIndex": 2, + "charIndex": 3, + "inputValue": "have" + } + }, + { + "type": "keyup", + "testMs": 2392.2, + "data": { + "code": "KeyE" + } + }, + { + "type": "keydown", + "testMs": 2404.9, + "data": { + "code": "Space" + } + }, + { + "type": "input", + "testMs": 2406.1, + "data": { + "inputType": "insertText", + "data": " ", + "correct": true, + "wordIndex": 2, + "charIndex": 4, + "inputValue": "have ", + "commitsWord": true + } + }, + { + "type": "keyup", + "testMs": 2526.1, + "data": { + "code": "Space" + } + }, + { + "type": "keydown", + "testMs": 2647.3, + "data": { + "code": "KeyL" + } + }, + { + "type": "input", + "testMs": 2650.1, + "data": { + "inputType": "insertText", + "data": "l", + "correct": true, + "wordIndex": 3, + "charIndex": 0, + "inputValue": "l" + } + }, + { + "type": "keyup", + "testMs": 2728.3, + "data": { + "code": "KeyL" + } + }, + { + "type": "keydown", + "testMs": 2826.3, + "data": { + "code": "KeyO" + } + }, + { + "type": "input", + "testMs": 2828.5, + "data": { + "inputType": "insertText", + "data": "o", + "correct": true, + "wordIndex": 3, + "charIndex": 1, + "inputValue": "lo" + } + }, + { + "type": "keyup", + "testMs": 2863.3, + "data": { + "code": "KeyO" + } + }, + { + "type": "keydown", + "testMs": 2914.2, + "data": { + "code": "KeyO" + } + }, + { + "type": "input", + "testMs": 2915.7, + "data": { + "inputType": "insertText", + "data": "o", + "correct": true, + "wordIndex": 3, + "charIndex": 2, + "inputValue": "loo" + } + }, + { + "type": "keyup", + "testMs": 3006.8, + "data": { + "code": "KeyO" + } + }, + { + "type": "timer", + "testMs": 3007.1, + "data": { + "event": "step", + "timer": 3, + "drift": 7.1 + } + }, + { + "type": "keydown", + "testMs": 3107.1, + "data": { + "code": "KeyK" + } + }, + { + "type": "input", + "testMs": 3107.8, + "data": { + "inputType": "insertText", + "data": "k", + "correct": true, + "wordIndex": 3, + "charIndex": 3, + "inputValue": "look" + } + }, + { + "type": "keyup", + "testMs": 3213.3, + "data": { + "code": "KeyK" + } + }, + { + "type": "keydown", + "testMs": 3280.7, + "data": { + "code": "Space" + } + }, + { + "type": "input", + "testMs": 3282.8, + "data": { + "inputType": "insertText", + "data": " ", + "correct": true, + "wordIndex": 3, + "charIndex": 4, + "inputValue": "look ", + "commitsWord": true + } + }, + { + "type": "keyup", + "testMs": 3406.1, + "data": { + "code": "Space" + } + }, + { + "type": "keydown", + "testMs": 3579, + "data": { + "code": "KeyH" + } + }, + { + "type": "input", + "testMs": 3580.2, + "data": { + "inputType": "insertText", + "data": "h", + "correct": true, + "wordIndex": 4, + "charIndex": 0, + "inputValue": "h" + } + }, + { + "type": "keyup", + "testMs": 3682, + "data": { + "code": "KeyH" + } + }, + { + "type": "keydown", + "testMs": 3761.1, + "data": { + "code": "KeyP" + } + }, + { + "type": "input", + "testMs": 3762.6, + "data": { + "inputType": "insertText", + "data": "p", + "correct": false, + "wordIndex": 4, + "charIndex": 1, + "inputValue": "hp" + } + }, + { + "type": "keyup", + "testMs": 3895.4, + "data": { + "code": "KeyP" + } + }, + { + "type": "keydown", + "testMs": 3919.1, + "data": { + "code": "KeyH" + } + }, + { + "type": "input", + "testMs": 3920.4, + "data": { + "inputType": "insertText", + "data": "h", + "correct": false, + "wordIndex": 4, + "charIndex": 2, + "inputValue": "hph" + } + }, + { + "type": "timer", + "testMs": 4007.1, + "data": { + "event": "step", + "timer": 4, + "drift": 7.1 + } + }, + { + "type": "keydown", + "testMs": 4020.6, + "data": { + "code": "KeyE" + } + }, + { + "type": "input", + "testMs": 4022.6, + "data": { + "inputType": "insertText", + "data": "e", + "correct": true, + "wordIndex": 4, + "charIndex": 3, + "inputValue": "hphe" + } + }, + { + "type": "keyup", + "testMs": 4026.7, + "data": { + "code": "KeyH" + } + }, + { + "type": "keyup", + "testMs": 4101.5, + "data": { + "code": "KeyE" + } + }, + { + "type": "keydown", + "testMs": 4175.5, + "data": { + "code": "KeyR" + } + }, + { + "type": "input", + "testMs": 4179.9, + "data": { + "inputType": "insertText", + "data": "r", + "correct": false, + "wordIndex": 4, + "charIndex": 4, + "inputValue": "hpher" + } + }, + { + "type": "keyup", + "testMs": 4307.8, + "data": { + "code": "KeyR" + } + }, + { + "type": "keydown", + "testMs": 4312.6, + "data": { + "code": "KeyE" + } + }, + { + "type": "input", + "testMs": 4314.9, + "data": { + "inputType": "insertText", + "data": "e", + "correct": false, + "wordIndex": 4, + "charIndex": 5, + "inputValue": "hphere" + } + }, + { + "type": "keyup", + "testMs": 4405, + "data": { + "code": "KeyE" + } + }, + { + "type": "keydown", + "testMs": 4465.2, + "data": { + "code": "Space" + } + }, + { + "type": "input", + "testMs": 4466.8, + "data": { + "inputType": "insertText", + "data": " ", + "correct": false, + "wordIndex": 4, + "charIndex": 6, + "inputValue": "hphere ", + "commitsWord": true + } + }, + { + "type": "keyup", + "testMs": 4567.6, + "data": { + "code": "Space" + } + }, + { + "type": "keydown", + "testMs": 4752.2, + "data": { + "code": "KeyN" + } + }, + { + "type": "input", + "testMs": 4754.7, + "data": { + "inputType": "insertText", + "data": "n", + "correct": true, + "wordIndex": 5, + "charIndex": 0, + "inputValue": "n" + } + }, + { + "type": "keyup", + "testMs": 4832.2, + "data": { + "code": "KeyN" + } + }, + { + "type": "keydown", + "testMs": 4923.9, + "data": { + "code": "KeyO" + } + }, + { + "type": "input", + "testMs": 4925.1, + "data": { + "inputType": "insertText", + "data": "o", + "correct": true, + "wordIndex": 5, + "charIndex": 1, + "inputValue": "no" + } + }, + { + "type": "timer", + "testMs": 5007, + "data": { + "event": "step", + "timer": 5, + "drift": 7 + } + }, + { + "type": "keyup", + "testMs": 5042.8, + "data": { + "code": "KeyO" + } + }, + { + "type": "keydown", + "testMs": 5074.2, + "data": { + "code": "KeyT" + } + }, + { + "type": "input", + "testMs": 5075.2, + "data": { + "inputType": "insertText", + "data": "t", + "correct": true, + "wordIndex": 5, + "charIndex": 2, + "inputValue": "not" + } + }, + { + "type": "keyup", + "testMs": 5219, + "data": { + "code": "KeyT" + } + }, + { + "type": "keydown", + "testMs": 5265, + "data": { + "code": "Space" + } + }, + { + "type": "input", + "testMs": 5267, + "data": { + "inputType": "insertText", + "data": " ", + "correct": true, + "wordIndex": 5, + "charIndex": 3, + "inputValue": "not ", + "commitsWord": true + } + }, + { + "type": "keyup", + "testMs": 5384.3, + "data": { + "code": "Space" + } + }, + { + "type": "keydown", + "testMs": 5564.7, + "data": { + "code": "KeyM" + } + }, + { + "type": "input", + "testMs": 5566.1, + "data": { + "inputType": "insertText", + "data": "m", + "correct": true, + "wordIndex": 6, + "charIndex": 0, + "inputValue": "m" + } + }, + { + "type": "keyup", + "testMs": 5710.2, + "data": { + "code": "KeyM" + } + }, + { + "type": "keydown", + "testMs": 5747.1, + "data": { + "code": "KeyE" + } + }, + { + "type": "input", + "testMs": 5749.8, + "data": { + "inputType": "insertText", + "data": "e", + "correct": true, + "wordIndex": 6, + "charIndex": 1, + "inputValue": "me" + } + }, + { + "type": "keyup", + "testMs": 5793.2, + "data": { + "code": "KeyE" + } + }, + { + "type": "keydown", + "testMs": 5845.6, + "data": { + "code": "KeyA" + } + }, + { + "type": "input", + "testMs": 5847.7, + "data": { + "inputType": "insertText", + "data": "a", + "correct": true, + "wordIndex": 6, + "charIndex": 2, + "inputValue": "mea" + } + }, + { + "type": "keyup", + "testMs": 5944.1, + "data": { + "code": "KeyA" + } + }, + { + "type": "timer", + "testMs": 6005.1, + "data": { + "event": "step", + "timer": 6, + "drift": 5.1 + } + }, + { + "type": "keydown", + "testMs": 6038, + "data": { + "code": "KeyN" + } + }, + { + "type": "input", + "testMs": 6038.8, + "data": { + "inputType": "insertText", + "data": "n", + "correct": true, + "wordIndex": 6, + "charIndex": 3, + "inputValue": "mean" + } + }, + { + "type": "keyup", + "testMs": 6114.6, + "data": { + "code": "KeyN" + } + }, + { + "type": "keydown", + "testMs": 6195.2, + "data": { + "code": "Space" + } + }, + { + "type": "input", + "testMs": 6197.3, + "data": { + "inputType": "insertText", + "data": " ", + "correct": true, + "wordIndex": 6, + "charIndex": 4, + "inputValue": "mean ", + "commitsWord": true + } + }, + { + "type": "keyup", + "testMs": 6266, + "data": { + "code": "Space" + } + }, + { + "type": "keydown", + "testMs": 6422.5, + "data": { + "code": "KeyF" + } + }, + { + "type": "input", + "testMs": 6424.4, + "data": { + "inputType": "insertText", + "data": "f", + "correct": true, + "wordIndex": 7, + "charIndex": 0, + "inputValue": "f" + } + }, + { + "type": "keyup", + "testMs": 6526.8, + "data": { + "code": "KeyF" + } + }, + { + "type": "keydown", + "testMs": 6582.3, + "data": { + "code": "KeyO" + } + }, + { + "type": "input", + "testMs": 6584.8, + "data": { + "inputType": "insertText", + "data": "o", + "correct": true, + "wordIndex": 7, + "charIndex": 1, + "inputValue": "fo" + } + }, + { + "type": "keyup", + "testMs": 6665.1, + "data": { + "code": "KeyO" + } + }, + { + "type": "keydown", + "testMs": 6763.8, + "data": { + "code": "KeyR" + } + }, + { + "type": "input", + "testMs": 6766.2, + "data": { + "inputType": "insertText", + "data": "r", + "correct": true, + "wordIndex": 7, + "charIndex": 2, + "inputValue": "for" + } + }, + { + "type": "keyup", + "testMs": 6822.1, + "data": { + "code": "KeyR" + } + }, + { + "type": "keydown", + "testMs": 6967.2, + "data": { + "code": "Space" + } + }, + { + "type": "input", + "testMs": 6969.3, + "data": { + "inputType": "insertText", + "data": " ", + "correct": true, + "wordIndex": 7, + "charIndex": 3, + "inputValue": "for ", + "commitsWord": true + } + }, + { + "type": "timer", + "testMs": 7007.1, + "data": { + "event": "step", + "timer": 7, + "drift": 7.1 + } + }, + { + "type": "keyup", + "testMs": 7078.3, + "data": { + "code": "Space" + } + }, + { + "type": "timer", + "testMs": 8007.2, + "data": { + "event": "step", + "timer": 8, + "drift": 7.2 + } + }, + { + "type": "keydown", + "testMs": 8868.8, + "data": { + "code": "KeyM" + } + }, + { + "type": "input", + "testMs": 8871.2, + "data": { + "inputType": "insertText", + "data": "m", + "correct": true, + "wordIndex": 8, + "charIndex": 0, + "inputValue": "m" + } + }, + { + "type": "keydown", + "testMs": 8967, + "data": { + "code": "KeyO" + } + }, + { + "type": "input", + "testMs": 8969, + "data": { + "inputType": "insertText", + "data": "o", + "correct": true, + "wordIndex": 8, + "charIndex": 1, + "inputValue": "mo" + } + }, + { + "type": "keyup", + "testMs": 8993.3, + "data": { + "code": "KeyM" + } + }, + { + "type": "timer", + "testMs": 9005.6, + "data": { + "event": "step", + "timer": 9, + "drift": 5.6 + } + }, + { + "type": "keyup", + "testMs": 9050.5, + "data": { + "code": "KeyO" + } + }, + { + "type": "keydown", + "testMs": 9075.6, + "data": { + "code": "KeyS" + } + }, + { + "type": "input", + "testMs": 9076.8, + "data": { + "inputType": "insertText", + "data": "s", + "correct": true, + "wordIndex": 8, + "charIndex": 2, + "inputValue": "mos" + } + }, + { + "type": "keyup", + "testMs": 9193.5, + "data": { + "code": "KeyS" + } + }, + { + "type": "keydown", + "testMs": 9259.3, + "data": { + "code": "KeyT" + } + }, + { + "type": "input", + "testMs": 9261.5, + "data": { + "inputType": "insertText", + "data": "t", + "correct": true, + "wordIndex": 8, + "charIndex": 3, + "inputValue": "most" + } + }, + { + "type": "keyup", + "testMs": 9365.8, + "data": { + "code": "KeyT" + } + }, + { + "type": "keydown", + "testMs": 9465.2, + "data": { + "code": "Space" + } + }, + { + "type": "input", + "testMs": 9466.8, + "data": { + "inputType": "insertText", + "data": " ", + "correct": true, + "wordIndex": 8, + "charIndex": 4, + "inputValue": "most ", + "commitsWord": true + } + }, + { + "type": "keyup", + "testMs": 9615.2, + "data": { + "code": "Space" + } + }, + { + "type": "keydown", + "testMs": 9837.2, + "data": { + "code": "KeyW" + } + }, + { + "type": "input", + "testMs": 9839.6, + "data": { + "inputType": "insertText", + "data": "w", + "correct": true, + "wordIndex": 9, + "charIndex": 0, + "inputValue": "w" + } + }, + { + "type": "keyup", + "testMs": 9942.7, + "data": { + "code": "KeyW" + } + }, + { + "type": "timer", + "testMs": 10006.5, + "data": { + "event": "step", + "timer": 10, + "drift": 6.5 + } + }, + { + "type": "keydown", + "testMs": 10032.2, + "data": { + "code": "KeyA" + } + }, + { + "type": "input", + "testMs": 10033.7, + "data": { + "inputType": "insertText", + "data": "a", + "correct": true, + "wordIndex": 9, + "charIndex": 1, + "inputValue": "wa" + } + }, + { + "type": "keyup", + "testMs": 10178.8, + "data": { + "code": "KeyA" + } + }, + { + "type": "keydown", + "testMs": 10221, + "data": { + "code": "KeyN" + } + }, + { + "type": "input", + "testMs": 10223.1, + "data": { + "inputType": "insertText", + "data": "n", + "correct": true, + "wordIndex": 9, + "charIndex": 2, + "inputValue": "wan" + } + }, + { + "type": "keyup", + "testMs": 10295.5, + "data": { + "code": "KeyN" + } + }, + { + "type": "keydown", + "testMs": 10385.8, + "data": { + "code": "KeyT" + } + }, + { + "type": "input", + "testMs": 10387.9, + "data": { + "inputType": "insertText", + "data": "t", + "correct": true, + "wordIndex": 9, + "charIndex": 3, + "inputValue": "want" + } + }, + { + "type": "keydown", + "testMs": 10522.3, + "data": { + "code": "Space" + } + }, + { + "type": "input", + "testMs": 10524.7, + "data": { + "inputType": "insertText", + "data": " ", + "correct": true, + "wordIndex": 9, + "charIndex": 4, + "inputValue": "want ", + "commitsWord": true + } + }, + { + "type": "keyup", + "testMs": 10530.3, + "data": { + "code": "KeyT" + } + }, + { + "type": "keyup", + "testMs": 10627.8, + "data": { + "code": "Space" + } + }, + { + "type": "keydown", + "testMs": 10744.7, + "data": { + "code": "KeyL" + } + }, + { + "type": "input", + "testMs": 10746.7, + "data": { + "inputType": "insertText", + "data": "l", + "correct": true, + "wordIndex": 10, + "charIndex": 0, + "inputValue": "l" + } + }, + { + "type": "keyup", + "testMs": 10853.1, + "data": { + "code": "KeyL" + } + }, + { + "type": "keydown", + "testMs": 10898.6, + "data": { + "code": "KeyE" + } + }, + { + "type": "input", + "testMs": 10900.6, + "data": { + "inputType": "insertText", + "data": "e", + "correct": true, + "wordIndex": 10, + "charIndex": 1, + "inputValue": "le" + } + }, + { + "type": "keyup", + "testMs": 10969, + "data": { + "code": "KeyE" + } + }, + { + "type": "timer", + "testMs": 11007.3, + "data": { + "event": "step", + "timer": 11, + "drift": 7.3 + } + }, + { + "type": "keydown", + "testMs": 11096.7, + "data": { + "code": "KeyA" + } + }, + { + "type": "input", + "testMs": 11098.7, + "data": { + "inputType": "insertText", + "data": "a", + "correct": true, + "wordIndex": 10, + "charIndex": 2, + "inputValue": "lea" + } + }, + { + "type": "keyup", + "testMs": 11143.1, + "data": { + "code": "KeyA" + } + }, + { + "type": "keydown", + "testMs": 11288.8, + "data": { + "code": "KeyD" + } + }, + { + "type": "input", + "testMs": 11290.7, + "data": { + "inputType": "insertText", + "data": "d", + "correct": true, + "wordIndex": 10, + "charIndex": 3, + "inputValue": "lead" + } + }, + { + "type": "keyup", + "testMs": 11338.5, + "data": { + "code": "KeyD" + } + }, + { + "type": "keydown", + "testMs": 11429.7, + "data": { + "code": "Space" + } + }, + { + "type": "input", + "testMs": 11431.2, + "data": { + "inputType": "insertText", + "data": " ", + "correct": true, + "wordIndex": 10, + "charIndex": 4, + "inputValue": "lead ", + "commitsWord": true + } + }, + { + "type": "keyup", + "testMs": 11567.1, + "data": { + "code": "Space" + } + }, + { + "type": "keydown", + "testMs": 11671.6, + "data": { + "code": "KeyM" + } + }, + { + "type": "input", + "testMs": 11674, + "data": { + "inputType": "insertText", + "data": "m", + "correct": true, + "wordIndex": 11, + "charIndex": 0, + "inputValue": "m" + } + }, + { + "type": "keydown", + "testMs": 11775.9, + "data": { + "code": "KeyU" + } + }, + { + "type": "input", + "testMs": 11778.1, + "data": { + "inputType": "insertText", + "data": "u", + "correct": true, + "wordIndex": 11, + "charIndex": 1, + "inputValue": "mu" + } + }, + { + "type": "keyup", + "testMs": 11783.1, + "data": { + "code": "KeyM" + } + }, + { + "type": "keyup", + "testMs": 11844.7, + "data": { + "code": "KeyU" + } + }, + { + "type": "keydown", + "testMs": 11981.1, + "data": { + "code": "KeyC" + } + }, + { + "type": "input", + "testMs": 11982.3, + "data": { + "inputType": "insertText", + "data": "c", + "correct": true, + "wordIndex": 11, + "charIndex": 2, + "inputValue": "muc" + } + }, + { + "type": "timer", + "testMs": 12007, + "data": { + "event": "step", + "timer": 12, + "drift": 7 + } + }, + { + "type": "keyup", + "testMs": 12081, + "data": { + "code": "KeyC" + } + }, + { + "type": "keydown", + "testMs": 12094.7, + "data": { + "code": "KeyH" + } + }, + { + "type": "input", + "testMs": 12096.9, + "data": { + "inputType": "insertText", + "data": "h", + "correct": true, + "wordIndex": 11, + "charIndex": 3, + "inputValue": "much" + } + }, + { + "type": "keyup", + "testMs": 12135.1, + "data": { + "code": "KeyH" + } + }, + { + "type": "keydown", + "testMs": 12204.6, + "data": { + "code": "Space" + } + }, + { + "type": "input", + "testMs": 12206.9, + "data": { + "inputType": "insertText", + "data": " ", + "correct": true, + "wordIndex": 11, + "charIndex": 4, + "inputValue": "much ", + "commitsWord": true + } + }, + { + "type": "keyup", + "testMs": 12315, + "data": { + "code": "Space" + } + }, + { + "type": "keydown", + "testMs": 12493.1, + "data": { + "code": "KeyO" + } + }, + { + "type": "input", + "testMs": 12495.3, + "data": { + "inputType": "insertText", + "data": "o", + "correct": true, + "wordIndex": 12, + "charIndex": 0, + "inputValue": "o" + } + }, + { + "type": "keyup", + "testMs": 12623.2, + "data": { + "code": "KeyO" + } + }, + { + "type": "keydown", + "testMs": 12664.6, + "data": { + "code": "KeyF" + } + }, + { + "type": "input", + "testMs": 12666.7, + "data": { + "inputType": "insertText", + "data": "f", + "correct": true, + "wordIndex": 12, + "charIndex": 1, + "inputValue": "of" + } + }, + { + "type": "keyup", + "testMs": 12767.9, + "data": { + "code": "KeyF" + } + }, + { + "type": "keydown", + "testMs": 12769.9, + "data": { + "code": "KeyF" + } + }, + { + "type": "input", + "testMs": 12771.6, + "data": { + "inputType": "insertText", + "data": "f", + "correct": true, + "wordIndex": 12, + "charIndex": 2, + "inputValue": "off" + } + }, + { + "type": "keyup", + "testMs": 12791.8, + "data": { + "code": "KeyF" + } + }, + { + "type": "keydown", + "testMs": 12874.2, + "data": { + "code": "Space" + } + }, + { + "type": "input", + "testMs": 12876.2, + "data": { + "inputType": "insertText", + "data": " ", + "correct": true, + "wordIndex": 12, + "charIndex": 3, + "inputValue": "off ", + "commitsWord": true + } + }, + { + "type": "keyup", + "testMs": 12980.3, + "data": { + "code": "Space" + } + }, + { + "type": "timer", + "testMs": 13007.1, + "data": { + "event": "step", + "timer": 13, + "drift": 7.1 + } + }, + { + "type": "keydown", + "testMs": 13162.3, + "data": { + "code": "KeyT" + } + }, + { + "type": "input", + "testMs": 13164.6, + "data": { + "inputType": "insertText", + "data": "t", + "correct": true, + "wordIndex": 13, + "charIndex": 0, + "inputValue": "t" + } + }, + { + "type": "keyup", + "testMs": 13266.6, + "data": { + "code": "KeyT" + } + }, + { + "type": "keydown", + "testMs": 13339.3, + "data": { + "code": "KeyH" + } + }, + { + "type": "input", + "testMs": 13341.5, + "data": { + "inputType": "insertText", + "data": "h", + "correct": true, + "wordIndex": 13, + "charIndex": 1, + "inputValue": "th" + } + }, + { + "type": "keyup", + "testMs": 13414.6, + "data": { + "code": "KeyH" + } + }, + { + "type": "keydown", + "testMs": 13543.3, + "data": { + "code": "KeyI" + } + }, + { + "type": "input", + "testMs": 13545.5, + "data": { + "inputType": "insertText", + "data": "i", + "correct": true, + "wordIndex": 13, + "charIndex": 2, + "inputValue": "thi" + } + }, + { + "type": "keyup", + "testMs": 13593.1, + "data": { + "code": "KeyI" + } + }, + { + "type": "keydown", + "testMs": 13748.7, + "data": { + "code": "KeyN" + } + }, + { + "type": "input", + "testMs": 13750.9, + "data": { + "inputType": "insertText", + "data": "n", + "correct": true, + "wordIndex": 13, + "charIndex": 3, + "inputValue": "thin" + } + }, + { + "type": "keyup", + "testMs": 13808.7, + "data": { + "code": "KeyN" + } + }, + { + "type": "keydown", + "testMs": 13885.3, + "data": { + "code": "KeyK" + } + }, + { + "type": "input", + "testMs": 13886.8, + "data": { + "inputType": "insertText", + "data": "k", + "correct": true, + "wordIndex": 13, + "charIndex": 4, + "inputValue": "think" + } + }, + { + "type": "keyup", + "testMs": 13980, + "data": { + "code": "KeyK" + } + }, + { + "type": "timer", + "testMs": 14007.3, + "data": { + "event": "step", + "timer": 14, + "drift": 7.3 + } + }, + { + "type": "keydown", + "testMs": 14026.5, + "data": { + "code": "Space" + } + }, + { + "type": "input", + "testMs": 14028.2, + "data": { + "inputType": "insertText", + "data": " ", + "correct": true, + "wordIndex": 13, + "charIndex": 5, + "inputValue": "think ", + "commitsWord": true + } + }, + { + "type": "keyup", + "testMs": 14127.4, + "data": { + "code": "Space" + } + }, + { + "type": "keydown", + "testMs": 14261.8, + "data": { + "code": "KeyY" + } + }, + { + "type": "input", + "testMs": 14264, + "data": { + "inputType": "insertText", + "data": "y", + "correct": true, + "wordIndex": 14, + "charIndex": 0, + "inputValue": "y" + } + }, + { + "type": "keydown", + "testMs": 14352.1, + "data": { + "code": "KeyE" + } + }, + { + "type": "input", + "testMs": 14354.4, + "data": { + "inputType": "insertText", + "data": "e", + "correct": true, + "wordIndex": 14, + "charIndex": 1, + "inputValue": "ye" + } + }, + { + "type": "keyup", + "testMs": 14373.2, + "data": { + "code": "KeyY" + } + }, + { + "type": "keyup", + "testMs": 14442.2, + "data": { + "code": "KeyE" + } + }, + { + "type": "keydown", + "testMs": 14536.6, + "data": { + "code": "KeyA" + } + }, + { + "type": "input", + "testMs": 14538.8, + "data": { + "inputType": "insertText", + "data": "a", + "correct": true, + "wordIndex": 14, + "charIndex": 2, + "inputValue": "yea" + } + }, + { + "type": "keyup", + "testMs": 14611.3, + "data": { + "code": "KeyA" + } + }, + { + "type": "keydown", + "testMs": 14680, + "data": { + "code": "KeyR" + } + }, + { + "type": "input", + "testMs": 14682.2, + "data": { + "inputType": "insertText", + "data": "r", + "correct": true, + "wordIndex": 14, + "charIndex": 3, + "inputValue": "year" + } + }, + { + "type": "keyup", + "testMs": 14760.7, + "data": { + "code": "KeyR" + } + }, + { + "type": "keydown", + "testMs": 14888.6, + "data": { + "code": "Space" + } + }, + { + "type": "input", + "testMs": 14891.1, + "data": { + "inputType": "insertText", + "data": " ", + "correct": true, + "wordIndex": 14, + "charIndex": 4, + "inputValue": "year ", + "commitsWord": true + } + }, + { + "type": "keyup", + "testMs": 14937.6, + "data": { + "code": "Space" + } + }, + { + "type": "timer", + "testMs": 15006, + "data": { + "event": "step", + "timer": 15, + "drift": 6 + } + }, + { + "type": "timer", + "testMs": 15007, + "data": { + "event": "end", + "timer": 15, + "date": 1791473720140 + } + } + ], + "context": { + "targetWords": [ + "into ", + "over ", + "have ", + "look ", + "here ", + "not ", + "mean ", + "for ", + "most ", + "want ", + "lead ", + "much ", + "off ", + "think ", + "year ", + "through ", + "to ", + "off ", + "head ", + "here ", + "also ", + "life ", + "end ", + "make ", + "would ", + "real ", + "first ", + "know ", + "into ", + "give ", + "general ", + "help ", + "own ", + "state ", + "on ", + "follow ", + "fact ", + "which ", + "develop ", + "must ", + "about ", + "keep ", + "but ", + "down ", + "against ", + "make ", + "since ", + "time ", + "begin ", + "house ", + "after ", + "so ", + "call ", + "child ", + "change ", + "word ", + "where ", + "get ", + "up ", + "day ", + "point ", + "must ", + "help ", + "through ", + "out ", + "he ", + "mean ", + "fact ", + "public ", + "just ", + "these ", + "during ", + "develop ", + "down ", + "ask ", + "general ", + "small ", + "great ", + "both ", + "order ", + "do ", + "man ", + "come ", + "increase ", + "show ", + "the ", + "down ", + "one ", + "do ", + "change ", + "with ", + "the ", + "right ", + "can ", + "down ", + "present ", + "new ", + "seem ", + "under ", + "you ", + "any ", + "leave ", + "large ", + "line ", + "under ", + "interest ", + "child ", + "know ", + "way ", + "general ", + "set ", + "old ", + "become ", + "interest ", + "last " + ], + "mode": "time", + "mode2": "15", + "koreanStatus": false, + "bailedOut": false + } + }, + "completedEvent": { + "wpm": 54.36, + "rawWpm": 59.96, + "charStats": [68, 3, 2, 0], + "charTotal": 75, + "acc": 93.33, + "language": "english", + "testDuration": 15.01, + "lastKeyToEnd": 118.4, + "startToFirstKey": 0, + "afkDuration": 1, + "tags": [], + "punctuation": false, + "numbers": false, + "lazyMode": false, + "timestamp": 1791473720263, + "mode": "time", + "mode2": "15", + "bailedOut": false, + "funbox": [], + "difficulty": "normal", + "blindMode": false, + "stopOnLetter": false, + "restartCount": 0, + "incompleteTests": [], + "incompleteTestSeconds": 0, + "consistency": 64.99, + "wpmConsistency": 81.89, + "keyConsistency": 7.22, + "chartData": { + "wpm": [84, 72, 72, 60, 53, 54, 57, 50, 47, 47, 49, 51, 53, 53, 54], + "burst": [84, 60, 72, 60, 72, 60, 72, 0, 24, 48, 72, 72, 72, 60, 72], + "err": [0, 0, 0, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + }, + "keySpacing": [ + 135.2, 183.90000000000003, 147.79999999999995, 121.30000000000007, + 227.29999999999995, 176.79999999999995, 184.29999999999995, + 128.9000000000001, 121.5, 287.5, 154.29999999999995, 196.60000000000014, + 200.4000000000001, 139.0999999999999, 242.4000000000001, 179, + 87.89999999999964, 192.9000000000001, 173.5999999999999, + 298.3000000000002, 182.0999999999999, 158, 101.5, 154.9000000000001, + 137.10000000000036, 152.59999999999945, 287, 171.69999999999982, + 150.30000000000018, 190.80000000000018, 299.6999999999998, + 182.40000000000055, 98.5, 192.39999999999964, 157.19999999999982, + 227.30000000000018, 159.80000000000018, 181.5, 203.39999999999964, + 1901.5999999999995, 98.20000000000073, 108.60000000000036, + 183.6999999999989, 205.90000000000146, 372, 195, 188.79999999999927, + 164.79999999999927, 136.5, 222.40000000000146, 153.89999999999964, + 198.10000000000036, 192.09999999999854, 140.90000000000146, + 241.89999999999964, 104.29999999999927, 205.20000000000073, + 113.60000000000036, 109.89999999999964, 288.5, 171.5, 105.29999999999927, + 104.30000000000109, 288.09999999999854, 177, 204, 205.40000000000146, + 136.59999999999854, 141.20000000000073, 235.29999999999927, + 90.30000000000109, 184.5, 143.39999999999964, 208.60000000000036 + ], + "keyDuration": [ + 59.6, 48.400000000000006, 92.79999999999995, 58.39999999999998, + 100.89999999999998, 48.700000000000045, 90.40000000000009, + 134.20000000000005, 115.79999999999995, 116.5, 132.0999999999999, + 40.299999999999955, 73.29999999999973, 126.39999999999964, + 121.19999999999982, 81, 37, 92.60000000000036, 106.20000000000027, + 125.40000000000009, 103, 134.30000000000018, 107.59999999999991, + 80.90000000000009, 132.30000000000018, 92.39999999999964, + 102.40000000000055, 80, 118.90000000000055, 144.80000000000018, + 119.30000000000018, 145.5, 46.099999999999454, 98.5, 76.60000000000036, + 70.80000000000018, 104.30000000000018, 82.80000000000018, + 58.30000000000018, 111.10000000000036, 124.5, 83.5, 117.89999999999964, + 106.5, 150, 105.5, 146.59999999999854, 74.5, 144.5, 105.5, + 108.39999999999964, 70.39999999999964, 46.399999999999636, + 49.70000000000073, 137.39999999999964, 111.5, 68.80000000000109, + 99.89999999999964, 40.399999999999636, 110.39999999999964, + 130.10000000000036, 103.29999999999927, 21.899999999999636, + 106.09999999999854, 104.30000000000109, 75.30000000000109, + 49.80000000000109, 60, 94.70000000000073, 100.89999999999964, + 111.40000000000146, 90.10000000000036, 74.69999999999891, + 80.70000000000073, 49 + ], + "keyOverlap": 74, + "client": "web" + } +} diff --git a/packages/typing-fixtures/reference/keystrokes/words-10-clean.json b/packages/typing-fixtures/reference/keystrokes/words-10-clean.json new file mode 100644 index 000000000..ce62aaa09 --- /dev/null +++ b/packages/typing-fixtures/reference/keystrokes/words-10-clean.json @@ -0,0 +1,1560 @@ +{ + "name": "words-10-clean", + "description": "words 10, no mistakes, overlapping keys", + "recordedWith": "web", + "config": { + "mode": "words", + "time": 30, + "words": 10, + "language": "english", + "punctuation": false, + "numbers": false, + "lazyMode": false, + "funbox": [], + "difficulty": "normal", + "blindMode": false, + "stopOnError": "off", + "confidenceMode": "off", + "freedomMode": false, + "strictSpace": false, + "quickEnd": false, + "deleteOnError": "off", + "oppositeShiftMode": "off", + "britishEnglish": false, + "quoteLength": [1] + }, + "keystrokes": [ + { + "kind": "key", + "key": "o", + "gap": 98, + "hold": 40 + }, + { + "kind": "key", + "key": "f", + "gap": 92, + "hold": 148 + }, + { + "kind": "key", + "key": " ", + "gap": 118, + "hold": 71 + }, + { + "kind": "key", + "key": "b", + "gap": 97, + "hold": 119 + }, + { + "kind": "key", + "key": "y", + "gap": 86, + "hold": 149 + }, + { + "kind": "key", + "key": " ", + "gap": 87, + "hold": 94 + }, + { + "kind": "key", + "key": "c", + "gap": 68, + "hold": 84 + }, + { + "kind": "key", + "key": "o", + "gap": 75, + "hold": 57 + }, + { + "kind": "key", + "key": "m", + "gap": 89, + "hold": 47 + }, + { + "kind": "key", + "key": "e", + "gap": 84, + "hold": 124 + }, + { + "kind": "key", + "key": " ", + "gap": 77, + "hold": 61 + }, + { + "kind": "key", + "key": "a", + "gap": 63, + "hold": 87 + }, + { + "kind": "key", + "key": "s", + "gap": 95, + "hold": 128 + }, + { + "kind": "key", + "key": " ", + "gap": 78, + "hold": 63 + }, + { + "kind": "key", + "key": "c", + "gap": 99, + "hold": 100 + }, + { + "kind": "key", + "key": "a", + "gap": 106, + "hold": 61 + }, + { + "kind": "key", + "key": "n", + "gap": 103, + "hold": 90 + }, + { + "kind": "key", + "key": " ", + "gap": 106, + "hold": 149 + }, + { + "kind": "key", + "key": "n", + "gap": 74, + "hold": 123 + }, + { + "kind": "key", + "key": "e", + "gap": 69, + "hold": 76 + }, + { + "kind": "key", + "key": "v", + "gap": 85, + "hold": 67 + }, + { + "kind": "key", + "key": "e", + "gap": 69, + "hold": 120 + }, + { + "kind": "key", + "key": "r", + "gap": 95, + "hold": 54 + }, + { + "kind": "key", + "key": " ", + "gap": 76, + "hold": 45 + }, + { + "kind": "key", + "key": "t", + "gap": 97, + "hold": 135 + }, + { + "kind": "key", + "key": "h", + "gap": 74, + "hold": 149 + }, + { + "kind": "key", + "key": "e", + "gap": 103, + "hold": 84 + }, + { + "kind": "key", + "key": "y", + "gap": 78, + "hold": 114 + }, + { + "kind": "key", + "key": " ", + "gap": 117, + "hold": 142 + }, + { + "kind": "key", + "key": "b", + "gap": 102, + "hold": 141 + }, + { + "kind": "key", + "key": "y", + "gap": 110, + "hold": 143 + }, + { + "kind": "key", + "key": " ", + "gap": 67, + "hold": 44 + }, + { + "kind": "key", + "key": "s", + "gap": 112, + "hold": 72 + }, + { + "kind": "key", + "key": "y", + "gap": 113, + "hold": 96 + }, + { + "kind": "key", + "key": "s", + "gap": 116, + "hold": 117 + }, + { + "kind": "key", + "key": "t", + "gap": 70, + "hold": 114 + }, + { + "kind": "key", + "key": "e", + "gap": 117, + "hold": 55 + }, + { + "kind": "key", + "key": "m", + "gap": 119, + "hold": 58 + }, + { + "kind": "key", + "key": " ", + "gap": 83, + "hold": 53 + }, + { + "kind": "key", + "key": "m", + "gap": 84, + "hold": 76 + }, + { + "kind": "key", + "key": "o", + "gap": 99, + "hold": 96 + }, + { + "kind": "key", + "key": "s", + "gap": 92, + "hold": 48 + }, + { + "kind": "key", + "key": "t", + "gap": 108, + "hold": 134 + } + ], + "eventLog": { + "version": 1, + "events": [ + { + "type": "keydown", + "testMs": -4.7, + "data": { + "code": "KeyO" + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": "o", + "correct": true, + "wordIndex": 0, + "charIndex": 0, + "inputValue": "o" + } + }, + { + "type": "timer", + "testMs": 0, + "data": { + "event": "start", + "timer": 0, + "date": 1791473675188 + } + }, + { + "type": "keyup", + "testMs": 32.3, + "data": { + "code": "KeyO" + } + }, + { + "type": "keydown", + "testMs": 87.5, + "data": { + "code": "KeyF" + } + }, + { + "type": "input", + "testMs": 88.7, + "data": { + "inputType": "insertText", + "data": "f", + "correct": true, + "wordIndex": 0, + "charIndex": 1, + "inputValue": "of" + } + }, + { + "type": "keydown", + "testMs": 200.8, + "data": { + "code": "Space" + } + }, + { + "type": "input", + "testMs": 202.9, + "data": { + "inputType": "insertText", + "data": " ", + "correct": true, + "wordIndex": 0, + "charIndex": 2, + "inputValue": "of ", + "commitsWord": true + } + }, + { + "type": "keyup", + "testMs": 231.3, + "data": { + "code": "KeyF" + } + }, + { + "type": "keyup", + "testMs": 272.5, + "data": { + "code": "Space" + } + }, + { + "type": "keydown", + "testMs": 374.5, + "data": { + "code": "KeyB" + } + }, + { + "type": "input", + "testMs": 375.1, + "data": { + "inputType": "insertText", + "data": "b", + "correct": true, + "wordIndex": 1, + "charIndex": 0, + "inputValue": "b" + } + }, + { + "type": "keydown", + "testMs": 461.5, + "data": { + "code": "KeyY" + } + }, + { + "type": "input", + "testMs": 462.4, + "data": { + "inputType": "insertText", + "data": "y", + "correct": true, + "wordIndex": 1, + "charIndex": 1, + "inputValue": "by" + } + }, + { + "type": "keyup", + "testMs": 494.2, + "data": { + "code": "KeyB" + } + }, + { + "type": "keydown", + "testMs": 548.9, + "data": { + "code": "Space" + } + }, + { + "type": "input", + "testMs": 550.1, + "data": { + "inputType": "insertText", + "data": " ", + "correct": true, + "wordIndex": 1, + "charIndex": 2, + "inputValue": "by ", + "commitsWord": true + } + }, + { + "type": "keyup", + "testMs": 611.2, + "data": { + "code": "KeyY" + } + }, + { + "type": "keyup", + "testMs": 641.7, + "data": { + "code": "Space" + } + }, + { + "type": "keydown", + "testMs": 714.8, + "data": { + "code": "KeyC" + } + }, + { + "type": "input", + "testMs": 715.6, + "data": { + "inputType": "insertText", + "data": "c", + "correct": true, + "wordIndex": 2, + "charIndex": 0, + "inputValue": "c" + } + }, + { + "type": "keydown", + "testMs": 790.5, + "data": { + "code": "KeyO" + } + }, + { + "type": "input", + "testMs": 791.5, + "data": { + "inputType": "insertText", + "data": "o", + "correct": true, + "wordIndex": 2, + "charIndex": 1, + "inputValue": "co" + } + }, + { + "type": "keyup", + "testMs": 799.3, + "data": { + "code": "KeyC" + } + }, + { + "type": "keyup", + "testMs": 847, + "data": { + "code": "KeyO" + } + }, + { + "type": "keydown", + "testMs": 879.2, + "data": { + "code": "KeyM" + } + }, + { + "type": "input", + "testMs": 880.3, + "data": { + "inputType": "insertText", + "data": "m", + "correct": true, + "wordIndex": 2, + "charIndex": 2, + "inputValue": "com" + } + }, + { + "type": "keyup", + "testMs": 925.8, + "data": { + "code": "KeyM" + } + }, + { + "type": "keydown", + "testMs": 963.6, + "data": { + "code": "KeyE" + } + }, + { + "type": "input", + "testMs": 965, + "data": { + "inputType": "insertText", + "data": "e", + "correct": true, + "wordIndex": 2, + "charIndex": 3, + "inputValue": "come" + } + }, + { + "type": "timer", + "testMs": 1003.3, + "data": { + "event": "step", + "timer": 1, + "drift": 3.3 + } + }, + { + "type": "keydown", + "testMs": 1041.2, + "data": { + "code": "Space" + } + }, + { + "type": "input", + "testMs": 1043.1, + "data": { + "inputType": "insertText", + "data": " ", + "correct": true, + "wordIndex": 2, + "charIndex": 4, + "inputValue": "come ", + "commitsWord": true + } + }, + { + "type": "keyup", + "testMs": 1088.5, + "data": { + "code": "KeyE" + } + }, + { + "type": "keyup", + "testMs": 1101.3, + "data": { + "code": "Space" + } + }, + { + "type": "keydown", + "testMs": 1173.8, + "data": { + "code": "KeyA" + } + }, + { + "type": "input", + "testMs": 1174.7, + "data": { + "inputType": "insertText", + "data": "a", + "correct": true, + "wordIndex": 3, + "charIndex": 0, + "inputValue": "a" + } + }, + { + "type": "keyup", + "testMs": 1260.5, + "data": { + "code": "KeyA" + } + }, + { + "type": "keydown", + "testMs": 1267, + "data": { + "code": "KeyS" + } + }, + { + "type": "input", + "testMs": 1267.7, + "data": { + "inputType": "insertText", + "data": "s", + "correct": true, + "wordIndex": 3, + "charIndex": 1, + "inputValue": "as" + } + }, + { + "type": "keydown", + "testMs": 1347.3, + "data": { + "code": "Space" + } + }, + { + "type": "input", + "testMs": 1348.1, + "data": { + "inputType": "insertText", + "data": " ", + "correct": true, + "wordIndex": 3, + "charIndex": 2, + "inputValue": "as ", + "commitsWord": true + } + }, + { + "type": "keyup", + "testMs": 1396.9, + "data": { + "code": "KeyS" + } + }, + { + "type": "keyup", + "testMs": 1408.6, + "data": { + "code": "Space" + } + }, + { + "type": "keydown", + "testMs": 1516.7, + "data": { + "code": "KeyC" + } + }, + { + "type": "input", + "testMs": 1518.8, + "data": { + "inputType": "insertText", + "data": "c", + "correct": true, + "wordIndex": 4, + "charIndex": 0, + "inputValue": "c" + } + }, + { + "type": "keyup", + "testMs": 1616.5, + "data": { + "code": "KeyC" + } + }, + { + "type": "keydown", + "testMs": 1619.5, + "data": { + "code": "KeyA" + } + }, + { + "type": "input", + "testMs": 1620.4, + "data": { + "inputType": "insertText", + "data": "a", + "correct": true, + "wordIndex": 4, + "charIndex": 1, + "inputValue": "ca" + } + }, + { + "type": "keyup", + "testMs": 1682, + "data": { + "code": "KeyA" + } + }, + { + "type": "keydown", + "testMs": 1724.8, + "data": { + "code": "KeyN" + } + }, + { + "type": "input", + "testMs": 1726.1, + "data": { + "inputType": "insertText", + "data": "n", + "correct": true, + "wordIndex": 4, + "charIndex": 2, + "inputValue": "can" + } + }, + { + "type": "keyup", + "testMs": 1812.6, + "data": { + "code": "KeyN" + } + }, + { + "type": "keydown", + "testMs": 1830.2, + "data": { + "code": "Space" + } + }, + { + "type": "input", + "testMs": 1831.1, + "data": { + "inputType": "insertText", + "data": " ", + "correct": true, + "wordIndex": 4, + "charIndex": 3, + "inputValue": "can ", + "commitsWord": true + } + }, + { + "type": "keyup", + "testMs": 1980.3, + "data": { + "code": "Space" + } + }, + { + "type": "timer", + "testMs": 2003, + "data": { + "event": "step", + "timer": 2, + "drift": 3 + } + }, + { + "type": "keydown", + "testMs": 2067.4, + "data": { + "code": "KeyN" + } + }, + { + "type": "input", + "testMs": 2069.7, + "data": { + "inputType": "insertText", + "data": "n", + "correct": true, + "wordIndex": 5, + "charIndex": 0, + "inputValue": "n" + } + }, + { + "type": "keydown", + "testMs": 2134.6, + "data": { + "code": "KeyE" + } + }, + { + "type": "input", + "testMs": 2136.8, + "data": { + "inputType": "insertText", + "data": "e", + "correct": true, + "wordIndex": 5, + "charIndex": 1, + "inputValue": "ne" + } + }, + { + "type": "keyup", + "testMs": 2190.3, + "data": { + "code": "KeyN" + } + }, + { + "type": "keyup", + "testMs": 2210.4, + "data": { + "code": "KeyE" + } + }, + { + "type": "keydown", + "testMs": 2217.5, + "data": { + "code": "KeyV" + } + }, + { + "type": "input", + "testMs": 2218.8, + "data": { + "inputType": "insertText", + "data": "v", + "correct": true, + "wordIndex": 5, + "charIndex": 2, + "inputValue": "nev" + } + }, + { + "type": "keyup", + "testMs": 2286.6, + "data": { + "code": "KeyV" + } + }, + { + "type": "keydown", + "testMs": 2290.2, + "data": { + "code": "KeyE" + } + }, + { + "type": "input", + "testMs": 2291.5, + "data": { + "inputType": "insertText", + "data": "e", + "correct": true, + "wordIndex": 5, + "charIndex": 3, + "inputValue": "neve" + } + }, + { + "type": "keydown", + "testMs": 2383.6, + "data": { + "code": "KeyR" + } + }, + { + "type": "input", + "testMs": 2385.3, + "data": { + "inputType": "insertText", + "data": "r", + "correct": true, + "wordIndex": 5, + "charIndex": 4, + "inputValue": "never" + } + }, + { + "type": "keyup", + "testMs": 2408.7, + "data": { + "code": "KeyE" + } + }, + { + "type": "keyup", + "testMs": 2438.6, + "data": { + "code": "KeyR" + } + }, + { + "type": "keydown", + "testMs": 2459.2, + "data": { + "code": "Space" + } + }, + { + "type": "input", + "testMs": 2460.6, + "data": { + "inputType": "insertText", + "data": " ", + "correct": true, + "wordIndex": 5, + "charIndex": 5, + "inputValue": "never ", + "commitsWord": true + } + }, + { + "type": "keyup", + "testMs": 2504.7, + "data": { + "code": "Space" + } + }, + { + "type": "keydown", + "testMs": 2613.2, + "data": { + "code": "KeyT" + } + }, + { + "type": "input", + "testMs": 2615.7, + "data": { + "inputType": "insertText", + "data": "t", + "correct": true, + "wordIndex": 6, + "charIndex": 0, + "inputValue": "t" + } + }, + { + "type": "keydown", + "testMs": 2686.8, + "data": { + "code": "KeyH" + } + }, + { + "type": "input", + "testMs": 2688.5, + "data": { + "inputType": "insertText", + "data": "h", + "correct": true, + "wordIndex": 6, + "charIndex": 1, + "inputValue": "th" + } + }, + { + "type": "keyup", + "testMs": 2747.8, + "data": { + "code": "KeyT" + } + }, + { + "type": "keydown", + "testMs": 2787.7, + "data": { + "code": "KeyE" + } + }, + { + "type": "input", + "testMs": 2788.4, + "data": { + "inputType": "insertText", + "data": "e", + "correct": true, + "wordIndex": 6, + "charIndex": 2, + "inputValue": "the" + } + }, + { + "type": "keyup", + "testMs": 2837.8, + "data": { + "code": "KeyH" + } + }, + { + "type": "keydown", + "testMs": 2866.3, + "data": { + "code": "KeyY" + } + }, + { + "type": "input", + "testMs": 2866.9, + "data": { + "inputType": "insertText", + "data": "y", + "correct": true, + "wordIndex": 6, + "charIndex": 3, + "inputValue": "they" + } + }, + { + "type": "keyup", + "testMs": 2870.8, + "data": { + "code": "KeyE" + } + }, + { + "type": "keyup", + "testMs": 2980.4, + "data": { + "code": "KeyY" + } + }, + { + "type": "keydown", + "testMs": 2981.6, + "data": { + "code": "Space" + } + }, + { + "type": "input", + "testMs": 2982.4, + "data": { + "inputType": "insertText", + "data": " ", + "correct": true, + "wordIndex": 6, + "charIndex": 4, + "inputValue": "they ", + "commitsWord": true + } + }, + { + "type": "timer", + "testMs": 3002.9, + "data": { + "event": "step", + "timer": 3, + "drift": 2.9 + } + }, + { + "type": "keyup", + "testMs": 3126.8, + "data": { + "code": "Space" + } + }, + { + "type": "keydown", + "testMs": 3240.7, + "data": { + "code": "KeyB" + } + }, + { + "type": "input", + "testMs": 3243, + "data": { + "inputType": "insertText", + "data": "b", + "correct": true, + "wordIndex": 7, + "charIndex": 0, + "inputValue": "b" + } + }, + { + "type": "keydown", + "testMs": 3351.1, + "data": { + "code": "KeyY" + } + }, + { + "type": "input", + "testMs": 3353.3, + "data": { + "inputType": "insertText", + "data": "y", + "correct": true, + "wordIndex": 7, + "charIndex": 1, + "inputValue": "by" + } + }, + { + "type": "keyup", + "testMs": 3379, + "data": { + "code": "KeyB" + } + }, + { + "type": "keydown", + "testMs": 3418.2, + "data": { + "code": "Space" + } + }, + { + "type": "input", + "testMs": 3420.2, + "data": { + "inputType": "insertText", + "data": " ", + "correct": true, + "wordIndex": 7, + "charIndex": 2, + "inputValue": "by ", + "commitsWord": true + } + }, + { + "type": "keyup", + "testMs": 3460.9, + "data": { + "code": "Space" + } + }, + { + "type": "keyup", + "testMs": 3508.5, + "data": { + "code": "KeyY" + } + }, + { + "type": "keydown", + "testMs": 3634.8, + "data": { + "code": "KeyS" + } + }, + { + "type": "input", + "testMs": 3636.7, + "data": { + "inputType": "insertText", + "data": "s", + "correct": true, + "wordIndex": 8, + "charIndex": 0, + "inputValue": "s" + } + }, + { + "type": "keyup", + "testMs": 3706.9, + "data": { + "code": "KeyS" + } + }, + { + "type": "keydown", + "testMs": 3748, + "data": { + "code": "KeyY" + } + }, + { + "type": "input", + "testMs": 3750.4, + "data": { + "inputType": "insertText", + "data": "y", + "correct": true, + "wordIndex": 8, + "charIndex": 1, + "inputValue": "sy" + } + }, + { + "type": "keyup", + "testMs": 3844, + "data": { + "code": "KeyY" + } + }, + { + "type": "keydown", + "testMs": 3865.4, + "data": { + "code": "KeyS" + } + }, + { + "type": "input", + "testMs": 3867.5, + "data": { + "inputType": "insertText", + "data": "s", + "correct": true, + "wordIndex": 8, + "charIndex": 2, + "inputValue": "sys" + } + }, + { + "type": "keydown", + "testMs": 3932.6, + "data": { + "code": "KeyT" + } + }, + { + "type": "input", + "testMs": 3933.8, + "data": { + "inputType": "insertText", + "data": "t", + "correct": true, + "wordIndex": 8, + "charIndex": 3, + "inputValue": "syst" + } + }, + { + "type": "keyup", + "testMs": 3980.3, + "data": { + "code": "KeyS" + } + }, + { + "type": "timer", + "testMs": 4003.1, + "data": { + "event": "step", + "timer": 4, + "drift": 3.1 + } + }, + { + "type": "keyup", + "testMs": 4047.6, + "data": { + "code": "KeyT" + } + }, + { + "type": "keydown", + "testMs": 4050.3, + "data": { + "code": "KeyE" + } + }, + { + "type": "input", + "testMs": 4051.9, + "data": { + "inputType": "insertText", + "data": "e", + "correct": true, + "wordIndex": 8, + "charIndex": 4, + "inputValue": "syste" + } + }, + { + "type": "keyup", + "testMs": 4106.7, + "data": { + "code": "KeyE" + } + }, + { + "type": "keydown", + "testMs": 4170.1, + "data": { + "code": "KeyM" + } + }, + { + "type": "input", + "testMs": 4172.3, + "data": { + "inputType": "insertText", + "data": "m", + "correct": true, + "wordIndex": 8, + "charIndex": 5, + "inputValue": "system" + } + }, + { + "type": "keyup", + "testMs": 4226.2, + "data": { + "code": "KeyM" + } + }, + { + "type": "keydown", + "testMs": 4252.6, + "data": { + "code": "Space" + } + }, + { + "type": "input", + "testMs": 4254.8, + "data": { + "inputType": "insertText", + "data": " ", + "correct": true, + "wordIndex": 8, + "charIndex": 6, + "inputValue": "system ", + "commitsWord": true + } + }, + { + "type": "keyup", + "testMs": 4304.1, + "data": { + "code": "Space" + } + }, + { + "type": "keydown", + "testMs": 4397.5, + "data": { + "code": "KeyM" + } + }, + { + "type": "input", + "testMs": 4399.8, + "data": { + "inputType": "insertText", + "data": "m", + "correct": true, + "wordIndex": 9, + "charIndex": 0, + "inputValue": "m", + "lastWord": true + } + }, + { + "type": "keyup", + "testMs": 4471.9, + "data": { + "code": "KeyM" + } + }, + { + "type": "keydown", + "testMs": 4496.7, + "data": { + "code": "KeyO" + } + }, + { + "type": "input", + "testMs": 4498.5, + "data": { + "inputType": "insertText", + "data": "o", + "correct": true, + "wordIndex": 9, + "charIndex": 1, + "inputValue": "mo", + "lastWord": true + } + }, + { + "type": "keydown", + "testMs": 4588.7, + "data": { + "code": "KeyS" + } + }, + { + "type": "input", + "testMs": 4590.5, + "data": { + "inputType": "insertText", + "data": "s", + "correct": true, + "wordIndex": 9, + "charIndex": 2, + "inputValue": "mos", + "lastWord": true + } + }, + { + "type": "keyup", + "testMs": 4592.2, + "data": { + "code": "KeyO" + } + }, + { + "type": "keyup", + "testMs": 4635.1, + "data": { + "code": "KeyS" + } + }, + { + "type": "keydown", + "testMs": 4695.1, + "data": { + "code": "KeyT" + } + }, + { + "type": "input", + "testMs": 4697.1, + "data": { + "inputType": "insertText", + "data": "t", + "correct": true, + "wordIndex": 9, + "charIndex": 3, + "inputValue": "most", + "lastWord": true + } + }, + { + "type": "timer", + "testMs": 4698.4, + "data": { + "event": "end", + "timer": 4, + "date": 1791473679885 + } + }, + { + "type": "keyup", + "testMs": 4799.83, + "data": { + "code": "KeyT", + "estimated": true + } + } + ], + "context": { + "targetWords": [ + "of ", + "by ", + "come ", + "as ", + "can ", + "never ", + "they ", + "by ", + "system ", + "most" + ], + "mode": "words", + "mode2": "10", + "koreanStatus": false, + "bailedOut": false + } + }, + "completedEvent": { + "wpm": 109.79, + "rawWpm": 109.79, + "charStats": [43, 0, 0, 0], + "charTotal": 43, + "acc": 100, + "language": "english", + "testDuration": 4.7, + "lastKeyToEnd": 3.3, + "startToFirstKey": 0, + "afkDuration": 0, + "tags": [], + "punctuation": false, + "numbers": false, + "lazyMode": false, + "timestamp": 1791473680017, + "mode": "words", + "mode2": "10", + "bailedOut": false, + "funbox": [], + "difficulty": "normal", + "blindMode": false, + "stopOnLetter": false, + "restartCount": 0, + "incompleteTests": [], + "incompleteTestSeconds": 0, + "consistency": 84.03, + "wpmConsistency": 95.73, + "keyConsistency": 59.8, + "chartData": { + "wpm": [120, 108, 116, 108, 110], + "burst": [120, 96, 132, 84, 120], + "err": [0, 0, 0, 0, 0] + }, + "keySpacing": [ + 87.5, 113.30000000000001, 173.7, 87, 87.39999999999998, + 165.89999999999998, 75.70000000000005, 88.70000000000005, + 84.39999999999998, 77.60000000000002, 132.5999999999999, + 93.20000000000005, 80.29999999999995, 169.4000000000001, + 102.79999999999995, 105.29999999999995, 105.40000000000009, + 237.20000000000005, 67.19999999999982, 82.90000000000009, + 72.69999999999982, 93.40000000000009, 75.59999999999991, 154, + 73.60000000000036, 100.89999999999964, 78.60000000000036, + 115.29999999999973, 259.0999999999999, 110.40000000000009, + 67.09999999999991, 216.60000000000036, 113.19999999999982, + 117.40000000000009, 67.19999999999982, 117.70000000000027, + 119.80000000000018, 82.5, 144.89999999999964, 99.19999999999982, 92, + 106.40000000000055 + ], + "keyDuration": [ + 37, 143.8, 71.69999999999999, 119.69999999999999, 149.70000000000005, + 92.80000000000007, 84.5, 56.5, 46.59999999999991, 124.89999999999998, + 60.09999999999991, 86.70000000000005, 129.9000000000001, + 61.299999999999955, 99.79999999999995, 62.5, 87.79999999999995, + 150.0999999999999, 122.90000000000009, 75.80000000000018, + 69.09999999999991, 118.5, 55, 45.5, 134.60000000000036, 151, + 83.10000000000036, 114.09999999999991, 145.20000000000027, + 138.30000000000018, 157.4000000000001, 42.70000000000027, + 72.09999999999991, 96, 114.90000000000009, 115, 56.399999999999636, + 56.099999999999454, 51.5, 74.39999999999964, 95.5, 46.400000000000546, + 104.72999999999956 + ], + "keyOverlap": 549.4, + "client": "web" + } +} diff --git a/packages/typing-fixtures/reference/keystrokes/words-10-errors.json b/packages/typing-fixtures/reference/keystrokes/words-10-errors.json new file mode 100644 index 000000000..b37157a37 --- /dev/null +++ b/packages/typing-fixtures/reference/keystrokes/words-10-errors.json @@ -0,0 +1,2152 @@ +{ + "name": "words-10-errors", + "description": "words 10 with a corrected typo, extra letters, a skipped word, word delete and a backtrack", + "recordedWith": "web", + "config": { + "mode": "words", + "time": 30, + "words": 10, + "language": "english", + "punctuation": false, + "numbers": false, + "lazyMode": false, + "funbox": [], + "difficulty": "normal", + "blindMode": false, + "stopOnError": "off", + "confidenceMode": "off", + "freedomMode": false, + "strictSpace": false, + "quickEnd": false, + "deleteOnError": "off", + "oppositeShiftMode": "off", + "britishEnglish": false, + "quoteLength": [1] + }, + "keystrokes": [ + { + "kind": "key", + "key": "a", + "gap": 178, + "hold": 76 + }, + { + "kind": "key", + "key": "l", + "gap": 124, + "hold": 99 + }, + { + "kind": "key", + "key": "l", + "gap": 195, + "hold": 109 + }, + { + "kind": "key", + "key": " ", + "gap": 150, + "hold": 79 + }, + { + "kind": "key", + "key": "m", + "gap": 184, + "hold": 53 + }, + { + "kind": "key", + "key": "q", + "gap": 184, + "hold": 62 + }, + { + "kind": "key", + "key": "Backspace", + "gap": 141, + "hold": 132 + }, + { + "kind": "key", + "key": "a", + "gap": 160, + "hold": 56 + }, + { + "kind": "key", + "key": "n", + "gap": 171, + "hold": 80 + }, + { + "kind": "key", + "key": " ", + "gap": 207, + "hold": 44 + }, + { + "kind": "key", + "key": "w", + "gap": 200, + "hold": 47 + }, + { + "kind": "key", + "key": "o", + "gap": 105, + "hold": 61 + }, + { + "kind": "key", + "key": "u", + "gap": 91, + "hold": 143 + }, + { + "kind": "key", + "key": "l", + "gap": 144, + "hold": 110 + }, + { + "kind": "key", + "key": "d", + "gap": 209, + "hold": 66 + }, + { + "kind": "key", + "key": " ", + "gap": 146, + "hold": 78 + }, + { + "kind": "key", + "key": "s", + "gap": 96, + "hold": 97 + }, + { + "kind": "key", + "key": "c", + "gap": 101, + "hold": 46 + }, + { + "kind": "key", + "key": "h", + "gap": 205, + "hold": 129 + }, + { + "kind": "key", + "key": "o", + "gap": 90, + "hold": 148 + }, + { + "kind": "key", + "key": "o", + "gap": 99, + "hold": 147 + }, + { + "kind": "key", + "key": "l", + "gap": 153, + "hold": 61 + }, + { + "kind": "key", + "key": "x", + "gap": 171, + "hold": 46 + }, + { + "kind": "key", + "key": "x", + "gap": 183, + "hold": 140 + }, + { + "kind": "key", + "key": " ", + "gap": 189, + "hold": 89 + }, + { + "kind": "key", + "key": "w", + "gap": 204, + "hold": 48 + }, + { + "kind": "key", + "key": "h", + "gap": 176, + "hold": 148 + }, + { + "kind": "key", + "key": "e", + "gap": 191, + "hold": 111 + }, + { + "kind": "key", + "key": "r", + "gap": 153, + "hold": 44 + }, + { + "kind": "key", + "key": "e", + "gap": 192, + "hold": 132 + }, + { + "kind": "key", + "key": " ", + "gap": 156, + "hold": 74 + }, + { + "kind": "key", + "key": "d", + "gap": 198, + "hold": 142 + }, + { + "kind": "key", + "key": "a", + "gap": 144, + "hold": 78 + }, + { + "kind": "key", + "key": "Alt+Backspace", + "gap": 92, + "hold": 145 + }, + { + "kind": "key", + "key": "d", + "gap": 128, + "hold": 88 + }, + { + "kind": "key", + "key": "a", + "gap": 186, + "hold": 135 + }, + { + "kind": "key", + "key": "y", + "gap": 200, + "hold": 87 + }, + { + "kind": "key", + "key": " ", + "gap": 115, + "hold": 111 + }, + { + "kind": "key", + "key": "d", + "gap": 165, + "hold": 118 + }, + { + "kind": "key", + "key": " ", + "gap": 203, + "hold": 63 + }, + { + "kind": "key", + "key": "Backspace", + "gap": 123, + "hold": 78 + }, + { + "kind": "key", + "key": "o", + "gap": 154, + "hold": 43 + }, + { + "kind": "key", + "key": " ", + "gap": 125, + "hold": 131 + }, + { + "kind": "key", + "key": "n", + "gap": 110, + "hold": 125 + }, + { + "kind": "key", + "key": "e", + "gap": 137, + "hold": 86 + }, + { + "kind": "key", + "key": "v", + "gap": 137, + "hold": 133 + }, + { + "kind": "key", + "key": "e", + "gap": 147, + "hold": 149 + }, + { + "kind": "key", + "key": "r", + "gap": 121, + "hold": 49 + }, + { + "kind": "key", + "key": " ", + "gap": 175, + "hold": 42 + }, + { + "kind": "key", + "key": "w", + "gap": 178, + "hold": 126 + }, + { + "kind": "key", + "key": "e", + "gap": 168, + "hold": 72 + }, + { + "kind": "key", + "key": "l", + "gap": 129, + "hold": 112 + }, + { + "kind": "key", + "key": "l", + "gap": 103, + "hold": 61 + }, + { + "kind": "key", + "key": " ", + "gap": 178, + "hold": 147 + }, + { + "kind": "key", + "key": "a", + "gap": 166, + "hold": 53 + }, + { + "kind": "key", + "key": "g", + "gap": 159, + "hold": 144 + }, + { + "kind": "key", + "key": "a", + "gap": 93, + "hold": 104 + }, + { + "kind": "key", + "key": "i", + "gap": 152, + "hold": 119 + }, + { + "kind": "key", + "key": "n", + "gap": 192, + "hold": 87 + }, + { + "kind": "key", + "key": "s", + "gap": 151, + "hold": 47 + }, + { + "kind": "key", + "key": "t", + "gap": 194, + "hold": 89 + } + ], + "eventLog": { + "version": 1, + "events": [ + { + "type": "keydown", + "testMs": -5.5, + "data": { + "code": "KeyA" + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": "a", + "correct": true, + "wordIndex": 0, + "charIndex": 0, + "inputValue": "a" + } + }, + { + "type": "timer", + "testMs": 0, + "data": { + "event": "start", + "timer": 0, + "date": 1791473691137 + } + }, + { + "type": "keyup", + "testMs": 66.9, + "data": { + "code": "KeyA" + } + }, + { + "type": "keydown", + "testMs": 115, + "data": { + "code": "KeyL" + } + }, + { + "type": "input", + "testMs": 116, + "data": { + "inputType": "insertText", + "data": "l", + "correct": true, + "wordIndex": 0, + "charIndex": 1, + "inputValue": "al" + } + }, + { + "type": "keyup", + "testMs": 214.4, + "data": { + "code": "KeyL" + } + }, + { + "type": "keydown", + "testMs": 308.7, + "data": { + "code": "KeyL" + } + }, + { + "type": "input", + "testMs": 309.9, + "data": { + "inputType": "insertText", + "data": "l", + "correct": true, + "wordIndex": 0, + "charIndex": 2, + "inputValue": "all" + } + }, + { + "type": "keyup", + "testMs": 419.7, + "data": { + "code": "KeyL" + } + }, + { + "type": "keydown", + "testMs": 460.3, + "data": { + "code": "Space" + } + }, + { + "type": "input", + "testMs": 462.6, + "data": { + "inputType": "insertText", + "data": " ", + "correct": true, + "wordIndex": 0, + "charIndex": 3, + "inputValue": "all ", + "commitsWord": true + } + }, + { + "type": "keyup", + "testMs": 540.7, + "data": { + "code": "Space" + } + }, + { + "type": "keydown", + "testMs": 737.7, + "data": { + "code": "KeyM" + } + }, + { + "type": "input", + "testMs": 740.2, + "data": { + "inputType": "insertText", + "data": "m", + "correct": true, + "wordIndex": 1, + "charIndex": 0, + "inputValue": "m" + } + }, + { + "type": "keyup", + "testMs": 789.9, + "data": { + "code": "KeyM" + } + }, + { + "type": "keydown", + "testMs": 919.9, + "data": { + "code": "KeyQ" + } + }, + { + "type": "input", + "testMs": 922.1, + "data": { + "inputType": "insertText", + "data": "q", + "correct": false, + "wordIndex": 1, + "charIndex": 1, + "inputValue": "mq" + } + }, + { + "type": "keyup", + "testMs": 981.4, + "data": { + "code": "KeyQ" + } + }, + { + "type": "timer", + "testMs": 1004.8, + "data": { + "event": "step", + "timer": 1, + "drift": 4.8 + } + }, + { + "type": "input", + "testMs": 1064.3, + "data": { + "inputType": "deleteContentBackward", + "wordIndex": 1, + "charIndex": 2, + "inputValue": "m" + } + }, + { + "type": "keydown", + "testMs": 1220.5, + "data": { + "code": "KeyA" + } + }, + { + "type": "input", + "testMs": 1223.2, + "data": { + "inputType": "insertText", + "data": "a", + "correct": true, + "wordIndex": 1, + "charIndex": 1, + "inputValue": "ma" + } + }, + { + "type": "keyup", + "testMs": 1277.6, + "data": { + "code": "KeyA" + } + }, + { + "type": "keydown", + "testMs": 1391.9, + "data": { + "code": "KeyN" + } + }, + { + "type": "input", + "testMs": 1394.3, + "data": { + "inputType": "insertText", + "data": "n", + "correct": true, + "wordIndex": 1, + "charIndex": 2, + "inputValue": "man" + } + }, + { + "type": "keyup", + "testMs": 1470.3, + "data": { + "code": "KeyN" + } + }, + { + "type": "keydown", + "testMs": 1598.8, + "data": { + "code": "Space" + } + }, + { + "type": "input", + "testMs": 1601.4, + "data": { + "inputType": "insertText", + "data": " ", + "correct": true, + "wordIndex": 1, + "charIndex": 3, + "inputValue": "man ", + "commitsWord": true + } + }, + { + "type": "keyup", + "testMs": 1642.5, + "data": { + "code": "Space" + } + }, + { + "type": "keydown", + "testMs": 1850, + "data": { + "code": "KeyW" + } + }, + { + "type": "input", + "testMs": 1851.2, + "data": { + "inputType": "insertText", + "data": "w", + "correct": true, + "wordIndex": 2, + "charIndex": 0, + "inputValue": "w" + } + }, + { + "type": "keyup", + "testMs": 1896, + "data": { + "code": "KeyW" + } + }, + { + "type": "keydown", + "testMs": 1956.8, + "data": { + "code": "KeyO" + } + }, + { + "type": "input", + "testMs": 1958.6, + "data": { + "inputType": "insertText", + "data": "o", + "correct": true, + "wordIndex": 2, + "charIndex": 1, + "inputValue": "wo" + } + }, + { + "type": "timer", + "testMs": 2004.4, + "data": { + "event": "step", + "timer": 2, + "drift": 4.4 + } + }, + { + "type": "keyup", + "testMs": 2016.7, + "data": { + "code": "KeyO" + } + }, + { + "type": "keydown", + "testMs": 2047.9, + "data": { + "code": "KeyU" + } + }, + { + "type": "input", + "testMs": 2049.6, + "data": { + "inputType": "insertText", + "data": "u", + "correct": true, + "wordIndex": 2, + "charIndex": 2, + "inputValue": "wou" + } + }, + { + "type": "keyup", + "testMs": 2191, + "data": { + "code": "KeyU" + } + }, + { + "type": "keydown", + "testMs": 2193.4, + "data": { + "code": "KeyL" + } + }, + { + "type": "input", + "testMs": 2195.3, + "data": { + "inputType": "insertText", + "data": "l", + "correct": true, + "wordIndex": 2, + "charIndex": 3, + "inputValue": "woul" + } + }, + { + "type": "keyup", + "testMs": 2301.1, + "data": { + "code": "KeyL" + } + }, + { + "type": "keydown", + "testMs": 2400.7, + "data": { + "code": "KeyD" + } + }, + { + "type": "input", + "testMs": 2403.2, + "data": { + "inputType": "insertText", + "data": "d", + "correct": true, + "wordIndex": 2, + "charIndex": 4, + "inputValue": "would" + } + }, + { + "type": "keyup", + "testMs": 2466.9, + "data": { + "code": "KeyD" + } + }, + { + "type": "keydown", + "testMs": 2546.9, + "data": { + "code": "Space" + } + }, + { + "type": "input", + "testMs": 2549.2, + "data": { + "inputType": "insertText", + "data": " ", + "correct": true, + "wordIndex": 2, + "charIndex": 5, + "inputValue": "would ", + "commitsWord": true + } + }, + { + "type": "keyup", + "testMs": 2623.3, + "data": { + "code": "Space" + } + }, + { + "type": "keydown", + "testMs": 2730.8, + "data": { + "code": "KeyS" + } + }, + { + "type": "input", + "testMs": 2733.2, + "data": { + "inputType": "insertText", + "data": "s", + "correct": true, + "wordIndex": 3, + "charIndex": 0, + "inputValue": "s" + } + }, + { + "type": "keyup", + "testMs": 2826.5, + "data": { + "code": "KeyS" + } + }, + { + "type": "keydown", + "testMs": 2828.1, + "data": { + "code": "KeyC" + } + }, + { + "type": "input", + "testMs": 2829.2, + "data": { + "inputType": "insertText", + "data": "c", + "correct": true, + "wordIndex": 3, + "charIndex": 1, + "inputValue": "sc" + } + }, + { + "type": "keyup", + "testMs": 2876.3, + "data": { + "code": "KeyC" + } + }, + { + "type": "timer", + "testMs": 3004.4, + "data": { + "event": "step", + "timer": 3, + "drift": 4.4 + } + }, + { + "type": "keydown", + "testMs": 3035.6, + "data": { + "code": "KeyH" + } + }, + { + "type": "input", + "testMs": 3038, + "data": { + "inputType": "insertText", + "data": "h", + "correct": true, + "wordIndex": 3, + "charIndex": 2, + "inputValue": "sch" + } + }, + { + "type": "keydown", + "testMs": 3126, + "data": { + "code": "KeyO" + } + }, + { + "type": "input", + "testMs": 3127.3, + "data": { + "inputType": "insertText", + "data": "o", + "correct": true, + "wordIndex": 3, + "charIndex": 3, + "inputValue": "scho" + } + }, + { + "type": "keyup", + "testMs": 3163.9, + "data": { + "code": "KeyH" + } + }, + { + "type": "keyup", + "testMs": 3225.5, + "data": { + "code": "KeyO" + } + }, + { + "type": "keydown", + "testMs": 3228.1, + "data": { + "code": "KeyO" + } + }, + { + "type": "input", + "testMs": 3229.3, + "data": { + "inputType": "insertText", + "data": "o", + "correct": true, + "wordIndex": 3, + "charIndex": 4, + "inputValue": "schoo" + } + }, + { + "type": "keyup", + "testMs": 3274, + "data": { + "code": "KeyO" + } + }, + { + "type": "keydown", + "testMs": 3373.9, + "data": { + "code": "KeyL" + } + }, + { + "type": "input", + "testMs": 3375, + "data": { + "inputType": "insertText", + "data": "l", + "correct": true, + "wordIndex": 3, + "charIndex": 5, + "inputValue": "school" + } + }, + { + "type": "keyup", + "testMs": 3438.6, + "data": { + "code": "KeyL" + } + }, + { + "type": "keydown", + "testMs": 3549.8, + "data": { + "code": "KeyX" + } + }, + { + "type": "input", + "testMs": 3554.3, + "data": { + "inputType": "insertText", + "data": "x", + "correct": false, + "wordIndex": 3, + "charIndex": 6, + "inputValue": "schoolx" + } + }, + { + "type": "keyup", + "testMs": 3594.6, + "data": { + "code": "KeyX" + } + }, + { + "type": "keydown", + "testMs": 3732.7, + "data": { + "code": "KeyX" + } + }, + { + "type": "input", + "testMs": 3736.7, + "data": { + "inputType": "insertText", + "data": "x", + "correct": false, + "wordIndex": 3, + "charIndex": 7, + "inputValue": "schoolxx" + } + }, + { + "type": "keyup", + "testMs": 3873.6, + "data": { + "code": "KeyX" + } + }, + { + "type": "keydown", + "testMs": 3921.9, + "data": { + "code": "Space" + } + }, + { + "type": "input", + "testMs": 3923.1, + "data": { + "inputType": "insertText", + "data": " ", + "correct": false, + "wordIndex": 3, + "charIndex": 8, + "inputValue": "schoolxx ", + "commitsWord": true + } + }, + { + "type": "timer", + "testMs": 4004.6, + "data": { + "event": "step", + "timer": 4, + "drift": 4.6 + } + }, + { + "type": "keyup", + "testMs": 4009.6, + "data": { + "code": "Space" + } + }, + { + "type": "keydown", + "testMs": 4225.1, + "data": { + "code": "KeyW" + } + }, + { + "type": "input", + "testMs": 4227.4, + "data": { + "inputType": "insertText", + "data": "w", + "correct": true, + "wordIndex": 4, + "charIndex": 0, + "inputValue": "w" + } + }, + { + "type": "keyup", + "testMs": 4273.2, + "data": { + "code": "KeyW" + } + }, + { + "type": "keydown", + "testMs": 4400.7, + "data": { + "code": "KeyH" + } + }, + { + "type": "input", + "testMs": 4402.6, + "data": { + "inputType": "insertText", + "data": "h", + "correct": true, + "wordIndex": 4, + "charIndex": 1, + "inputValue": "wh" + } + }, + { + "type": "keyup", + "testMs": 4548.6, + "data": { + "code": "KeyH" + } + }, + { + "type": "keydown", + "testMs": 4592.3, + "data": { + "code": "KeyE" + } + }, + { + "type": "input", + "testMs": 4594.5, + "data": { + "inputType": "insertText", + "data": "e", + "correct": true, + "wordIndex": 4, + "charIndex": 2, + "inputValue": "whe" + } + }, + { + "type": "keyup", + "testMs": 4700.9, + "data": { + "code": "KeyE" + } + }, + { + "type": "keydown", + "testMs": 4743.9, + "data": { + "code": "KeyR" + } + }, + { + "type": "input", + "testMs": 4746.3, + "data": { + "inputType": "insertText", + "data": "r", + "correct": true, + "wordIndex": 4, + "charIndex": 3, + "inputValue": "wher" + } + }, + { + "type": "keyup", + "testMs": 4788.5, + "data": { + "code": "KeyR" + } + }, + { + "type": "keydown", + "testMs": 4936.8, + "data": { + "code": "KeyE" + } + }, + { + "type": "input", + "testMs": 4938.2, + "data": { + "inputType": "insertText", + "data": "e", + "correct": true, + "wordIndex": 4, + "charIndex": 4, + "inputValue": "where" + } + }, + { + "type": "timer", + "testMs": 5004.6, + "data": { + "event": "step", + "timer": 5, + "drift": 4.6 + } + }, + { + "type": "keyup", + "testMs": 5067.8, + "data": { + "code": "KeyE" + } + }, + { + "type": "keydown", + "testMs": 5094.2, + "data": { + "code": "Space" + } + }, + { + "type": "input", + "testMs": 5095.1, + "data": { + "inputType": "insertText", + "data": " ", + "correct": true, + "wordIndex": 4, + "charIndex": 5, + "inputValue": "where ", + "commitsWord": true + } + }, + { + "type": "keyup", + "testMs": 5165.4, + "data": { + "code": "Space" + } + }, + { + "type": "keydown", + "testMs": 5372.2, + "data": { + "code": "KeyD" + } + }, + { + "type": "input", + "testMs": 5374.5, + "data": { + "inputType": "insertText", + "data": "d", + "correct": true, + "wordIndex": 5, + "charIndex": 0, + "inputValue": "d" + } + }, + { + "type": "keyup", + "testMs": 5514.3, + "data": { + "code": "KeyD" + } + }, + { + "type": "keydown", + "testMs": 5516.6, + "data": { + "code": "KeyA" + } + }, + { + "type": "input", + "testMs": 5518.8, + "data": { + "inputType": "insertText", + "data": "a", + "correct": true, + "wordIndex": 5, + "charIndex": 1, + "inputValue": "da" + } + }, + { + "type": "keyup", + "testMs": 5593.8, + "data": { + "code": "KeyA" + } + }, + { + "type": "input", + "testMs": 5606.5, + "data": { + "inputType": "deleteWordBackward", + "wordIndex": 5, + "charIndex": 2, + "inputValue": "" + } + }, + { + "type": "keydown", + "testMs": 5766.4, + "data": { + "code": "KeyD" + } + }, + { + "type": "input", + "testMs": 5768.8, + "data": { + "inputType": "insertText", + "data": "d", + "correct": true, + "wordIndex": 5, + "charIndex": 0, + "inputValue": "d" + } + }, + { + "type": "keyup", + "testMs": 5855.5, + "data": { + "code": "KeyD" + } + }, + { + "type": "keydown", + "testMs": 5952.6, + "data": { + "code": "KeyA" + } + }, + { + "type": "input", + "testMs": 5954.8, + "data": { + "inputType": "insertText", + "data": "a", + "correct": true, + "wordIndex": 5, + "charIndex": 1, + "inputValue": "da" + } + }, + { + "type": "timer", + "testMs": 6004.6, + "data": { + "event": "step", + "timer": 6, + "drift": 4.6 + } + }, + { + "type": "keyup", + "testMs": 6087.5, + "data": { + "code": "KeyA" + } + }, + { + "type": "keydown", + "testMs": 6153, + "data": { + "code": "KeyY" + } + }, + { + "type": "input", + "testMs": 6155.3, + "data": { + "inputType": "insertText", + "data": "y", + "correct": true, + "wordIndex": 5, + "charIndex": 2, + "inputValue": "day" + } + }, + { + "type": "keyup", + "testMs": 6239.3, + "data": { + "code": "KeyY" + } + }, + { + "type": "keydown", + "testMs": 6266.1, + "data": { + "code": "Space" + } + }, + { + "type": "input", + "testMs": 6267.5, + "data": { + "inputType": "insertText", + "data": " ", + "correct": true, + "wordIndex": 5, + "charIndex": 3, + "inputValue": "day ", + "commitsWord": true + } + }, + { + "type": "keyup", + "testMs": 6377.1, + "data": { + "code": "Space" + } + }, + { + "type": "keydown", + "testMs": 6550.7, + "data": { + "code": "KeyD" + } + }, + { + "type": "input", + "testMs": 6553.1, + "data": { + "inputType": "insertText", + "data": "d", + "correct": true, + "wordIndex": 6, + "charIndex": 0, + "inputValue": "d" + } + }, + { + "type": "keyup", + "testMs": 6669.3, + "data": { + "code": "KeyD" + } + }, + { + "type": "keydown", + "testMs": 6753.4, + "data": { + "code": "Space" + } + }, + { + "type": "input", + "testMs": 6755.7, + "data": { + "inputType": "insertText", + "data": " ", + "correct": false, + "wordIndex": 6, + "charIndex": 1, + "inputValue": "d ", + "commitsWord": true + } + }, + { + "type": "keyup", + "testMs": 6817.2, + "data": { + "code": "Space" + } + }, + { + "type": "input", + "testMs": 6880.7, + "data": { + "inputType": "deleteContentBackward", + "wordIndex": 6, + "charIndex": 1, + "inputValue": "d" + } + }, + { + "type": "timer", + "testMs": 7004.7, + "data": { + "event": "step", + "timer": 7, + "drift": 4.7 + } + }, + { + "type": "keydown", + "testMs": 7030.9, + "data": { + "code": "KeyO" + } + }, + { + "type": "input", + "testMs": 7032.8, + "data": { + "inputType": "insertText", + "data": "o", + "correct": true, + "wordIndex": 6, + "charIndex": 1, + "inputValue": "do" + } + }, + { + "type": "keyup", + "testMs": 7073.9, + "data": { + "code": "KeyO" + } + }, + { + "type": "keydown", + "testMs": 7156.7, + "data": { + "code": "Space" + } + }, + { + "type": "input", + "testMs": 7158.6, + "data": { + "inputType": "insertText", + "data": " ", + "correct": true, + "wordIndex": 6, + "charIndex": 2, + "inputValue": "do ", + "commitsWord": true + } + }, + { + "type": "keyup", + "testMs": 7286.6, + "data": { + "code": "Space" + } + }, + { + "type": "keydown", + "testMs": 7407, + "data": { + "code": "KeyN" + } + }, + { + "type": "input", + "testMs": 7409.1, + "data": { + "inputType": "insertText", + "data": "n", + "correct": true, + "wordIndex": 7, + "charIndex": 0, + "inputValue": "n" + } + }, + { + "type": "keyup", + "testMs": 7531.8, + "data": { + "code": "KeyN" + } + }, + { + "type": "keydown", + "testMs": 7540.9, + "data": { + "code": "KeyE" + } + }, + { + "type": "input", + "testMs": 7542, + "data": { + "inputType": "insertText", + "data": "e", + "correct": true, + "wordIndex": 7, + "charIndex": 1, + "inputValue": "ne" + } + }, + { + "type": "keyup", + "testMs": 7629.3, + "data": { + "code": "KeyE" + } + }, + { + "type": "keydown", + "testMs": 7679.3, + "data": { + "code": "KeyV" + } + }, + { + "type": "input", + "testMs": 7680.4, + "data": { + "inputType": "insertText", + "data": "v", + "correct": true, + "wordIndex": 7, + "charIndex": 2, + "inputValue": "nev" + } + }, + { + "type": "keyup", + "testMs": 7813.2, + "data": { + "code": "KeyV" + } + }, + { + "type": "keydown", + "testMs": 7826.2, + "data": { + "code": "KeyE" + } + }, + { + "type": "input", + "testMs": 7828.2, + "data": { + "inputType": "insertText", + "data": "e", + "correct": true, + "wordIndex": 7, + "charIndex": 3, + "inputValue": "neve" + } + }, + { + "type": "keydown", + "testMs": 7948.1, + "data": { + "code": "KeyR" + } + }, + { + "type": "input", + "testMs": 7949.3, + "data": { + "inputType": "insertText", + "data": "r", + "correct": true, + "wordIndex": 7, + "charIndex": 4, + "inputValue": "never" + } + }, + { + "type": "keyup", + "testMs": 7975.8, + "data": { + "code": "KeyE" + } + }, + { + "type": "keyup", + "testMs": 7996.2, + "data": { + "code": "KeyR" + } + }, + { + "type": "timer", + "testMs": 8004.4, + "data": { + "event": "step", + "timer": 8, + "drift": 4.4 + } + }, + { + "type": "keydown", + "testMs": 8122.9, + "data": { + "code": "Space" + } + }, + { + "type": "input", + "testMs": 8125.2, + "data": { + "inputType": "insertText", + "data": " ", + "correct": true, + "wordIndex": 7, + "charIndex": 5, + "inputValue": "never ", + "commitsWord": true + } + }, + { + "type": "keyup", + "testMs": 8165.3, + "data": { + "code": "Space" + } + }, + { + "type": "keydown", + "testMs": 8356.4, + "data": { + "code": "KeyW" + } + }, + { + "type": "input", + "testMs": 8358.5, + "data": { + "inputType": "insertText", + "data": "w", + "correct": true, + "wordIndex": 8, + "charIndex": 0, + "inputValue": "w" + } + }, + { + "type": "keyup", + "testMs": 8480.6, + "data": { + "code": "KeyW" + } + }, + { + "type": "keydown", + "testMs": 8522, + "data": { + "code": "KeyE" + } + }, + { + "type": "input", + "testMs": 8523.7, + "data": { + "inputType": "insertText", + "data": "e", + "correct": true, + "wordIndex": 8, + "charIndex": 1, + "inputValue": "we" + } + }, + { + "type": "keyup", + "testMs": 8593.5, + "data": { + "code": "KeyE" + } + }, + { + "type": "keydown", + "testMs": 8650.9, + "data": { + "code": "KeyL" + } + }, + { + "type": "input", + "testMs": 8652.8, + "data": { + "inputType": "insertText", + "data": "l", + "correct": true, + "wordIndex": 8, + "charIndex": 2, + "inputValue": "wel" + } + }, + { + "type": "keyup", + "testMs": 8753.4, + "data": { + "code": "KeyL" + } + }, + { + "type": "keydown", + "testMs": 8755, + "data": { + "code": "KeyL" + } + }, + { + "type": "input", + "testMs": 8755.9, + "data": { + "inputType": "insertText", + "data": "l", + "correct": true, + "wordIndex": 8, + "charIndex": 3, + "inputValue": "well" + } + }, + { + "type": "keyup", + "testMs": 8759.8, + "data": { + "code": "KeyL" + } + }, + { + "type": "keydown", + "testMs": 8931.4, + "data": { + "code": "Space" + } + }, + { + "type": "input", + "testMs": 8932.8, + "data": { + "inputType": "insertText", + "data": " ", + "correct": true, + "wordIndex": 8, + "charIndex": 4, + "inputValue": "well ", + "commitsWord": true + } + }, + { + "type": "timer", + "testMs": 9004.7, + "data": { + "event": "step", + "timer": 9, + "drift": 4.7 + } + }, + { + "type": "keyup", + "testMs": 9079.9, + "data": { + "code": "Space" + } + }, + { + "type": "keydown", + "testMs": 9257.3, + "data": { + "code": "KeyA" + } + }, + { + "type": "input", + "testMs": 9259.6, + "data": { + "inputType": "insertText", + "data": "a", + "correct": true, + "wordIndex": 9, + "charIndex": 0, + "inputValue": "a", + "lastWord": true + } + }, + { + "type": "keyup", + "testMs": 9308.9, + "data": { + "code": "KeyA" + } + }, + { + "type": "keydown", + "testMs": 9416.3, + "data": { + "code": "KeyG" + } + }, + { + "type": "input", + "testMs": 9418.3, + "data": { + "inputType": "insertText", + "data": "g", + "correct": true, + "wordIndex": 9, + "charIndex": 1, + "inputValue": "ag", + "lastWord": true + } + }, + { + "type": "keydown", + "testMs": 9509.6, + "data": { + "code": "KeyA" + } + }, + { + "type": "input", + "testMs": 9510.9, + "data": { + "inputType": "insertText", + "data": "a", + "correct": true, + "wordIndex": 9, + "charIndex": 2, + "inputValue": "aga", + "lastWord": true + } + }, + { + "type": "keyup", + "testMs": 9559.5, + "data": { + "code": "KeyG" + } + }, + { + "type": "keyup", + "testMs": 9612.9, + "data": { + "code": "KeyA" + } + }, + { + "type": "keydown", + "testMs": 9661.7, + "data": { + "code": "KeyI" + } + }, + { + "type": "input", + "testMs": 9664.2, + "data": { + "inputType": "insertText", + "data": "i", + "correct": true, + "wordIndex": 9, + "charIndex": 3, + "inputValue": "agai", + "lastWord": true + } + }, + { + "type": "keyup", + "testMs": 9779.3, + "data": { + "code": "KeyI" + } + }, + { + "type": "keydown", + "testMs": 9853.8, + "data": { + "code": "KeyN" + } + }, + { + "type": "input", + "testMs": 9856.1, + "data": { + "inputType": "insertText", + "data": "n", + "correct": true, + "wordIndex": 9, + "charIndex": 4, + "inputValue": "again", + "lastWord": true + } + }, + { + "type": "keyup", + "testMs": 9939.4, + "data": { + "code": "KeyN" + } + }, + { + "type": "keydown", + "testMs": 10004.3, + "data": { + "code": "KeyS" + } + }, + { + "type": "input", + "testMs": 10006.7, + "data": { + "inputType": "insertText", + "data": "s", + "correct": true, + "wordIndex": 9, + "charIndex": 5, + "inputValue": "agains", + "lastWord": true + } + }, + { + "type": "timer", + "testMs": 10007.4, + "data": { + "event": "step", + "timer": 10, + "drift": 7.4 + } + }, + { + "type": "keyup", + "testMs": 10051.3, + "data": { + "code": "KeyS" + } + }, + { + "type": "keydown", + "testMs": 10199.3, + "data": { + "code": "KeyT" + } + }, + { + "type": "input", + "testMs": 10201.6, + "data": { + "inputType": "insertText", + "data": "t", + "correct": true, + "wordIndex": 9, + "charIndex": 6, + "inputValue": "against", + "lastWord": true + } + }, + { + "type": "timer", + "testMs": 10203, + "data": { + "event": "end", + "timer": 10, + "date": 1791473701338 + } + }, + { + "type": "keyup", + "testMs": 10288.8, + "data": { + "code": "KeyT" + } + } + ], + "context": { + "targetWords": [ + "all ", + "man ", + "would ", + "school ", + "where ", + "day ", + "do ", + "never ", + "well ", + "against" + ], + "mode": "words", + "mode2": "10", + "koreanStatus": false, + "bailedOut": false + } + }, + "completedEvent": { + "wpm": 52.94, + "rawWpm": 63.53, + "charStats": [45, 1, 2, 0], + "charTotal": 54, + "acc": 91.38, + "language": "english", + "testDuration": 10.2, + "lastKeyToEnd": 3.7, + "startToFirstKey": 0, + "afkDuration": 0, + "tags": [], + "punctuation": false, + "numbers": false, + "lazyMode": false, + "timestamp": 1791473701467, + "mode": "words", + "mode2": "10", + "bailedOut": false, + "funbox": [], + "difficulty": "normal", + "blindMode": false, + "stopOnLetter": false, + "restartCount": 0, + "incompleteTests": [], + "incompleteTestSeconds": 0, + "consistency": 83.63, + "wpmConsistency": 86.18, + "keyConsistency": 67.27, + "chartData": { + "wpm": [48, 60, 64, 42, 46, 44, 43, 48, 51, 52], + "burst": [72, 60, 72, 84, 60, 60, 48, 84, 72, 60], + "err": [1, 0, 0, 3, 0, 0, 1, 0, 0, 0] + }, + "keySpacing": [ + 115, 193.7, 151.60000000000002, 277.40000000000003, 182.19999999999993, + 300.6, 171.4000000000001, 206.89999999999986, 251.20000000000005, + 106.79999999999995, 91.10000000000014, 145.5, 207.29999999999973, + 146.20000000000027, 183.9000000000001, 97.29999999999973, 207.5, + 90.40000000000009, 102.09999999999991, 145.80000000000018, + 175.9000000000001, 182.89999999999964, 189.20000000000027, + 303.2000000000003, 175.59999999999945, 191.60000000000036, + 151.59999999999945, 192.90000000000055, 157.39999999999964, 278, + 144.40000000000055, 249.79999999999927, 186.20000000000073, + 200.39999999999964, 113.10000000000036, 284.59999999999945, + 202.69999999999982, 277.5, 125.80000000000018, 250.30000000000018, + 133.89999999999964, 138.40000000000055, 146.89999999999964, + 121.90000000000055, 174.79999999999927, 233.5, 165.60000000000036, + 128.89999999999964, 104.10000000000036, 176.39999999999964, + 325.89999999999964, 159, 93.30000000000109, 152.10000000000036, + 192.09999999999854, 150.5, 195 + ], + "keyDuration": [ + 72.4, 99.4, 111, 80.40000000000003, 52.19999999999993, 61.5, + 57.09999999999991, 78.39999999999986, 43.700000000000045, 46, + 59.90000000000009, 143.0999999999999, 107.69999999999982, + 66.20000000000027, 76.40000000000009, 95.69999999999982, + 48.20000000000027, 128.30000000000018, 99.5, 45.90000000000009, + 64.69999999999982, 44.79999999999973, 140.9000000000001, + 87.69999999999982, 48.099999999999454, 147.90000000000055, + 108.59999999999945, 44.600000000000364, 131, 71.19999999999982, + 142.10000000000036, 77.19999999999982, 89.10000000000036, + 134.89999999999964, 86.30000000000018, 111, 118.60000000000036, + 63.80000000000018, 43, 129.90000000000055, 124.80000000000018, + 88.40000000000055, 133.89999999999964, 149.60000000000036, + 48.099999999999454, 42.400000000000546, 124.20000000000073, 71.5, 102.5, + 4.799999999999272, 148.5, 51.600000000000364, 143.20000000000073, + 103.29999999999927, 117.59999999999854, 85.60000000000036, 47, 89.5 + ], + "keyOverlap": 115.5, + "client": "web" + } +} diff --git a/packages/typing-fixtures/reference/keystrokes/words-10-punctuation-numbers.json b/packages/typing-fixtures/reference/keystrokes/words-10-punctuation-numbers.json new file mode 100644 index 000000000..2e6775b88 --- /dev/null +++ b/packages/typing-fixtures/reference/keystrokes/words-10-punctuation-numbers.json @@ -0,0 +1,1855 @@ +{ + "name": "words-10-punctuation-numbers", + "description": "words 10 with punctuation and numbers", + "recordedWith": "web", + "config": { + "mode": "words", + "time": 30, + "words": 10, + "language": "english", + "punctuation": true, + "numbers": true, + "lazyMode": false, + "funbox": [], + "difficulty": "normal", + "blindMode": false, + "stopOnError": "off", + "confidenceMode": "off", + "freedomMode": false, + "strictSpace": false, + "quickEnd": false, + "deleteOnError": "off", + "oppositeShiftMode": "off", + "britishEnglish": false, + "quoteLength": [1] + }, + "keystrokes": [ + { + "kind": "key", + "key": "2", + "gap": 109, + "hold": 109 + }, + { + "kind": "key", + "key": "5", + "gap": 126, + "hold": 117 + }, + { + "kind": "key", + "key": " ", + "gap": 160, + "hold": 89 + }, + { + "kind": "key", + "key": "f", + "gap": 188, + "hold": 66 + }, + { + "kind": "key", + "key": "o", + "gap": 151, + "hold": 47 + }, + { + "kind": "key", + "key": "r", + "gap": 152, + "hold": 48 + }, + { + "kind": "key", + "key": ".", + "gap": 158, + "hold": 64 + }, + { + "kind": "key", + "key": " ", + "gap": 109, + "hold": 145 + }, + { + "kind": "key", + "key": "B", + "gap": 173, + "hold": 50 + }, + { + "kind": "key", + "key": "e", + "gap": 105, + "hold": 44 + }, + { + "kind": "key", + "key": "f", + "gap": 157, + "hold": 89 + }, + { + "kind": "key", + "key": "o", + "gap": 180, + "hold": 125 + }, + { + "kind": "key", + "key": "r", + "gap": 95, + "hold": 90 + }, + { + "kind": "key", + "key": "e", + "gap": 133, + "hold": 95 + }, + { + "kind": "key", + "key": " ", + "gap": 129, + "hold": 97 + }, + { + "kind": "key", + "key": "t", + "gap": 105, + "hold": 135 + }, + { + "kind": "key", + "key": "u", + "gap": 129, + "hold": 121 + }, + { + "kind": "key", + "key": "r", + "gap": 198, + "hold": 96 + }, + { + "kind": "key", + "key": "n", + "gap": 172, + "hold": 123 + }, + { + "kind": "key", + "key": " ", + "gap": 168, + "hold": 74 + }, + { + "kind": "key", + "key": "s", + "gap": 123, + "hold": 51 + }, + { + "kind": "key", + "key": "c", + "gap": 127, + "hold": 150 + }, + { + "kind": "key", + "key": "h", + "gap": 158, + "hold": 84 + }, + { + "kind": "key", + "key": "o", + "gap": 96, + "hold": 89 + }, + { + "kind": "key", + "key": "o", + "gap": 185, + "hold": 150 + }, + { + "kind": "key", + "key": "l", + "gap": 120, + "hold": 135 + }, + { + "kind": "key", + "key": " ", + "gap": 99, + "hold": 100 + }, + { + "kind": "key", + "key": "l", + "gap": 154, + "hold": 60 + }, + { + "kind": "key", + "key": "i", + "gap": 139, + "hold": 53 + }, + { + "kind": "key", + "key": "n", + "gap": 103, + "hold": 149 + }, + { + "kind": "key", + "key": "e", + "gap": 191, + "hold": 61 + }, + { + "kind": "key", + "key": " ", + "gap": 93, + "hold": 145 + }, + { + "kind": "key", + "key": "3", + "gap": 163, + "hold": 127 + }, + { + "kind": "key", + "key": "4", + "gap": 138, + "hold": 89 + }, + { + "kind": "key", + "key": " ", + "gap": 94, + "hold": 86 + }, + { + "kind": "key", + "key": "e", + "gap": 126, + "hold": 117 + }, + { + "kind": "key", + "key": "a", + "gap": 90, + "hold": 123 + }, + { + "kind": "key", + "key": "r", + "gap": 135, + "hold": 68 + }, + { + "kind": "key", + "key": "l", + "gap": 208, + "hold": 138 + }, + { + "kind": "key", + "key": "y", + "gap": 191, + "hold": 105 + }, + { + "kind": "key", + "key": " ", + "gap": 170, + "hold": 145 + }, + { + "kind": "key", + "key": "n", + "gap": 127, + "hold": 50 + }, + { + "kind": "key", + "key": "o", + "gap": 160, + "hold": 84 + }, + { + "kind": "key", + "key": "t", + "gap": 130, + "hold": 138 + }, + { + "kind": "key", + "key": ",", + "gap": 129, + "hold": 100 + }, + { + "kind": "key", + "key": " ", + "gap": 124, + "hold": 120 + }, + { + "kind": "key", + "key": "m", + "gap": 154, + "hold": 84 + }, + { + "kind": "key", + "key": "o", + "gap": 179, + "hold": 97 + }, + { + "kind": "key", + "key": "s", + "gap": 176, + "hold": 131 + }, + { + "kind": "key", + "key": "t", + "gap": 134, + "hold": 69 + }, + { + "kind": "key", + "key": ".", + "gap": 207, + "hold": 47 + } + ], + "eventLog": { + "version": 1, + "events": [ + { + "type": "keydown", + "testMs": -6.6, + "data": { + "code": "Digit2" + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": "2", + "correct": true, + "wordIndex": 0, + "charIndex": 0, + "inputValue": "2" + } + }, + { + "type": "timer", + "testMs": 0, + "data": { + "event": "start", + "timer": 0, + "date": 1791473772708 + } + }, + { + "type": "keyup", + "testMs": 98.8, + "data": { + "code": "Digit2" + } + }, + { + "type": "keydown", + "testMs": 117.1, + "data": { + "code": "Digit5" + } + }, + { + "type": "input", + "testMs": 118, + "data": { + "inputType": "insertText", + "data": "5", + "correct": true, + "wordIndex": 0, + "charIndex": 1, + "inputValue": "25" + } + }, + { + "type": "keyup", + "testMs": 232.9, + "data": { + "code": "Digit5" + } + }, + { + "type": "keydown", + "testMs": 276.7, + "data": { + "code": "Space" + } + }, + { + "type": "input", + "testMs": 278.8, + "data": { + "inputType": "insertText", + "data": " ", + "correct": true, + "wordIndex": 0, + "charIndex": 2, + "inputValue": "25 ", + "commitsWord": true + } + }, + { + "type": "keyup", + "testMs": 366, + "data": { + "code": "Space" + } + }, + { + "type": "keydown", + "testMs": 565.6, + "data": { + "code": "KeyF" + } + }, + { + "type": "input", + "testMs": 568.2, + "data": { + "inputType": "insertText", + "data": "f", + "correct": true, + "wordIndex": 1, + "charIndex": 0, + "inputValue": "f" + } + }, + { + "type": "keyup", + "testMs": 630.7, + "data": { + "code": "KeyF" + } + }, + { + "type": "keydown", + "testMs": 716.4, + "data": { + "code": "KeyO" + } + }, + { + "type": "input", + "testMs": 718.9, + "data": { + "inputType": "insertText", + "data": "o", + "correct": true, + "wordIndex": 1, + "charIndex": 1, + "inputValue": "fo" + } + }, + { + "type": "keyup", + "testMs": 762.8, + "data": { + "code": "KeyO" + } + }, + { + "type": "keydown", + "testMs": 868.1, + "data": { + "code": "KeyR" + } + }, + { + "type": "input", + "testMs": 869.9, + "data": { + "inputType": "insertText", + "data": "r", + "correct": true, + "wordIndex": 1, + "charIndex": 2, + "inputValue": "for" + } + }, + { + "type": "keyup", + "testMs": 915.1, + "data": { + "code": "KeyR" + } + }, + { + "type": "timer", + "testMs": 1001.3, + "data": { + "event": "step", + "timer": 1, + "drift": 1.3 + } + }, + { + "type": "keydown", + "testMs": 1025.5, + "data": { + "code": "Period" + } + }, + { + "type": "input", + "testMs": 1027.4, + "data": { + "inputType": "insertText", + "data": ".", + "correct": true, + "wordIndex": 1, + "charIndex": 3, + "inputValue": "for." + } + }, + { + "type": "keyup", + "testMs": 1088.8, + "data": { + "code": "Period" + } + }, + { + "type": "keydown", + "testMs": 1130.4, + "data": { + "code": "Space" + } + }, + { + "type": "input", + "testMs": 1131.8, + "data": { + "inputType": "insertText", + "data": " ", + "correct": true, + "wordIndex": 1, + "charIndex": 4, + "inputValue": "for. ", + "commitsWord": true + } + }, + { + "type": "keyup", + "testMs": 1279.9, + "data": { + "code": "Space" + } + }, + { + "type": "keydown", + "testMs": 1461.6, + "data": { + "code": "KeyB" + } + }, + { + "type": "input", + "testMs": 1464.2, + "data": { + "inputType": "insertText", + "data": "B", + "correct": true, + "wordIndex": 2, + "charIndex": 0, + "inputValue": "B" + } + }, + { + "type": "keyup", + "testMs": 1511.8, + "data": { + "code": "KeyB" + } + }, + { + "type": "keydown", + "testMs": 1565.6, + "data": { + "code": "KeyE" + } + }, + { + "type": "input", + "testMs": 1566.8, + "data": { + "inputType": "insertText", + "data": "e", + "correct": true, + "wordIndex": 2, + "charIndex": 1, + "inputValue": "Be" + } + }, + { + "type": "keyup", + "testMs": 1609.6, + "data": { + "code": "KeyE" + } + }, + { + "type": "keydown", + "testMs": 1725.2, + "data": { + "code": "KeyF" + } + }, + { + "type": "input", + "testMs": 1727.5, + "data": { + "inputType": "insertText", + "data": "f", + "correct": true, + "wordIndex": 2, + "charIndex": 2, + "inputValue": "Bef" + } + }, + { + "type": "keyup", + "testMs": 1813, + "data": { + "code": "KeyF" + } + }, + { + "type": "keydown", + "testMs": 1904.7, + "data": { + "code": "KeyO" + } + }, + { + "type": "input", + "testMs": 1907.1, + "data": { + "inputType": "insertText", + "data": "o", + "correct": true, + "wordIndex": 2, + "charIndex": 3, + "inputValue": "Befo" + } + }, + { + "type": "keydown", + "testMs": 1998.8, + "data": { + "code": "KeyR" + } + }, + { + "type": "input", + "testMs": 1999.9, + "data": { + "inputType": "insertText", + "data": "r", + "correct": true, + "wordIndex": 2, + "charIndex": 4, + "inputValue": "Befor" + } + }, + { + "type": "timer", + "testMs": 2000.8, + "data": { + "event": "step", + "timer": 2, + "drift": 0.8 + } + }, + { + "type": "keyup", + "testMs": 2027.7, + "data": { + "code": "KeyO" + } + }, + { + "type": "keyup", + "testMs": 2088.9, + "data": { + "code": "KeyR" + } + }, + { + "type": "keydown", + "testMs": 2132.3, + "data": { + "code": "KeyE" + } + }, + { + "type": "input", + "testMs": 2134.7, + "data": { + "inputType": "insertText", + "data": "e", + "correct": true, + "wordIndex": 2, + "charIndex": 5, + "inputValue": "Before" + } + }, + { + "type": "keyup", + "testMs": 2227.8, + "data": { + "code": "KeyE" + } + }, + { + "type": "keydown", + "testMs": 2261.7, + "data": { + "code": "Space" + } + }, + { + "type": "input", + "testMs": 2263.6, + "data": { + "inputType": "insertText", + "data": " ", + "correct": true, + "wordIndex": 2, + "charIndex": 6, + "inputValue": "Before ", + "commitsWord": true + } + }, + { + "type": "keyup", + "testMs": 2356.4, + "data": { + "code": "Space" + } + }, + { + "type": "keydown", + "testMs": 2470.2, + "data": { + "code": "KeyT" + } + }, + { + "type": "input", + "testMs": 2472.5, + "data": { + "inputType": "insertText", + "data": "t", + "correct": true, + "wordIndex": 3, + "charIndex": 0, + "inputValue": "t" + } + }, + { + "type": "keydown", + "testMs": 2598, + "data": { + "code": "KeyU" + } + }, + { + "type": "input", + "testMs": 2599.6, + "data": { + "inputType": "insertText", + "data": "u", + "correct": true, + "wordIndex": 3, + "charIndex": 1, + "inputValue": "tu" + } + }, + { + "type": "keyup", + "testMs": 2602.1, + "data": { + "code": "KeyT" + } + }, + { + "type": "keyup", + "testMs": 2719, + "data": { + "code": "KeyU" + } + }, + { + "type": "keydown", + "testMs": 2796.3, + "data": { + "code": "KeyR" + } + }, + { + "type": "input", + "testMs": 2798.1, + "data": { + "inputType": "insertText", + "data": "r", + "correct": true, + "wordIndex": 3, + "charIndex": 2, + "inputValue": "tur" + } + }, + { + "type": "keyup", + "testMs": 2891.2, + "data": { + "code": "KeyR" + } + }, + { + "type": "keydown", + "testMs": 2968.8, + "data": { + "code": "KeyN" + } + }, + { + "type": "input", + "testMs": 2970.5, + "data": { + "inputType": "insertText", + "data": "n", + "correct": true, + "wordIndex": 3, + "charIndex": 3, + "inputValue": "turn" + } + }, + { + "type": "timer", + "testMs": 3000.1, + "data": { + "event": "step", + "timer": 3, + "drift": 0.1 + } + }, + { + "type": "keyup", + "testMs": 3089.8, + "data": { + "code": "KeyN" + } + }, + { + "type": "keydown", + "testMs": 3133.8, + "data": { + "code": "Space" + } + }, + { + "type": "input", + "testMs": 3134.8, + "data": { + "inputType": "insertText", + "data": " ", + "correct": true, + "wordIndex": 3, + "charIndex": 4, + "inputValue": "turn ", + "commitsWord": true + } + }, + { + "type": "keyup", + "testMs": 3209.1, + "data": { + "code": "Space" + } + }, + { + "type": "keydown", + "testMs": 3338.1, + "data": { + "code": "KeyS" + } + }, + { + "type": "input", + "testMs": 3339.6, + "data": { + "inputType": "insertText", + "data": "s", + "correct": true, + "wordIndex": 4, + "charIndex": 0, + "inputValue": "s" + } + }, + { + "type": "keyup", + "testMs": 3389.6, + "data": { + "code": "KeyS" + } + }, + { + "type": "keydown", + "testMs": 3464.7, + "data": { + "code": "KeyC" + } + }, + { + "type": "input", + "testMs": 3466.7, + "data": { + "inputType": "insertText", + "data": "c", + "correct": true, + "wordIndex": 4, + "charIndex": 1, + "inputValue": "sc" + } + }, + { + "type": "keyup", + "testMs": 3616.4, + "data": { + "code": "KeyC" + } + }, + { + "type": "keydown", + "testMs": 3622.3, + "data": { + "code": "KeyH" + } + }, + { + "type": "input", + "testMs": 3624.3, + "data": { + "inputType": "insertText", + "data": "h", + "correct": true, + "wordIndex": 4, + "charIndex": 2, + "inputValue": "sch" + } + }, + { + "type": "keyup", + "testMs": 3707.1, + "data": { + "code": "KeyH" + } + }, + { + "type": "keydown", + "testMs": 3718.5, + "data": { + "code": "KeyO" + } + }, + { + "type": "input", + "testMs": 3719.6, + "data": { + "inputType": "insertText", + "data": "o", + "correct": true, + "wordIndex": 4, + "charIndex": 3, + "inputValue": "scho" + } + }, + { + "type": "keyup", + "testMs": 3808.9, + "data": { + "code": "KeyO" + } + }, + { + "type": "keydown", + "testMs": 3905.3, + "data": { + "code": "KeyO" + } + }, + { + "type": "input", + "testMs": 3907.3, + "data": { + "inputType": "insertText", + "data": "o", + "correct": true, + "wordIndex": 4, + "charIndex": 4, + "inputValue": "schoo" + } + }, + { + "type": "timer", + "testMs": 4001.3, + "data": { + "event": "step", + "timer": 4, + "drift": 1.3 + } + }, + { + "type": "keydown", + "testMs": 4025.1, + "data": { + "code": "KeyL" + } + }, + { + "type": "input", + "testMs": 4026, + "data": { + "inputType": "insertText", + "data": "l", + "correct": true, + "wordIndex": 4, + "charIndex": 5, + "inputValue": "school" + } + }, + { + "type": "keyup", + "testMs": 4053.2, + "data": { + "code": "KeyO" + } + }, + { + "type": "keydown", + "testMs": 4123.7, + "data": { + "code": "Space" + } + }, + { + "type": "input", + "testMs": 4124.6, + "data": { + "inputType": "insertText", + "data": " ", + "correct": true, + "wordIndex": 4, + "charIndex": 6, + "inputValue": "school ", + "commitsWord": true + } + }, + { + "type": "keyup", + "testMs": 4159.8, + "data": { + "code": "KeyL" + } + }, + { + "type": "keyup", + "testMs": 4224.2, + "data": { + "code": "Space" + } + }, + { + "type": "keydown", + "testMs": 4386.8, + "data": { + "code": "KeyL" + } + }, + { + "type": "input", + "testMs": 4389.2, + "data": { + "inputType": "insertText", + "data": "l", + "correct": true, + "wordIndex": 5, + "charIndex": 0, + "inputValue": "l" + } + }, + { + "type": "keyup", + "testMs": 4446.8, + "data": { + "code": "KeyL" + } + }, + { + "type": "keydown", + "testMs": 4526.5, + "data": { + "code": "KeyI" + } + }, + { + "type": "input", + "testMs": 4528.7, + "data": { + "inputType": "insertText", + "data": "i", + "correct": true, + "wordIndex": 5, + "charIndex": 1, + "inputValue": "li" + } + }, + { + "type": "keyup", + "testMs": 4578.6, + "data": { + "code": "KeyI" + } + }, + { + "type": "keydown", + "testMs": 4628.3, + "data": { + "code": "KeyN" + } + }, + { + "type": "input", + "testMs": 4629.9, + "data": { + "inputType": "insertText", + "data": "n", + "correct": true, + "wordIndex": 5, + "charIndex": 2, + "inputValue": "lin" + } + }, + { + "type": "keyup", + "testMs": 4778.5, + "data": { + "code": "KeyN" + } + }, + { + "type": "keydown", + "testMs": 4820, + "data": { + "code": "KeyE" + } + }, + { + "type": "input", + "testMs": 4822, + "data": { + "inputType": "insertText", + "data": "e", + "correct": true, + "wordIndex": 5, + "charIndex": 3, + "inputValue": "line" + } + }, + { + "type": "keyup", + "testMs": 4880.9, + "data": { + "code": "KeyE" + } + }, + { + "type": "keydown", + "testMs": 4913.1, + "data": { + "code": "Space" + } + }, + { + "type": "input", + "testMs": 4915.5, + "data": { + "inputType": "insertText", + "data": " ", + "correct": true, + "wordIndex": 5, + "charIndex": 4, + "inputValue": "line ", + "commitsWord": true + } + }, + { + "type": "timer", + "testMs": 5000.9, + "data": { + "event": "step", + "timer": 5, + "drift": 0.9 + } + }, + { + "type": "keyup", + "testMs": 5056.6, + "data": { + "code": "Space" + } + }, + { + "type": "keydown", + "testMs": 5229.8, + "data": { + "code": "Digit3" + } + }, + { + "type": "input", + "testMs": 5232, + "data": { + "inputType": "insertText", + "data": "3", + "correct": true, + "wordIndex": 6, + "charIndex": 0, + "inputValue": "3" + } + }, + { + "type": "keyup", + "testMs": 5356.2, + "data": { + "code": "Digit3" + } + }, + { + "type": "keydown", + "testMs": 5366.1, + "data": { + "code": "Digit4" + } + }, + { + "type": "input", + "testMs": 5367.3, + "data": { + "inputType": "insertText", + "data": "4", + "correct": true, + "wordIndex": 6, + "charIndex": 1, + "inputValue": "34" + } + }, + { + "type": "keyup", + "testMs": 5456.2, + "data": { + "code": "Digit4" + } + }, + { + "type": "keydown", + "testMs": 5457.7, + "data": { + "code": "Space" + } + }, + { + "type": "input", + "testMs": 5458.8, + "data": { + "inputType": "insertText", + "data": " ", + "correct": true, + "wordIndex": 6, + "charIndex": 2, + "inputValue": "34 ", + "commitsWord": true + } + }, + { + "type": "keyup", + "testMs": 5547.2, + "data": { + "code": "Space" + } + }, + { + "type": "keydown", + "testMs": 5682.7, + "data": { + "code": "KeyE" + } + }, + { + "type": "input", + "testMs": 5685, + "data": { + "inputType": "insertText", + "data": "e", + "correct": true, + "wordIndex": 7, + "charIndex": 0, + "inputValue": "e" + } + }, + { + "type": "keydown", + "testMs": 5773.6, + "data": { + "code": "KeyA" + } + }, + { + "type": "input", + "testMs": 5774.9, + "data": { + "inputType": "insertText", + "data": "a", + "correct": true, + "wordIndex": 7, + "charIndex": 1, + "inputValue": "ea" + } + }, + { + "type": "keyup", + "testMs": 5799, + "data": { + "code": "KeyE" + } + }, + { + "type": "keyup", + "testMs": 5896.3, + "data": { + "code": "KeyA" + } + }, + { + "type": "keydown", + "testMs": 5907, + "data": { + "code": "KeyR" + } + }, + { + "type": "input", + "testMs": 5908.8, + "data": { + "inputType": "insertText", + "data": "r", + "correct": true, + "wordIndex": 7, + "charIndex": 2, + "inputValue": "ear" + } + }, + { + "type": "keyup", + "testMs": 5977.8, + "data": { + "code": "KeyR" + } + }, + { + "type": "timer", + "testMs": 6001.2, + "data": { + "event": "step", + "timer": 6, + "drift": 1.2 + } + }, + { + "type": "keydown", + "testMs": 6116.4, + "data": { + "code": "KeyL" + } + }, + { + "type": "input", + "testMs": 6118.6, + "data": { + "inputType": "insertText", + "data": "l", + "correct": true, + "wordIndex": 7, + "charIndex": 3, + "inputValue": "earl" + } + }, + { + "type": "keyup", + "testMs": 6254.6, + "data": { + "code": "KeyL" + } + }, + { + "type": "keydown", + "testMs": 6307.5, + "data": { + "code": "KeyY" + } + }, + { + "type": "input", + "testMs": 6309.5, + "data": { + "inputType": "insertText", + "data": "y", + "correct": true, + "wordIndex": 7, + "charIndex": 4, + "inputValue": "early" + } + }, + { + "type": "keyup", + "testMs": 6412.3, + "data": { + "code": "KeyY" + } + }, + { + "type": "keydown", + "testMs": 6477.9, + "data": { + "code": "Space" + } + }, + { + "type": "input", + "testMs": 6480.4, + "data": { + "inputType": "insertText", + "data": " ", + "correct": true, + "wordIndex": 7, + "charIndex": 5, + "inputValue": "early ", + "commitsWord": true + } + }, + { + "type": "keyup", + "testMs": 6624.2, + "data": { + "code": "Space" + } + }, + { + "type": "keydown", + "testMs": 6762.1, + "data": { + "code": "KeyN" + } + }, + { + "type": "input", + "testMs": 6764.6, + "data": { + "inputType": "insertText", + "data": "n", + "correct": true, + "wordIndex": 8, + "charIndex": 0, + "inputValue": "n" + } + }, + { + "type": "keyup", + "testMs": 6811.1, + "data": { + "code": "KeyN" + } + }, + { + "type": "keydown", + "testMs": 6921.9, + "data": { + "code": "KeyO" + } + }, + { + "type": "input", + "testMs": 6924.1, + "data": { + "inputType": "insertText", + "data": "o", + "correct": true, + "wordIndex": 8, + "charIndex": 1, + "inputValue": "no" + } + }, + { + "type": "timer", + "testMs": 7001.1, + "data": { + "event": "step", + "timer": 7, + "drift": 1.1 + } + }, + { + "type": "keyup", + "testMs": 7005.1, + "data": { + "code": "KeyO" + } + }, + { + "type": "keydown", + "testMs": 7051.7, + "data": { + "code": "KeyT" + } + }, + { + "type": "input", + "testMs": 7053.6, + "data": { + "inputType": "insertText", + "data": "t", + "correct": true, + "wordIndex": 8, + "charIndex": 2, + "inputValue": "not" + } + }, + { + "type": "keydown", + "testMs": 7180.6, + "data": { + "code": "Comma" + } + }, + { + "type": "input", + "testMs": 7182.7, + "data": { + "inputType": "insertText", + "data": ",", + "correct": true, + "wordIndex": 8, + "charIndex": 3, + "inputValue": "not," + } + }, + { + "type": "keyup", + "testMs": 7186.8, + "data": { + "code": "KeyT" + } + }, + { + "type": "keyup", + "testMs": 7279.4, + "data": { + "code": "Comma" + } + }, + { + "type": "keydown", + "testMs": 7304.7, + "data": { + "code": "Space" + } + }, + { + "type": "input", + "testMs": 7306.5, + "data": { + "inputType": "insertText", + "data": " ", + "correct": true, + "wordIndex": 8, + "charIndex": 4, + "inputValue": "not, ", + "commitsWord": true + } + }, + { + "type": "keyup", + "testMs": 7424.3, + "data": { + "code": "Space" + } + }, + { + "type": "keydown", + "testMs": 7586.9, + "data": { + "code": "KeyM" + } + }, + { + "type": "input", + "testMs": 7589.2, + "data": { + "inputType": "insertText", + "data": "m", + "correct": true, + "wordIndex": 9, + "charIndex": 0, + "inputValue": "m", + "lastWord": true + } + }, + { + "type": "keyup", + "testMs": 7671, + "data": { + "code": "KeyM" + } + }, + { + "type": "keydown", + "testMs": 7764.9, + "data": { + "code": "KeyO" + } + }, + { + "type": "input", + "testMs": 7765.5, + "data": { + "inputType": "insertText", + "data": "o", + "correct": true, + "wordIndex": 9, + "charIndex": 1, + "inputValue": "mo", + "lastWord": true + } + }, + { + "type": "keyup", + "testMs": 7861.7, + "data": { + "code": "KeyO" + } + }, + { + "type": "keydown", + "testMs": 7941.2, + "data": { + "code": "KeyS" + } + }, + { + "type": "input", + "testMs": 7942.1, + "data": { + "inputType": "insertText", + "data": "s", + "correct": true, + "wordIndex": 9, + "charIndex": 2, + "inputValue": "mos", + "lastWord": true + } + }, + { + "type": "timer", + "testMs": 8001, + "data": { + "event": "step", + "timer": 8, + "drift": 1 + } + }, + { + "type": "keyup", + "testMs": 8073.8, + "data": { + "code": "KeyS" + } + }, + { + "type": "keydown", + "testMs": 8075.8, + "data": { + "code": "KeyT" + } + }, + { + "type": "input", + "testMs": 8077.4, + "data": { + "inputType": "insertText", + "data": "t", + "correct": true, + "wordIndex": 9, + "charIndex": 3, + "inputValue": "most", + "lastWord": true + } + }, + { + "type": "keyup", + "testMs": 8146, + "data": { + "code": "KeyT" + } + }, + { + "type": "keydown", + "testMs": 8283.8, + "data": { + "code": "Period" + } + }, + { + "type": "input", + "testMs": 8286.1, + "data": { + "inputType": "insertText", + "data": ".", + "correct": true, + "wordIndex": 9, + "charIndex": 4, + "inputValue": "most.", + "lastWord": true + } + }, + { + "type": "timer", + "testMs": 8287.6, + "data": { + "event": "end", + "timer": 8, + "date": 1791473780994 + } + }, + { + "type": "keyup", + "testMs": 8330.1, + "data": { + "code": "Period" + } + } + ], + "context": { + "targetWords": [ + "25 ", + "for. ", + "Before ", + "turn ", + "school ", + "line ", + "34 ", + "early ", + "not, ", + "most." + ], + "mode": "words", + "mode2": "10", + "koreanStatus": false, + "bailedOut": false + } + }, + "completedEvent": { + "wpm": 73.82, + "rawWpm": 73.82, + "charStats": [51, 0, 0, 0], + "charTotal": 51, + "acc": 100, + "language": "english", + "testDuration": 8.29, + "lastKeyToEnd": 3.8, + "startToFirstKey": 0, + "afkDuration": 0, + "tags": [], + "punctuation": true, + "numbers": true, + "lazyMode": false, + "timestamp": 1791473781136, + "mode": "words", + "mode2": "10", + "bailedOut": false, + "funbox": [], + "difficulty": "normal", + "blindMode": false, + "stopOnLetter": false, + "restartCount": 0, + "incompleteTests": [], + "incompleteTestSeconds": 0, + "consistency": 90.21, + "wpmConsistency": 97.63, + "keyConsistency": 63.72, + "chartData": { + "wpm": [72, 78, 76, 75, 77, 76, 74, 74], + "burst": [72, 84, 72, 72, 84, 72, 60, 72], + "err": [0, 0, 0, 0, 0, 0, 0, 0] + }, + "keySpacing": [ + 117.1, 159.6, 288.90000000000003, 150.79999999999995, 151.70000000000005, + 157.39999999999998, 104.90000000000009, 331.1999999999998, 104, + 159.60000000000014, 179.5, 94.09999999999991, 133.50000000000023, + 129.39999999999964, 208.5, 127.80000000000018, 198.30000000000018, 172.5, + 165, 204.29999999999973, 126.59999999999991, 157.60000000000036, + 96.19999999999982, 186.80000000000018, 119.79999999999973, + 98.59999999999991, 263.10000000000036, 139.69999999999982, + 101.80000000000018, 191.69999999999982, 93.10000000000036, + 316.6999999999998, 136.30000000000018, 91.59999999999945, 225, + 90.90000000000055, 133.39999999999964, 209.39999999999964, + 191.10000000000036, 170.39999999999964, 284.2000000000007, + 159.79999999999927, 129.80000000000018, 128.90000000000055, + 124.09999999999945, 282.1999999999998, 178, 176.30000000000018, + 134.60000000000036, 207.9999999999991 + ], + "keyDuration": [ + 105.39999999999999, 115.80000000000001, 89.30000000000001, + 65.10000000000002, 46.39999999999998, 47, 63.299999999999955, 149.5, + 50.200000000000045, 44, 87.79999999999995, 123, 90.10000000000014, 95.5, + 94.70000000000027, 131.9000000000001, 121, 94.89999999999964, 121, + 75.29999999999973, 51.5, 151.70000000000027, 84.79999999999973, + 90.40000000000009, 147.89999999999964, 134.70000000000027, 100.5, 60, + 52.100000000000364, 150.19999999999982, 60.899999999999636, 143.5, + 126.39999999999964, 90.09999999999945, 89.5, 116.30000000000018, + 122.69999999999982, 70.80000000000018, 138.20000000000073, + 104.80000000000018, 146.30000000000018, 49, 83.20000000000073, + 135.10000000000036, 98.79999999999927, 119.60000000000036, + 84.10000000000036, 96.80000000000018, 132.60000000000036, + 70.19999999999982, 46.30000000000109 + ], + "keyOverlap": 128.8, + "client": "web" + } +} diff --git a/packages/typing-fixtures/reference/keystrokes/words-10-stop-on-letter.json b/packages/typing-fixtures/reference/keystrokes/words-10-stop-on-letter.json new file mode 100644 index 000000000..a5583f71b --- /dev/null +++ b/packages/typing-fixtures/reference/keystrokes/words-10-stop-on-letter.json @@ -0,0 +1,2168 @@ +{ + "name": "words-10-stop-on-letter", + "description": "stop on error letter, blocked wrong letters", + "recordedWith": "web", + "config": { + "mode": "words", + "time": 30, + "words": 10, + "language": "english", + "punctuation": false, + "numbers": false, + "lazyMode": false, + "funbox": [], + "difficulty": "normal", + "blindMode": false, + "stopOnError": "letter", + "confidenceMode": "off", + "freedomMode": false, + "strictSpace": false, + "quickEnd": false, + "deleteOnError": "off", + "oppositeShiftMode": "off", + "britishEnglish": false, + "quoteLength": [1] + }, + "keystrokes": [ + { + "kind": "key", + "key": "w", + "gap": 91, + "hold": 47 + }, + { + "kind": "key", + "key": "i", + "gap": 207, + "hold": 117 + }, + { + "kind": "key", + "key": "l", + "gap": 153, + "hold": 85 + }, + { + "kind": "key", + "key": "l", + "gap": 146, + "hold": 66 + }, + { + "kind": "key", + "key": " ", + "gap": 156, + "hold": 120 + }, + { + "kind": "key", + "key": "p", + "gap": 121, + "hold": 57 + }, + { + "kind": "key", + "key": "z", + "gap": 182, + "hold": 97 + }, + { + "kind": "key", + "key": "z", + "gap": 114, + "hold": 80 + }, + { + "kind": "key", + "key": "e", + "gap": 125, + "hold": 99 + }, + { + "kind": "key", + "key": "o", + "gap": 126, + "hold": 148 + }, + { + "kind": "key", + "key": "p", + "gap": 120, + "hold": 137 + }, + { + "kind": "key", + "key": "l", + "gap": 113, + "hold": 56 + }, + { + "kind": "key", + "key": "e", + "gap": 109, + "hold": 72 + }, + { + "kind": "key", + "key": " ", + "gap": 156, + "hold": 124 + }, + { + "kind": "key", + "key": "b", + "gap": 99, + "hold": 140 + }, + { + "kind": "key", + "key": "e", + "gap": 156, + "hold": 108 + }, + { + "kind": "key", + "key": "c", + "gap": 172, + "hold": 146 + }, + { + "kind": "key", + "key": "o", + "gap": 165, + "hold": 141 + }, + { + "kind": "key", + "key": "m", + "gap": 189, + "hold": 44 + }, + { + "kind": "key", + "key": "e", + "gap": 111, + "hold": 79 + }, + { + "kind": "key", + "key": " ", + "gap": 141, + "hold": 42 + }, + { + "kind": "key", + "key": "o", + "gap": 101, + "hold": 124 + }, + { + "kind": "key", + "key": "p", + "gap": 134, + "hold": 87 + }, + { + "kind": "key", + "key": "e", + "gap": 146, + "hold": 144 + }, + { + "kind": "key", + "key": "n", + "gap": 174, + "hold": 116 + }, + { + "kind": "key", + "key": " ", + "gap": 130, + "hold": 79 + }, + { + "kind": "key", + "key": "s", + "gap": 107, + "hold": 55 + }, + { + "kind": "key", + "key": "z", + "gap": 200, + "hold": 75 + }, + { + "kind": "key", + "key": "z", + "gap": 113, + "hold": 110 + }, + { + "kind": "key", + "key": "u", + "gap": 130, + "hold": 45 + }, + { + "kind": "key", + "key": "c", + "gap": 172, + "hold": 141 + }, + { + "kind": "key", + "key": "h", + "gap": 156, + "hold": 131 + }, + { + "kind": "key", + "key": " ", + "gap": 169, + "hold": 141 + }, + { + "kind": "key", + "key": "h", + "gap": 104, + "hold": 98 + }, + { + "kind": "key", + "key": "o", + "gap": 168, + "hold": 138 + }, + { + "kind": "key", + "key": "l", + "gap": 205, + "hold": 132 + }, + { + "kind": "key", + "key": "d", + "gap": 161, + "hold": 146 + }, + { + "kind": "key", + "key": " ", + "gap": 206, + "hold": 100 + }, + { + "kind": "key", + "key": "w", + "gap": 188, + "hold": 41 + }, + { + "kind": "key", + "key": "h", + "gap": 152, + "hold": 76 + }, + { + "kind": "key", + "key": "a", + "gap": 177, + "hold": 100 + }, + { + "kind": "key", + "key": "t", + "gap": 181, + "hold": 147 + }, + { + "kind": "key", + "key": " ", + "gap": 157, + "hold": 131 + }, + { + "kind": "key", + "key": "o", + "gap": 142, + "hold": 111 + }, + { + "kind": "key", + "key": "z", + "gap": 179, + "hold": 101 + }, + { + "kind": "key", + "key": "z", + "gap": 127, + "hold": 66 + }, + { + "kind": "key", + "key": "n", + "gap": 91, + "hold": 43 + }, + { + "kind": "key", + "key": "e", + "gap": 203, + "hold": 132 + }, + { + "kind": "key", + "key": " ", + "gap": 166, + "hold": 42 + }, + { + "kind": "key", + "key": "a", + "gap": 164, + "hold": 105 + }, + { + "kind": "key", + "key": "t", + "gap": 176, + "hold": 149 + }, + { + "kind": "key", + "key": " ", + "gap": 123, + "hold": 148 + }, + { + "kind": "key", + "key": "p", + "gap": 168, + "hold": 129 + }, + { + "kind": "key", + "key": "o", + "gap": 165, + "hold": 131 + }, + { + "kind": "key", + "key": "s", + "gap": 107, + "hold": 149 + }, + { + "kind": "key", + "key": "s", + "gap": 151, + "hold": 102 + }, + { + "kind": "key", + "key": "i", + "gap": 109, + "hold": 84 + }, + { + "kind": "key", + "key": "b", + "gap": 115, + "hold": 129 + }, + { + "kind": "key", + "key": "l", + "gap": 193, + "hold": 121 + }, + { + "kind": "key", + "key": "e", + "gap": 169, + "hold": 149 + } + ], + "eventLog": { + "version": 1, + "events": [ + { + "type": "keydown", + "testMs": -6.9, + "data": { + "code": "KeyW" + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": "w", + "correct": true, + "wordIndex": 0, + "charIndex": 0, + "inputValue": "w" + } + }, + { + "type": "timer", + "testMs": 0, + "data": { + "event": "start", + "timer": 0, + "date": 1791473759325 + } + }, + { + "type": "keyup", + "testMs": 48.1, + "data": { + "code": "KeyW" + } + }, + { + "type": "keydown", + "testMs": 197.6, + "data": { + "code": "KeyI" + } + }, + { + "type": "input", + "testMs": 198.9, + "data": { + "inputType": "insertText", + "data": "i", + "correct": true, + "wordIndex": 0, + "charIndex": 1, + "inputValue": "wi" + } + }, + { + "type": "keyup", + "testMs": 314.6, + "data": { + "code": "KeyI" + } + }, + { + "type": "keydown", + "testMs": 351, + "data": { + "code": "KeyL" + } + }, + { + "type": "input", + "testMs": 352.3, + "data": { + "inputType": "insertText", + "data": "l", + "correct": true, + "wordIndex": 0, + "charIndex": 2, + "inputValue": "wil" + } + }, + { + "type": "keyup", + "testMs": 436.8, + "data": { + "code": "KeyL" + } + }, + { + "type": "keydown", + "testMs": 496.5, + "data": { + "code": "KeyL" + } + }, + { + "type": "input", + "testMs": 498.9, + "data": { + "inputType": "insertText", + "data": "l", + "correct": true, + "wordIndex": 0, + "charIndex": 3, + "inputValue": "will" + } + }, + { + "type": "keyup", + "testMs": 562.9, + "data": { + "code": "KeyL" + } + }, + { + "type": "keydown", + "testMs": 652.1, + "data": { + "code": "Space" + } + }, + { + "type": "input", + "testMs": 655.2, + "data": { + "inputType": "insertText", + "data": " ", + "correct": true, + "wordIndex": 0, + "charIndex": 4, + "inputValue": "will ", + "commitsWord": true + } + }, + { + "type": "keyup", + "testMs": 772.5, + "data": { + "code": "Space" + } + }, + { + "type": "keydown", + "testMs": 903.1, + "data": { + "code": "KeyP" + } + }, + { + "type": "input", + "testMs": 905.6, + "data": { + "inputType": "insertText", + "data": "p", + "correct": true, + "wordIndex": 1, + "charIndex": 0, + "inputValue": "p" + } + }, + { + "type": "keyup", + "testMs": 959.9, + "data": { + "code": "KeyP" + } + }, + { + "type": "timer", + "testMs": 1000.7, + "data": { + "event": "step", + "timer": 1, + "drift": 0.7 + } + }, + { + "type": "keydown", + "testMs": 1084, + "data": { + "code": "KeyZ" + } + }, + { + "type": "input", + "testMs": 1085, + "data": { + "inputType": "insertText", + "data": "z", + "correct": false, + "wordIndex": 1, + "charIndex": 1, + "inputStopped": true, + "inputValue": "p" + } + }, + { + "type": "keyup", + "testMs": 1180.8, + "data": { + "code": "KeyZ" + } + }, + { + "type": "keydown", + "testMs": 1198.1, + "data": { + "code": "KeyZ" + } + }, + { + "type": "input", + "testMs": 1198.9, + "data": { + "inputType": "insertText", + "data": "z", + "correct": false, + "wordIndex": 1, + "charIndex": 1, + "inputStopped": true, + "inputValue": "p" + } + }, + { + "type": "keyup", + "testMs": 1277.4, + "data": { + "code": "KeyZ" + } + }, + { + "type": "keydown", + "testMs": 1322.5, + "data": { + "code": "KeyE" + } + }, + { + "type": "input", + "testMs": 1323.2, + "data": { + "inputType": "insertText", + "data": "e", + "correct": true, + "wordIndex": 1, + "charIndex": 1, + "inputValue": "pe" + } + }, + { + "type": "keyup", + "testMs": 1421.5, + "data": { + "code": "KeyE" + } + }, + { + "type": "keydown", + "testMs": 1448.9, + "data": { + "code": "KeyO" + } + }, + { + "type": "input", + "testMs": 1450.1, + "data": { + "inputType": "insertText", + "data": "o", + "correct": true, + "wordIndex": 1, + "charIndex": 2, + "inputValue": "peo" + } + }, + { + "type": "keydown", + "testMs": 1569.8, + "data": { + "code": "KeyP" + } + }, + { + "type": "input", + "testMs": 1571.8, + "data": { + "inputType": "insertText", + "data": "p", + "correct": true, + "wordIndex": 1, + "charIndex": 3, + "inputValue": "peop" + } + }, + { + "type": "keyup", + "testMs": 1597.2, + "data": { + "code": "KeyO" + } + }, + { + "type": "keydown", + "testMs": 1683.7, + "data": { + "code": "KeyL" + } + }, + { + "type": "input", + "testMs": 1685.1, + "data": { + "inputType": "insertText", + "data": "l", + "correct": true, + "wordIndex": 1, + "charIndex": 4, + "inputValue": "peopl" + } + }, + { + "type": "keyup", + "testMs": 1706.7, + "data": { + "code": "KeyP" + } + }, + { + "type": "keyup", + "testMs": 1736.5, + "data": { + "code": "KeyL" + } + }, + { + "type": "keydown", + "testMs": 1791.8, + "data": { + "code": "KeyE" + } + }, + { + "type": "input", + "testMs": 1793.4, + "data": { + "inputType": "insertText", + "data": "e", + "correct": true, + "wordIndex": 1, + "charIndex": 5, + "inputValue": "people" + } + }, + { + "type": "keyup", + "testMs": 1865.2, + "data": { + "code": "KeyE" + } + }, + { + "type": "keydown", + "testMs": 1947.7, + "data": { + "code": "Space" + } + }, + { + "type": "input", + "testMs": 1949.7, + "data": { + "inputType": "insertText", + "data": " ", + "correct": true, + "wordIndex": 1, + "charIndex": 6, + "inputValue": "people ", + "commitsWord": true + } + }, + { + "type": "timer", + "testMs": 2000.5, + "data": { + "event": "step", + "timer": 2, + "drift": 0.5 + } + }, + { + "type": "keyup", + "testMs": 2073.1, + "data": { + "code": "Space" + } + }, + { + "type": "keydown", + "testMs": 2182.6, + "data": { + "code": "KeyB" + } + }, + { + "type": "input", + "testMs": 2185.2, + "data": { + "inputType": "insertText", + "data": "b", + "correct": true, + "wordIndex": 2, + "charIndex": 0, + "inputValue": "b" + } + }, + { + "type": "keyup", + "testMs": 2321.7, + "data": { + "code": "KeyB" + } + }, + { + "type": "keydown", + "testMs": 2336.8, + "data": { + "code": "KeyE" + } + }, + { + "type": "input", + "testMs": 2337.6, + "data": { + "inputType": "insertText", + "data": "e", + "correct": true, + "wordIndex": 2, + "charIndex": 1, + "inputValue": "be" + } + }, + { + "type": "keyup", + "testMs": 2445.4, + "data": { + "code": "KeyE" + } + }, + { + "type": "keydown", + "testMs": 2510.4, + "data": { + "code": "KeyC" + } + }, + { + "type": "input", + "testMs": 2512, + "data": { + "inputType": "insertText", + "data": "c", + "correct": true, + "wordIndex": 2, + "charIndex": 2, + "inputValue": "bec" + } + }, + { + "type": "keyup", + "testMs": 2655.8, + "data": { + "code": "KeyC" + } + }, + { + "type": "keydown", + "testMs": 2674.9, + "data": { + "code": "KeyO" + } + }, + { + "type": "input", + "testMs": 2677, + "data": { + "inputType": "insertText", + "data": "o", + "correct": true, + "wordIndex": 2, + "charIndex": 3, + "inputValue": "beco" + } + }, + { + "type": "keyup", + "testMs": 2815.9, + "data": { + "code": "KeyO" + } + }, + { + "type": "keydown", + "testMs": 2865, + "data": { + "code": "KeyM" + } + }, + { + "type": "input", + "testMs": 2867.4, + "data": { + "inputType": "insertText", + "data": "m", + "correct": true, + "wordIndex": 2, + "charIndex": 4, + "inputValue": "becom" + } + }, + { + "type": "keyup", + "testMs": 2907.8, + "data": { + "code": "KeyM" + } + }, + { + "type": "keydown", + "testMs": 2975.2, + "data": { + "code": "KeyE" + } + }, + { + "type": "input", + "testMs": 2977, + "data": { + "inputType": "insertText", + "data": "e", + "correct": true, + "wordIndex": 2, + "charIndex": 5, + "inputValue": "become" + } + }, + { + "type": "timer", + "testMs": 3000.3, + "data": { + "event": "step", + "timer": 3, + "drift": 0.3 + } + }, + { + "type": "keyup", + "testMs": 3054.8, + "data": { + "code": "KeyE" + } + }, + { + "type": "keydown", + "testMs": 3116.8, + "data": { + "code": "Space" + } + }, + { + "type": "input", + "testMs": 3119.2, + "data": { + "inputType": "insertText", + "data": " ", + "correct": true, + "wordIndex": 2, + "charIndex": 6, + "inputValue": "become ", + "commitsWord": true + } + }, + { + "type": "keyup", + "testMs": 3157.6, + "data": { + "code": "Space" + } + }, + { + "type": "keydown", + "testMs": 3266.8, + "data": { + "code": "KeyO" + } + }, + { + "type": "input", + "testMs": 3267.9, + "data": { + "inputType": "insertText", + "data": "o", + "correct": true, + "wordIndex": 3, + "charIndex": 0, + "inputValue": "o" + } + }, + { + "type": "keyup", + "testMs": 3389.7, + "data": { + "code": "KeyO" + } + }, + { + "type": "keydown", + "testMs": 3399.3, + "data": { + "code": "KeyP" + } + }, + { + "type": "input", + "testMs": 3400.8, + "data": { + "inputType": "insertText", + "data": "p", + "correct": true, + "wordIndex": 3, + "charIndex": 1, + "inputValue": "op" + } + }, + { + "type": "keyup", + "testMs": 3485.3, + "data": { + "code": "KeyP" + } + }, + { + "type": "keydown", + "testMs": 3546.9, + "data": { + "code": "KeyE" + } + }, + { + "type": "input", + "testMs": 3547.8, + "data": { + "inputType": "insertText", + "data": "e", + "correct": true, + "wordIndex": 3, + "charIndex": 2, + "inputValue": "ope" + } + }, + { + "type": "keyup", + "testMs": 3689.9, + "data": { + "code": "KeyE" + } + }, + { + "type": "keydown", + "testMs": 3719.4, + "data": { + "code": "KeyN" + } + }, + { + "type": "input", + "testMs": 3720.5, + "data": { + "inputType": "insertText", + "data": "n", + "correct": true, + "wordIndex": 3, + "charIndex": 3, + "inputValue": "open" + } + }, + { + "type": "keyup", + "testMs": 3836.9, + "data": { + "code": "KeyN" + } + }, + { + "type": "keydown", + "testMs": 3849, + "data": { + "code": "Space" + } + }, + { + "type": "input", + "testMs": 3850.3, + "data": { + "inputType": "insertText", + "data": " ", + "correct": true, + "wordIndex": 3, + "charIndex": 4, + "inputValue": "open ", + "commitsWord": true + } + }, + { + "type": "keyup", + "testMs": 3930.5, + "data": { + "code": "Space" + } + }, + { + "type": "timer", + "testMs": 4000.3, + "data": { + "event": "step", + "timer": 4, + "drift": 0.3 + } + }, + { + "type": "keydown", + "testMs": 4047.8, + "data": { + "code": "KeyS" + } + }, + { + "type": "input", + "testMs": 4050, + "data": { + "inputType": "insertText", + "data": "s", + "correct": true, + "wordIndex": 4, + "charIndex": 0, + "inputValue": "s" + } + }, + { + "type": "keyup", + "testMs": 4102.6, + "data": { + "code": "KeyS" + } + }, + { + "type": "keydown", + "testMs": 4247.9, + "data": { + "code": "KeyZ" + } + }, + { + "type": "input", + "testMs": 4249.7, + "data": { + "inputType": "insertText", + "data": "z", + "correct": false, + "wordIndex": 4, + "charIndex": 1, + "inputStopped": true, + "inputValue": "s" + } + }, + { + "type": "keyup", + "testMs": 4323.1, + "data": { + "code": "KeyZ" + } + }, + { + "type": "keydown", + "testMs": 4360.3, + "data": { + "code": "KeyZ" + } + }, + { + "type": "input", + "testMs": 4361.8, + "data": { + "inputType": "insertText", + "data": "z", + "correct": false, + "wordIndex": 4, + "charIndex": 1, + "inputStopped": true, + "inputValue": "s" + } + }, + { + "type": "keyup", + "testMs": 4471.4, + "data": { + "code": "KeyZ" + } + }, + { + "type": "keydown", + "testMs": 4487.4, + "data": { + "code": "KeyU" + } + }, + { + "type": "input", + "testMs": 4489.5, + "data": { + "inputType": "insertText", + "data": "u", + "correct": true, + "wordIndex": 4, + "charIndex": 1, + "inputValue": "su" + } + }, + { + "type": "keyup", + "testMs": 4534.9, + "data": { + "code": "KeyU" + } + }, + { + "type": "keydown", + "testMs": 4663, + "data": { + "code": "KeyC" + } + }, + { + "type": "input", + "testMs": 4665.3, + "data": { + "inputType": "insertText", + "data": "c", + "correct": true, + "wordIndex": 4, + "charIndex": 2, + "inputValue": "suc" + } + }, + { + "type": "keyup", + "testMs": 4804.5, + "data": { + "code": "KeyC" + } + }, + { + "type": "keydown", + "testMs": 4817.9, + "data": { + "code": "KeyH" + } + }, + { + "type": "input", + "testMs": 4820, + "data": { + "inputType": "insertText", + "data": "h", + "correct": true, + "wordIndex": 4, + "charIndex": 3, + "inputValue": "such" + } + }, + { + "type": "keyup", + "testMs": 4949.5, + "data": { + "code": "KeyH" + } + }, + { + "type": "keydown", + "testMs": 4985.7, + "data": { + "code": "Space" + } + }, + { + "type": "input", + "testMs": 4988, + "data": { + "inputType": "insertText", + "data": " ", + "correct": true, + "wordIndex": 4, + "charIndex": 4, + "inputValue": "such ", + "commitsWord": true + } + }, + { + "type": "timer", + "testMs": 5000.3, + "data": { + "event": "step", + "timer": 5, + "drift": 0.3 + } + }, + { + "type": "keyup", + "testMs": 5127.5, + "data": { + "code": "Space" + } + }, + { + "type": "keydown", + "testMs": 5240.8, + "data": { + "code": "KeyH" + } + }, + { + "type": "input", + "testMs": 5242, + "data": { + "inputType": "insertText", + "data": "h", + "correct": true, + "wordIndex": 5, + "charIndex": 0, + "inputValue": "h" + } + }, + { + "type": "keyup", + "testMs": 5337.5, + "data": { + "code": "KeyH" + } + }, + { + "type": "keydown", + "testMs": 5409.7, + "data": { + "code": "KeyO" + } + }, + { + "type": "input", + "testMs": 5412.1, + "data": { + "inputType": "insertText", + "data": "o", + "correct": true, + "wordIndex": 5, + "charIndex": 1, + "inputValue": "ho" + } + }, + { + "type": "keyup", + "testMs": 5548.1, + "data": { + "code": "KeyO" + } + }, + { + "type": "keydown", + "testMs": 5614.4, + "data": { + "code": "KeyL" + } + }, + { + "type": "input", + "testMs": 5615.9, + "data": { + "inputType": "insertText", + "data": "l", + "correct": true, + "wordIndex": 5, + "charIndex": 2, + "inputValue": "hol" + } + }, + { + "type": "keyup", + "testMs": 5745.8, + "data": { + "code": "KeyL" + } + }, + { + "type": "keydown", + "testMs": 5775.7, + "data": { + "code": "KeyD" + } + }, + { + "type": "input", + "testMs": 5778.1, + "data": { + "inputType": "insertText", + "data": "d", + "correct": true, + "wordIndex": 5, + "charIndex": 3, + "inputValue": "hold" + } + }, + { + "type": "keyup", + "testMs": 5922.4, + "data": { + "code": "KeyD" + } + }, + { + "type": "keydown", + "testMs": 5982.3, + "data": { + "code": "Space" + } + }, + { + "type": "input", + "testMs": 5984.4, + "data": { + "inputType": "insertText", + "data": " ", + "correct": true, + "wordIndex": 5, + "charIndex": 4, + "inputValue": "hold ", + "commitsWord": true + } + }, + { + "type": "timer", + "testMs": 6000.3, + "data": { + "event": "step", + "timer": 6, + "drift": 0.3 + } + }, + { + "type": "keyup", + "testMs": 6080.9, + "data": { + "code": "Space" + } + }, + { + "type": "keydown", + "testMs": 6280, + "data": { + "code": "KeyW" + } + }, + { + "type": "input", + "testMs": 6282.5, + "data": { + "inputType": "insertText", + "data": "w", + "correct": true, + "wordIndex": 6, + "charIndex": 0, + "inputValue": "w" + } + }, + { + "type": "keyup", + "testMs": 6319, + "data": { + "code": "KeyW" + } + }, + { + "type": "keydown", + "testMs": 6432.5, + "data": { + "code": "KeyH" + } + }, + { + "type": "input", + "testMs": 6435.2, + "data": { + "inputType": "insertText", + "data": "h", + "correct": true, + "wordIndex": 6, + "charIndex": 1, + "inputValue": "wh" + } + }, + { + "type": "keyup", + "testMs": 6507.5, + "data": { + "code": "KeyH" + } + }, + { + "type": "keydown", + "testMs": 6609.5, + "data": { + "code": "KeyA" + } + }, + { + "type": "input", + "testMs": 6611.8, + "data": { + "inputType": "insertText", + "data": "a", + "correct": true, + "wordIndex": 6, + "charIndex": 2, + "inputValue": "wha" + } + }, + { + "type": "keyup", + "testMs": 6707.1, + "data": { + "code": "KeyA" + } + }, + { + "type": "keydown", + "testMs": 6789.2, + "data": { + "code": "KeyT" + } + }, + { + "type": "input", + "testMs": 6791.6, + "data": { + "inputType": "insertText", + "data": "t", + "correct": true, + "wordIndex": 6, + "charIndex": 3, + "inputValue": "what" + } + }, + { + "type": "keyup", + "testMs": 6936.5, + "data": { + "code": "KeyT" + } + }, + { + "type": "keydown", + "testMs": 6944.4, + "data": { + "code": "Space" + } + }, + { + "type": "input", + "testMs": 6946.2, + "data": { + "inputType": "insertText", + "data": " ", + "correct": true, + "wordIndex": 6, + "charIndex": 4, + "inputValue": "what ", + "commitsWord": true + } + }, + { + "type": "timer", + "testMs": 7000.4, + "data": { + "event": "step", + "timer": 7, + "drift": 0.4 + } + }, + { + "type": "keyup", + "testMs": 7077.6, + "data": { + "code": "Space" + } + }, + { + "type": "keydown", + "testMs": 7229.2, + "data": { + "code": "KeyO" + } + }, + { + "type": "input", + "testMs": 7231.5, + "data": { + "inputType": "insertText", + "data": "o", + "correct": true, + "wordIndex": 7, + "charIndex": 0, + "inputValue": "o" + } + }, + { + "type": "keyup", + "testMs": 7341.8, + "data": { + "code": "KeyO" + } + }, + { + "type": "keydown", + "testMs": 7410, + "data": { + "code": "KeyZ" + } + }, + { + "type": "input", + "testMs": 7412.7, + "data": { + "inputType": "insertText", + "data": "z", + "correct": false, + "wordIndex": 7, + "charIndex": 1, + "inputStopped": true, + "inputValue": "o" + } + }, + { + "type": "keyup", + "testMs": 7511, + "data": { + "code": "KeyZ" + } + }, + { + "type": "keydown", + "testMs": 7536.6, + "data": { + "code": "KeyZ" + } + }, + { + "type": "input", + "testMs": 7538.9, + "data": { + "inputType": "insertText", + "data": "z", + "correct": false, + "wordIndex": 7, + "charIndex": 1, + "inputStopped": true, + "inputValue": "o" + } + }, + { + "type": "keyup", + "testMs": 7603.4, + "data": { + "code": "KeyZ" + } + }, + { + "type": "keydown", + "testMs": 7627.1, + "data": { + "code": "KeyN" + } + }, + { + "type": "input", + "testMs": 7629.6, + "data": { + "inputType": "insertText", + "data": "n", + "correct": true, + "wordIndex": 7, + "charIndex": 1, + "inputValue": "on" + } + }, + { + "type": "keyup", + "testMs": 7669.3, + "data": { + "code": "KeyN" + } + }, + { + "type": "keydown", + "testMs": 7831.2, + "data": { + "code": "KeyE" + } + }, + { + "type": "input", + "testMs": 7833.4, + "data": { + "inputType": "insertText", + "data": "e", + "correct": true, + "wordIndex": 7, + "charIndex": 2, + "inputValue": "one" + } + }, + { + "type": "keyup", + "testMs": 7962.9, + "data": { + "code": "KeyE" + } + }, + { + "type": "keydown", + "testMs": 7996.2, + "data": { + "code": "Space" + } + }, + { + "type": "input", + "testMs": 7997.4, + "data": { + "inputType": "insertText", + "data": " ", + "correct": true, + "wordIndex": 7, + "charIndex": 3, + "inputValue": "one ", + "commitsWord": true + } + }, + { + "type": "timer", + "testMs": 8000.4, + "data": { + "event": "step", + "timer": 8, + "drift": 0.4 + } + }, + { + "type": "keyup", + "testMs": 8037.9, + "data": { + "code": "Space" + } + }, + { + "type": "keydown", + "testMs": 8209.8, + "data": { + "code": "KeyA" + } + }, + { + "type": "input", + "testMs": 8211.9, + "data": { + "inputType": "insertText", + "data": "a", + "correct": true, + "wordIndex": 8, + "charIndex": 0, + "inputValue": "a" + } + }, + { + "type": "keyup", + "testMs": 8317.1, + "data": { + "code": "KeyA" + } + }, + { + "type": "keydown", + "testMs": 8385.9, + "data": { + "code": "KeyT" + } + }, + { + "type": "input", + "testMs": 8387.5, + "data": { + "inputType": "insertText", + "data": "t", + "correct": true, + "wordIndex": 8, + "charIndex": 1, + "inputValue": "at" + } + }, + { + "type": "keydown", + "testMs": 8509.6, + "data": { + "code": "Space" + } + }, + { + "type": "input", + "testMs": 8511, + "data": { + "inputType": "insertText", + "data": " ", + "correct": true, + "wordIndex": 8, + "charIndex": 2, + "inputValue": "at ", + "commitsWord": true + } + }, + { + "type": "keyup", + "testMs": 8534.8, + "data": { + "code": "KeyT" + } + }, + { + "type": "keyup", + "testMs": 8658, + "data": { + "code": "Space" + } + }, + { + "type": "keydown", + "testMs": 8836.2, + "data": { + "code": "KeyP" + } + }, + { + "type": "input", + "testMs": 8838.7, + "data": { + "inputType": "insertText", + "data": "p", + "correct": true, + "wordIndex": 9, + "charIndex": 0, + "inputValue": "p", + "lastWord": true + } + }, + { + "type": "keyup", + "testMs": 8963.9, + "data": { + "code": "KeyP" + } + }, + { + "type": "keydown", + "testMs": 9000.5, + "data": { + "code": "KeyO" + } + }, + { + "type": "input", + "testMs": 9002.8, + "data": { + "inputType": "insertText", + "data": "o", + "correct": true, + "wordIndex": 9, + "charIndex": 1, + "inputValue": "po", + "lastWord": true + } + }, + { + "type": "timer", + "testMs": 9003.6, + "data": { + "event": "step", + "timer": 9, + "drift": 3.6 + } + }, + { + "type": "keydown", + "testMs": 9105.3, + "data": { + "code": "KeyS" + } + }, + { + "type": "input", + "testMs": 9105.9, + "data": { + "inputType": "insertText", + "data": "s", + "correct": true, + "wordIndex": 9, + "charIndex": 2, + "inputValue": "pos", + "lastWord": true + } + }, + { + "type": "keyup", + "testMs": 9130.3, + "data": { + "code": "KeyO" + } + }, + { + "type": "keyup", + "testMs": 9254.5, + "data": { + "code": "KeyS" + } + }, + { + "type": "keydown", + "testMs": 9255.4, + "data": { + "code": "KeyS" + } + }, + { + "type": "input", + "testMs": 9256, + "data": { + "inputType": "insertText", + "data": "s", + "correct": true, + "wordIndex": 9, + "charIndex": 3, + "inputValue": "poss", + "lastWord": true + } + }, + { + "type": "keyup", + "testMs": 9360.4, + "data": { + "code": "KeyS" + } + }, + { + "type": "keydown", + "testMs": 9364.6, + "data": { + "code": "KeyI" + } + }, + { + "type": "input", + "testMs": 9365.8, + "data": { + "inputType": "insertText", + "data": "i", + "correct": true, + "wordIndex": 9, + "charIndex": 4, + "inputValue": "possi", + "lastWord": true + } + }, + { + "type": "keyup", + "testMs": 9452.1, + "data": { + "code": "KeyI" + } + }, + { + "type": "keydown", + "testMs": 9482.9, + "data": { + "code": "KeyB" + } + }, + { + "type": "input", + "testMs": 9484.4, + "data": { + "inputType": "insertText", + "data": "b", + "correct": true, + "wordIndex": 9, + "charIndex": 5, + "inputValue": "possib", + "lastWord": true + } + }, + { + "type": "keyup", + "testMs": 9612.4, + "data": { + "code": "KeyB" + } + }, + { + "type": "keydown", + "testMs": 9675.8, + "data": { + "code": "KeyL" + } + }, + { + "type": "input", + "testMs": 9678.1, + "data": { + "inputType": "insertText", + "data": "l", + "correct": true, + "wordIndex": 9, + "charIndex": 6, + "inputValue": "possibl", + "lastWord": true + } + }, + { + "type": "keyup", + "testMs": 9797.1, + "data": { + "code": "KeyL" + } + }, + { + "type": "keydown", + "testMs": 9844.7, + "data": { + "code": "KeyE" + } + }, + { + "type": "input", + "testMs": 9846.4, + "data": { + "inputType": "insertText", + "data": "e", + "correct": true, + "wordIndex": 9, + "charIndex": 7, + "inputValue": "possible", + "lastWord": true + } + }, + { + "type": "timer", + "testMs": 9847.9, + "data": { + "event": "end", + "timer": 9, + "date": 1791473769171 + } + }, + { + "type": "keyup", + "testMs": 9923.7, + "data": { + "code": "KeyE", + "estimated": true + } + } + ], + "context": { + "targetWords": [ + "will ", + "people ", + "become ", + "open ", + "such ", + "hold ", + "what ", + "one ", + "at ", + "possible" + ], + "mode": "words", + "mode2": "10", + "koreanStatus": false, + "bailedOut": false + } + }, + "completedEvent": { + "wpm": 65.79, + "rawWpm": 65.79, + "charStats": [54, 0, 0, 0], + "charTotal": 54, + "acc": 90, + "language": "english", + "testDuration": 9.85, + "lastKeyToEnd": 3.2, + "startToFirstKey": 0, + "afkDuration": 0, + "tags": [], + "punctuation": false, + "numbers": false, + "lazyMode": false, + "timestamp": 1791473769303, + "mode": "words", + "mode2": "10", + "bailedOut": false, + "funbox": [], + "difficulty": "normal", + "blindMode": false, + "stopOnLetter": true, + "restartCount": 0, + "incompleteTests": [], + "incompleteTestSeconds": 0, + "consistency": 79.41, + "wpmConsistency": 95.35, + "keyConsistency": 71.11, + "chartData": { + "wpm": [72, 72, 72, 72, 70, 68, 67, 65, 63, 66], + "burst": [72, 96, 72, 72, 84, 60, 60, 72, 48, 99], + "err": [0, 2, 0, 0, 2, 0, 0, 2, 0, 0] + }, + "keySpacing": [ + 197.6, 153.4, 145.5, 155.60000000000002, 251, 180.89999999999998, + 114.09999999999991, 124.40000000000009, 126.40000000000009, + 120.89999999999986, 113.90000000000009, 108.09999999999991, + 155.9000000000001, 234.89999999999986, 154.20000000000027, + 173.5999999999999, 164.5, 190.0999999999999, 110.19999999999982, + 141.60000000000036, 150, 132.5, 147.5999999999999, 172.5, + 129.5999999999999, 198.80000000000018, 200.09999999999945, + 112.40000000000055, 127.09999999999945, 175.60000000000036, + 154.89999999999964, 167.80000000000018, 255.10000000000036, + 168.89999999999964, 204.69999999999982, 161.30000000000018, + 206.60000000000036, 297.6999999999998, 152.5, 177, 179.69999999999982, + 155.19999999999982, 284.8000000000002, 180.80000000000018, + 126.60000000000036, 90.5, 204.09999999999945, 165, 213.59999999999945, + 176.10000000000036, 123.70000000000073, 326.60000000000036, + 164.29999999999927, 104.79999999999927, 150.10000000000036, + 109.20000000000073, 118.29999999999927, 192.89999999999964, + 168.90000000000146 + ], + "keyDuration": [ + 55, 117.00000000000003, 85.80000000000001, 66.39999999999998, + 120.39999999999998, 56.799999999999955, 96.79999999999995, + 79.30000000000018, 99, 148.29999999999995, 136.9000000000001, + 52.799999999999955, 73.40000000000009, 125.39999999999986, + 139.0999999999999, 108.59999999999991, 145.4000000000001, 141, + 42.80000000000018, 79.60000000000036, 40.79999999999973, + 122.89999999999964, 86, 143, 117.5, 81.5, 54.80000000000018, + 75.20000000000073, 111.09999999999945, 47.5, 141.5, 131.60000000000036, + 141.80000000000018, 96.69999999999982, 138.40000000000055, + 131.40000000000055, 146.69999999999982, 98.59999999999945, 39, 75, + 97.60000000000036, 147.30000000000018, 133.20000000000073, + 112.60000000000036, 101, 66.79999999999927, 42.19999999999982, + 131.69999999999982, 41.69999999999982, 107.30000000000109, + 148.89999999999964, 148.39999999999964, 127.69999999999891, + 129.79999999999927, 149.20000000000073, 105, 87.5, 129.5, + 121.30000000000109, 79 + ], + "keyOverlap": 100.6, + "client": "web" + } +} diff --git a/packages/typing-fixtures/reference/keystrokes/zen.json b/packages/typing-fixtures/reference/keystrokes/zen.json new file mode 100644 index 000000000..a7907feb5 --- /dev/null +++ b/packages/typing-fixtures/reference/keystrokes/zen.json @@ -0,0 +1,1106 @@ +{ + "name": "zen", + "description": "zen mode with newlines and a backspace, finished with shift+enter", + "recordedWith": "web", + "config": { + "mode": "zen", + "time": 30, + "words": 50, + "language": "english", + "punctuation": false, + "numbers": false, + "lazyMode": false, + "funbox": [], + "difficulty": "normal", + "blindMode": false, + "stopOnError": "off", + "confidenceMode": "off", + "freedomMode": false, + "strictSpace": false, + "quickEnd": false, + "deleteOnError": "off", + "oppositeShiftMode": "off", + "britishEnglish": false, + "quoteLength": [1] + }, + "keystrokes": [ + { + "kind": "key", + "key": "h", + "gap": 201, + "hold": 77 + }, + { + "kind": "key", + "key": "e", + "gap": 117, + "hold": 49 + }, + { + "kind": "key", + "key": "l", + "gap": 118, + "hold": 96 + }, + { + "kind": "key", + "key": "l", + "gap": 99, + "hold": 147 + }, + { + "kind": "key", + "key": "o", + "gap": 165, + "hold": 116 + }, + { + "kind": "key", + "key": " ", + "gap": 182, + "hold": 81 + }, + { + "kind": "key", + "key": "t", + "gap": 178, + "hold": 136 + }, + { + "kind": "key", + "key": "h", + "gap": 153, + "hold": 122 + }, + { + "kind": "key", + "key": "e", + "gap": 141, + "hold": 94 + }, + { + "kind": "key", + "key": "r", + "gap": 178, + "hold": 142 + }, + { + "kind": "key", + "key": "e", + "gap": 208, + "hold": 92 + }, + { + "kind": "key", + "key": " ", + "gap": 132, + "hold": 127 + }, + { + "kind": "key", + "key": "z", + "gap": 122, + "hold": 123 + }, + { + "kind": "key", + "key": "e", + "gap": 153, + "hold": 78 + }, + { + "kind": "key", + "key": "n", + "gap": 145, + "hold": 41 + }, + { + "kind": "key", + "key": "Enter", + "gap": 148, + "hold": 99 + }, + { + "kind": "key", + "key": "m", + "gap": 182, + "hold": 50 + }, + { + "kind": "key", + "key": "o", + "gap": 187, + "hold": 84 + }, + { + "kind": "key", + "key": "e", + "gap": 130, + "hold": 100 + }, + { + "kind": "key", + "key": "d", + "gap": 97, + "hold": 109 + }, + { + "kind": "key", + "key": "Backspace", + "gap": 123, + "hold": 101 + }, + { + "kind": "key", + "key": "Backspace", + "gap": 108, + "hold": 145 + }, + { + "kind": "key", + "key": "d", + "gap": 192, + "hold": 141 + }, + { + "kind": "key", + "key": "e", + "gap": 132, + "hold": 60 + }, + { + "kind": "key", + "key": " ", + "gap": 169, + "hold": 95 + }, + { + "kind": "key", + "key": "r", + "gap": 98, + "hold": 124 + }, + { + "kind": "key", + "key": "o", + "gap": 205, + "hold": 40 + }, + { + "kind": "key", + "key": "c", + "gap": 148, + "hold": 140 + }, + { + "kind": "key", + "key": "k", + "gap": 173, + "hold": 101 + }, + { + "kind": "key", + "key": "s", + "gap": 208, + "hold": 72 + }, + { + "kind": "key", + "key": "Shift+Enter", + "gap": 182, + "hold": 80 + } + ], + "eventLog": { + "version": 1, + "events": [ + { + "type": "keydown", + "testMs": -6.7, + "data": { + "code": "KeyH" + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": "h", + "correct": true, + "wordIndex": 0, + "charIndex": 0, + "inputValue": "h" + } + }, + { + "type": "timer", + "testMs": 0, + "data": { + "event": "start", + "timer": 0, + "date": 1791473724697 + } + }, + { + "type": "keyup", + "testMs": 67, + "data": { + "code": "KeyH" + } + }, + { + "type": "keydown", + "testMs": 105.7, + "data": { + "code": "KeyE" + } + }, + { + "type": "input", + "testMs": 106.6, + "data": { + "inputType": "insertText", + "data": "e", + "correct": true, + "wordIndex": 0, + "charIndex": 1, + "inputValue": "he" + } + }, + { + "type": "keyup", + "testMs": 158.9, + "data": { + "code": "KeyE" + } + }, + { + "type": "keydown", + "testMs": 223.5, + "data": { + "code": "KeyL" + } + }, + { + "type": "input", + "testMs": 225.1, + "data": { + "inputType": "insertText", + "data": "l", + "correct": true, + "wordIndex": 0, + "charIndex": 2, + "inputValue": "hel" + } + }, + { + "type": "keyup", + "testMs": 322.1, + "data": { + "code": "KeyL" + } + }, + { + "type": "keydown", + "testMs": 324.3, + "data": { + "code": "KeyL" + } + }, + { + "type": "input", + "testMs": 326.1, + "data": { + "inputType": "insertText", + "data": "l", + "correct": true, + "wordIndex": 0, + "charIndex": 3, + "inputValue": "hell" + } + }, + { + "type": "keyup", + "testMs": 471.8, + "data": { + "code": "KeyL" + } + }, + { + "type": "keydown", + "testMs": 489.3, + "data": { + "code": "KeyO" + } + }, + { + "type": "input", + "testMs": 491.8, + "data": { + "inputType": "insertText", + "data": "o", + "correct": true, + "wordIndex": 0, + "charIndex": 4, + "inputValue": "hello" + } + }, + { + "type": "keyup", + "testMs": 605.4, + "data": { + "code": "KeyO" + } + }, + { + "type": "keydown", + "testMs": 670.7, + "data": { + "code": "Space" + } + }, + { + "type": "input", + "testMs": 674.2, + "data": { + "inputType": "insertText", + "data": " ", + "correct": true, + "wordIndex": 0, + "charIndex": 5, + "inputValue": "hello ", + "commitsWord": true + } + }, + { + "type": "keyup", + "testMs": 752.8, + "data": { + "code": "Space" + } + }, + { + "type": "keydown", + "testMs": 850.7, + "data": { + "code": "KeyT" + } + }, + { + "type": "input", + "testMs": 853.2, + "data": { + "inputType": "insertText", + "data": "t", + "correct": true, + "wordIndex": 1, + "charIndex": 0, + "inputValue": "t" + } + }, + { + "type": "keyup", + "testMs": 985.8, + "data": { + "code": "KeyT" + } + }, + { + "type": "keydown", + "testMs": 1000.6, + "data": { + "code": "KeyH" + } + }, + { + "type": "input", + "testMs": 1002.7, + "data": { + "inputType": "insertText", + "data": "h", + "correct": true, + "wordIndex": 1, + "charIndex": 1, + "inputValue": "th" + } + }, + { + "type": "timer", + "testMs": 1011.6, + "data": { + "event": "step", + "timer": 1, + "drift": 11.6 + } + }, + { + "type": "keyup", + "testMs": 1123.5, + "data": { + "code": "KeyH" + } + }, + { + "type": "keydown", + "testMs": 1142.1, + "data": { + "code": "KeyE" + } + }, + { + "type": "input", + "testMs": 1144.7, + "data": { + "inputType": "insertText", + "data": "e", + "correct": true, + "wordIndex": 1, + "charIndex": 2, + "inputValue": "the" + } + }, + { + "type": "keyup", + "testMs": 1238, + "data": { + "code": "KeyE" + } + }, + { + "type": "keydown", + "testMs": 1320.9, + "data": { + "code": "KeyR" + } + }, + { + "type": "input", + "testMs": 1323.3, + "data": { + "inputType": "insertText", + "data": "r", + "correct": true, + "wordIndex": 1, + "charIndex": 3, + "inputValue": "ther" + } + }, + { + "type": "keyup", + "testMs": 1464.2, + "data": { + "code": "KeyR" + } + }, + { + "type": "keydown", + "testMs": 1528.9, + "data": { + "code": "KeyE" + } + }, + { + "type": "input", + "testMs": 1531.3, + "data": { + "inputType": "insertText", + "data": "e", + "correct": true, + "wordIndex": 1, + "charIndex": 4, + "inputValue": "there" + } + }, + { + "type": "keyup", + "testMs": 1621.5, + "data": { + "code": "KeyE" + } + }, + { + "type": "keydown", + "testMs": 1660.5, + "data": { + "code": "Space" + } + }, + { + "type": "input", + "testMs": 1662.7, + "data": { + "inputType": "insertText", + "data": " ", + "correct": true, + "wordIndex": 1, + "charIndex": 5, + "inputValue": "there ", + "commitsWord": true + } + }, + { + "type": "keydown", + "testMs": 1783.1, + "data": { + "code": "KeyZ" + } + }, + { + "type": "input", + "testMs": 1785.1, + "data": { + "inputType": "insertText", + "data": "z", + "correct": true, + "wordIndex": 2, + "charIndex": 0, + "inputValue": "z" + } + }, + { + "type": "keyup", + "testMs": 1787, + "data": { + "code": "Space" + } + }, + { + "type": "keyup", + "testMs": 1904.5, + "data": { + "code": "KeyZ" + } + }, + { + "type": "keydown", + "testMs": 1936.3, + "data": { + "code": "KeyE" + } + }, + { + "type": "input", + "testMs": 1938.5, + "data": { + "inputType": "insertText", + "data": "e", + "correct": true, + "wordIndex": 2, + "charIndex": 1, + "inputValue": "ze" + } + }, + { + "type": "timer", + "testMs": 2011.7, + "data": { + "event": "step", + "timer": 2, + "drift": 11.7 + } + }, + { + "type": "keyup", + "testMs": 2015, + "data": { + "code": "KeyE" + } + }, + { + "type": "keydown", + "testMs": 2081.7, + "data": { + "code": "KeyN" + } + }, + { + "type": "input", + "testMs": 2083.9, + "data": { + "inputType": "insertText", + "data": "n", + "correct": true, + "wordIndex": 2, + "charIndex": 2, + "inputValue": "zen" + } + }, + { + "type": "keyup", + "testMs": 2121.5, + "data": { + "code": "KeyN" + } + }, + { + "type": "keydown", + "testMs": 2229.8, + "data": { + "code": "Enter" + } + }, + { + "type": "input", + "testMs": 2233.7, + "data": { + "inputType": "insertText", + "data": "\n", + "correct": true, + "wordIndex": 2, + "charIndex": 3, + "inputValue": "zen\n", + "commitsWord": true + } + }, + { + "type": "keyup", + "testMs": 2330.7, + "data": { + "code": "Enter" + } + }, + { + "type": "keydown", + "testMs": 2412.8, + "data": { + "code": "KeyM" + } + }, + { + "type": "input", + "testMs": 2415.4, + "data": { + "inputType": "insertText", + "data": "m", + "correct": true, + "wordIndex": 3, + "charIndex": 0, + "inputValue": "m" + } + }, + { + "type": "keyup", + "testMs": 2461.1, + "data": { + "code": "KeyM" + } + }, + { + "type": "keydown", + "testMs": 2598.8, + "data": { + "code": "KeyO" + } + }, + { + "type": "input", + "testMs": 2601.4, + "data": { + "inputType": "insertText", + "data": "o", + "correct": true, + "wordIndex": 3, + "charIndex": 1, + "inputValue": "mo" + } + }, + { + "type": "keyup", + "testMs": 2683, + "data": { + "code": "KeyO" + } + }, + { + "type": "keydown", + "testMs": 2729.2, + "data": { + "code": "KeyE" + } + }, + { + "type": "input", + "testMs": 2730.9, + "data": { + "inputType": "insertText", + "data": "e", + "correct": true, + "wordIndex": 3, + "charIndex": 2, + "inputValue": "moe" + } + }, + { + "type": "keydown", + "testMs": 2826.3, + "data": { + "code": "KeyD" + } + }, + { + "type": "input", + "testMs": 2827.9, + "data": { + "inputType": "insertText", + "data": "d", + "correct": true, + "wordIndex": 3, + "charIndex": 3, + "inputValue": "moed" + } + }, + { + "type": "keyup", + "testMs": 2830.4, + "data": { + "code": "KeyE" + } + }, + { + "type": "keyup", + "testMs": 2934.3, + "data": { + "code": "KeyD" + } + }, + { + "type": "input", + "testMs": 2948.5, + "data": { + "inputType": "deleteContentBackward", + "wordIndex": 3, + "charIndex": 4, + "inputValue": "moe" + } + }, + { + "type": "timer", + "testMs": 3011.7, + "data": { + "event": "step", + "timer": 3, + "drift": 11.7 + } + }, + { + "type": "input", + "testMs": 3055.7, + "data": { + "inputType": "deleteContentBackward", + "wordIndex": 3, + "charIndex": 3, + "inputValue": "mo" + } + }, + { + "type": "keydown", + "testMs": 3249.4, + "data": { + "code": "KeyD" + } + }, + { + "type": "input", + "testMs": 3251.7, + "data": { + "inputType": "insertText", + "data": "d", + "correct": true, + "wordIndex": 3, + "charIndex": 2, + "inputValue": "mod" + } + }, + { + "type": "keydown", + "testMs": 3379.6, + "data": { + "code": "KeyE" + } + }, + { + "type": "input", + "testMs": 3381.3, + "data": { + "inputType": "insertText", + "data": "e", + "correct": true, + "wordIndex": 3, + "charIndex": 3, + "inputValue": "mode" + } + }, + { + "type": "keyup", + "testMs": 3387.1, + "data": { + "code": "KeyD" + } + }, + { + "type": "keyup", + "testMs": 3440, + "data": { + "code": "KeyE" + } + }, + { + "type": "keydown", + "testMs": 3548.9, + "data": { + "code": "Space" + } + }, + { + "type": "input", + "testMs": 3551.1, + "data": { + "inputType": "insertText", + "data": " ", + "correct": true, + "wordIndex": 3, + "charIndex": 4, + "inputValue": "mode ", + "commitsWord": true + } + }, + { + "type": "keyup", + "testMs": 3642.6, + "data": { + "code": "Space" + } + }, + { + "type": "keydown", + "testMs": 3643.6, + "data": { + "code": "KeyR" + } + }, + { + "type": "input", + "testMs": 3644.4, + "data": { + "inputType": "insertText", + "data": "r", + "correct": true, + "wordIndex": 4, + "charIndex": 0, + "inputValue": "r" + } + }, + { + "type": "keyup", + "testMs": 3770.3, + "data": { + "code": "KeyR" + } + }, + { + "type": "keydown", + "testMs": 3851.7, + "data": { + "code": "KeyO" + } + }, + { + "type": "input", + "testMs": 3854.4, + "data": { + "inputType": "insertText", + "data": "o", + "correct": true, + "wordIndex": 4, + "charIndex": 1, + "inputValue": "ro" + } + }, + { + "type": "keyup", + "testMs": 3891, + "data": { + "code": "KeyO" + } + }, + { + "type": "keydown", + "testMs": 3999.1, + "data": { + "code": "KeyC" + } + }, + { + "type": "input", + "testMs": 4001.1, + "data": { + "inputType": "insertText", + "data": "c", + "correct": true, + "wordIndex": 4, + "charIndex": 2, + "inputValue": "roc" + } + }, + { + "type": "timer", + "testMs": 4011.7, + "data": { + "event": "step", + "timer": 4, + "drift": 11.7 + } + }, + { + "type": "keyup", + "testMs": 4141.6, + "data": { + "code": "KeyC" + } + }, + { + "type": "keydown", + "testMs": 4173.5, + "data": { + "code": "KeyK" + } + }, + { + "type": "input", + "testMs": 4176, + "data": { + "inputType": "insertText", + "data": "k", + "correct": true, + "wordIndex": 4, + "charIndex": 3, + "inputValue": "rock" + } + }, + { + "type": "keyup", + "testMs": 4273.8, + "data": { + "code": "KeyK" + } + }, + { + "type": "keydown", + "testMs": 4379.8, + "data": { + "code": "KeyS" + } + }, + { + "type": "input", + "testMs": 4381.6, + "data": { + "inputType": "insertText", + "data": "s", + "correct": true, + "wordIndex": 4, + "charIndex": 4, + "inputValue": "rocks" + } + }, + { + "type": "keyup", + "testMs": 4452.7, + "data": { + "code": "KeyS" + } + }, + { + "type": "keydown", + "testMs": 4560.4, + "data": { + "code": "Enter", + "shift": true + } + }, + { + "type": "timer", + "testMs": 4561.4, + "data": { + "event": "end", + "timer": 4, + "date": 1791473729257 + } + }, + { + "type": "keyup", + "testMs": 4582.8, + "data": { + "code": "Enter", + "shift": true + } + } + ], + "context": { + "targetWords": [], + "mode": "zen", + "mode2": "zen", + "koreanStatus": false, + "bailedOut": false + } + }, + "completedEvent": { + "wpm": 68.42, + "rawWpm": 68.42, + "charStats": [26, 0, 0, 0], + "charTotal": 26, + "acc": 100, + "language": "english", + "testDuration": 4.56, + "lastKeyToEnd": 0, + "startToFirstKey": 0, + "afkDuration": 0, + "tags": [], + "punctuation": false, + "numbers": false, + "lazyMode": false, + "timestamp": 1791473729386, + "mode": "zen", + "mode2": "zen", + "bailedOut": false, + "funbox": [], + "difficulty": "normal", + "blindMode": false, + "stopOnLetter": false, + "restartCount": 0, + "incompleteTests": [], + "incompleteTestSeconds": 0, + "consistency": 86.36, + "wpmConsistency": 90.89, + "keyConsistency": 62.49, + "chartData": { + "wpm": [84, 84, 76, 69, 68], + "burst": [84, 84, 72, 60, 64], + "err": [0, 0, 0, 0, 0] + }, + "keySpacing": [ + 105.7, 117.8, 100.80000000000001, 165, 181.40000000000003, 180, + 149.89999999999998, 141.4999999999999, 178.80000000000018, 208, + 131.5999999999999, 122.59999999999991, 153.20000000000005, + 145.39999999999986, 148.10000000000036, 183, 186, 130.39999999999964, + 97.10000000000036, 423.0999999999999, 130.19999999999982, + 169.30000000000018, 94.69999999999982, 208.0999999999999, + 147.4000000000001, 174.4000000000001, 206.30000000000018, + 180.59999999999945 + ], + "keyDuration": [ + 73.7, 53.2, 98.60000000000002, 147.5, 116.09999999999997, + 82.09999999999991, 135.0999999999999, 122.89999999999998, + 95.90000000000009, 143.29999999999995, 92.59999999999991, 126.5, + 121.40000000000009, 78.70000000000005, 39.80000000000018, + 100.89999999999964, 48.29999999999973, 84.19999999999982, + 101.20000000000027, 108, 137.69999999999982, 60.40000000000009, + 93.69999999999982, 126.70000000000027, 39.30000000000018, + 142.50000000000045, 100.30000000000018, 72.89999999999964, + 22.400000000000546 + ], + "keyOverlap": 15.5, + "client": "web" + } +} diff --git a/packages/typing-fixtures/reference/words/__snapshots__/custom-newlines-tabs.json b/packages/typing-fixtures/reference/words/__snapshots__/custom-newlines-tabs.json new file mode 100644 index 000000000..ff4ceac09 --- /dev/null +++ b/packages/typing-fixtures/reference/words/__snapshots__/custom-newlines-tabs.json @@ -0,0 +1,10 @@ +{ + "words": ["line\n", "\tindented ", "word ", "end\n"], + "sectionIndexes": [1, 2, 3, 4], + "hasTab": true, + "hasNewline": true, + "allJoiningScript": false, + "extra": [], + "allWordsGenerated": true, + "currentQuote": null +} diff --git a/packages/typing-fixtures/reference/words/__snapshots__/custom-random.json b/packages/typing-fixtures/reference/words/__snapshots__/custom-random.json new file mode 100644 index 000000000..9c74d0081 --- /dev/null +++ b/packages/typing-fixtures/reference/words/__snapshots__/custom-random.json @@ -0,0 +1,44 @@ +{ + "words": [ + "delta ", + "epsilon ", + "beta ", + "alpha ", + "gamma ", + "beta ", + "alpha ", + "delta ", + "beta ", + "epsilon ", + "gamma ", + "zeta ", + "epsilon ", + "alpha ", + "beta ", + "delta ", + "alpha ", + "beta ", + "delta ", + "alpha ", + "zeta ", + "gamma ", + "epsilon ", + "beta ", + "gamma ", + "zeta ", + "beta ", + "gamma ", + "delta ", + "epsilon " + ], + "sectionIndexes": [ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30 + ], + "hasTab": false, + "hasNewline": false, + "allJoiningScript": false, + "extra": [], + "allWordsGenerated": true, + "currentQuote": null +} diff --git a/packages/typing-fixtures/reference/words/__snapshots__/custom-repeat.json b/packages/typing-fixtures/reference/words/__snapshots__/custom-repeat.json new file mode 100644 index 000000000..248b1801e --- /dev/null +++ b/packages/typing-fixtures/reference/words/__snapshots__/custom-repeat.json @@ -0,0 +1,20 @@ +{ + "words": [ + "The ", + "quick ", + "brown ", + "fox ", + "jumps ", + "over ", + "the ", + "lazy ", + "dog " + ], + "sectionIndexes": [1, 2, 3, 4, 5, 6, 7, 8, 9], + "hasTab": false, + "hasNewline": false, + "allJoiningScript": false, + "extra": [], + "allWordsGenerated": true, + "currentQuote": null +} diff --git a/packages/typing-fixtures/reference/words/__snapshots__/custom-repeated.json b/packages/typing-fixtures/reference/words/__snapshots__/custom-repeated.json new file mode 100644 index 000000000..6a8954501 --- /dev/null +++ b/packages/typing-fixtures/reference/words/__snapshots__/custom-repeated.json @@ -0,0 +1,23 @@ +{ + "words": [ + "cyan ", + "green ", + "red ", + "cyan ", + "green ", + "blue ", + "cyan ", + "green ", + "blue ", + "red ", + "green ", + "cyan " + ], + "sectionIndexes": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], + "hasTab": false, + "hasNewline": false, + "allJoiningScript": false, + "extra": [], + "allWordsGenerated": true, + "currentQuote": null +} diff --git a/packages/typing-fixtures/reference/words/__snapshots__/custom-sections.json b/packages/typing-fixtures/reference/words/__snapshots__/custom-sections.json new file mode 100644 index 000000000..a36cefd4c --- /dev/null +++ b/packages/typing-fixtures/reference/words/__snapshots__/custom-sections.json @@ -0,0 +1,25 @@ +{ + "words": [ + "a ", + "dog ", + "ran ", + "on ", + "the ", + "mat ", + "far ", + "away ", + "the ", + "cat ", + "sat ", + "on ", + "the ", + "mat " + ], + "sectionIndexes": [1, 1, 1, 2, 2, 2, 3, 3, 4, 4, 4, 5, 5, 5], + "hasTab": false, + "hasNewline": false, + "allJoiningScript": false, + "extra": [], + "allWordsGenerated": true, + "currentQuote": null +} diff --git a/packages/typing-fixtures/reference/words/__snapshots__/custom-shuffle.json b/packages/typing-fixtures/reference/words/__snapshots__/custom-shuffle.json new file mode 100644 index 000000000..f1e84d494 --- /dev/null +++ b/packages/typing-fixtures/reference/words/__snapshots__/custom-shuffle.json @@ -0,0 +1,21 @@ +{ + "words": [ + "four ", + "three ", + "one ", + "two ", + "five ", + "two ", + "one ", + "three ", + "five ", + "four " + ], + "sectionIndexes": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + "hasTab": false, + "hasNewline": false, + "allJoiningScript": false, + "extra": [], + "allWordsGenerated": true, + "currentQuote": null +} diff --git a/packages/typing-fixtures/reference/words/__snapshots__/custom-time.json b/packages/typing-fixtures/reference/words/__snapshots__/custom-time.json new file mode 100644 index 000000000..543a5b3ae --- /dev/null +++ b/packages/typing-fixtures/reference/words/__snapshots__/custom-time.json @@ -0,0 +1,144 @@ +{ + "words": [ + "tick ", + "tick ", + "tick ", + "tick ", + "tock ", + "tock ", + "tock ", + "tick ", + "tock ", + "clock ", + "tock ", + "tick ", + "tick ", + "tick ", + "tick ", + "tock ", + "clock ", + "clock ", + "tick ", + "tock ", + "tick ", + "clock ", + "tick ", + "clock ", + "tock ", + "tick ", + "tick ", + "tick ", + "tock ", + "tick ", + "tick ", + "clock ", + "tock ", + "tick ", + "tick ", + "tock ", + "tock ", + "tick ", + "tick ", + "tock ", + "tick ", + "tick ", + "tick ", + "tick ", + "clock ", + "clock ", + "tick ", + "tock ", + "clock ", + "tock ", + "tock ", + "clock ", + "tick ", + "clock ", + "tick ", + "clock ", + "tock ", + "tock ", + "tick ", + "tock ", + "tick ", + "tick ", + "tock ", + "tick ", + "tick ", + "tick ", + "tock ", + "tick ", + "tick ", + "tick ", + "clock ", + "clock ", + "tick ", + "clock ", + "clock ", + "tick ", + "tock ", + "tock ", + "tick ", + "tock ", + "clock ", + "clock ", + "tick ", + "tock ", + "clock ", + "tock ", + "tick ", + "clock ", + "tock ", + "tock ", + "clock ", + "clock ", + "tock ", + "tock ", + "tock ", + "clock ", + "tock ", + "clock ", + "clock ", + "clock " + ], + "sectionIndexes": [ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, + 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, + 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, + 98, 99, 100 + ], + "hasTab": false, + "hasNewline": false, + "allJoiningScript": false, + "extra": [ + { + "word": "tock ", + "wordRaw": "tock", + "sectionIndex": 101 + }, + { + "word": "tock ", + "wordRaw": "tock", + "sectionIndex": 102 + }, + { + "word": "tock ", + "wordRaw": "tock", + "sectionIndex": 103 + }, + { + "word": "tock ", + "wordRaw": "tock", + "sectionIndex": 104 + }, + { + "word": "tick ", + "wordRaw": "tick", + "sectionIndex": 105 + } + ], + "allWordsGenerated": false, + "currentQuote": null +} diff --git a/packages/typing-fixtures/reference/words/__snapshots__/funbox-58008.json b/packages/typing-fixtures/reference/words/__snapshots__/funbox-58008.json new file mode 100644 index 000000000..bd63eb722 --- /dev/null +++ b/packages/typing-fixtures/reference/words/__snapshots__/funbox-58008.json @@ -0,0 +1,33 @@ +{ + "words": [ + "36 ", + "89.706 ", + "646 ", + "8536.4 ", + "86 ", + "36/1144 ", + "172 ", + "199 ", + "23.1*7 ", + "1.32 ", + "8 ", + "558*21 ", + "17.5 ", + "75*9 ", + "6.11039 ", + "7408 ", + "1 ", + "5 ", + "960027 ", + "8972.42 " + ], + "sectionIndexes": [ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 + ], + "hasTab": false, + "hasNewline": false, + "allJoiningScript": false, + "extra": [], + "allWordsGenerated": true, + "currentQuote": null +} diff --git a/packages/typing-fixtures/reference/words/__snapshots__/funbox-ALL_CAPS.json b/packages/typing-fixtures/reference/words/__snapshots__/funbox-ALL_CAPS.json new file mode 100644 index 000000000..a75c65044 --- /dev/null +++ b/packages/typing-fixtures/reference/words/__snapshots__/funbox-ALL_CAPS.json @@ -0,0 +1,33 @@ +{ + "words": [ + "WHILE ", + "THAN ", + "NATION ", + "LIFE ", + "THING ", + "GIVE ", + "NO ", + "WORK ", + "FROM ", + "LEAD ", + "GOOD ", + "PRESENT ", + "SEEM ", + "MUCH ", + "WITH ", + "ASK ", + "BEGIN ", + "OLD ", + "BACK ", + "GREAT " + ], + "sectionIndexes": [ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 + ], + "hasTab": false, + "hasNewline": false, + "allJoiningScript": false, + "extra": [], + "allWordsGenerated": true, + "currentQuote": null +} diff --git a/packages/typing-fixtures/reference/words/__snapshots__/funbox-IPv4.json b/packages/typing-fixtures/reference/words/__snapshots__/funbox-IPv4.json new file mode 100644 index 000000000..052bc0b79 --- /dev/null +++ b/packages/typing-fixtures/reference/words/__snapshots__/funbox-IPv4.json @@ -0,0 +1,33 @@ +{ + "words": [ + "227.248.0.0/13 ", + "243.79.31.133 ", + "154.96.0.0/12 ", + "117.73.216.147 ", + "232.133.195.121 ", + "134.179.192.0/18 ", + "107.81.191.198 ", + "18.242.174.219 ", + "9.163.90.84 ", + "188.65.0.0/17 ", + "96.56.0.0/14 ", + "24.146.209.77 ", + "23.41.122.235 ", + "66.2.209.109 ", + "164.147.203.63 ", + "164.196.251.103 ", + "86.119.99.196 ", + "145.178.56.56 ", + "132.246.71.250 ", + "81.30.67.0/24 " + ], + "sectionIndexes": [ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 + ], + "hasTab": false, + "hasNewline": false, + "allJoiningScript": false, + "extra": [], + "allWordsGenerated": true, + "currentQuote": null +} diff --git a/packages/typing-fixtures/reference/words/__snapshots__/funbox-IPv6.json b/packages/typing-fixtures/reference/words/__snapshots__/funbox-IPv6.json new file mode 100644 index 000000000..e85ab324b --- /dev/null +++ b/packages/typing-fixtures/reference/words/__snapshots__/funbox-IPv6.json @@ -0,0 +1,33 @@ +{ + "words": [ + "b8d3:fecc:805f:9d52:539d:e0e3:128b:ce68 ", + "dcdb:6c49:3ee5:6ee5:1200:43d7:e9c0:a19d ", + "2960:4dfd:17d4:56d5:5200:9dcf:7770:6dbc ", + "f1dd:dd38:b030:9186:e26c:20a:2299:10cb ", + "4ee4:3855:10e4:d0a9:8286:13af:80ca:44c2 ", + "c5f1:9340:6cc0:225:2033:92fe:66c1:54ce ", + "4870:f047:b1e:5bed:b761:d5a0:7bc8:be36 ", + "eab9:7101:74cd:e02b:ec6c:a355:b6ff:4aef ", + "bacd:1ad4:4d89:6e2c:4730:f016:1f8d:a1dd ", + "c8d1:2d25:47d:287a:fc73:e713:1047:f6ae ", + "2926:827c:aaec:e639:817b:6de2:9a1c:f0b2 ", + "757c:4ec6:f09e:dd8:476c:8a7f:6c8b:c25e ", + "f246:82b1:50fa:f339:f400::/72 ", + "7b0e:c8da:2789:295a:bca:2ea0:46e8:ea3a ", + "f576:b0ee:9717:b2af:5f56:b540:fbfe:402 ", + "c489:cd5f:798d:ab55:b05a:98e:373a:78cd ", + "4502:9749:4642:cbf2:1a5b:eb3e:99a3:7d55 ", + "b1b0:5cb5:d1d6:6b88:a21b:9955:c33b:401f ", + "8324:f005:fed9:3cd4:6e3e:bd7b:35ff:ecd5 ", + "ffdb:656d:d33a:86c8:c9da:3799:5a6:6663 " + ], + "sectionIndexes": [ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 + ], + "hasTab": false, + "hasNewline": false, + "allJoiningScript": false, + "extra": [], + "allWordsGenerated": true, + "currentQuote": null +} diff --git a/packages/typing-fixtures/reference/words/__snapshots__/funbox-arrows.json b/packages/typing-fixtures/reference/words/__snapshots__/funbox-arrows.json new file mode 100644 index 000000000..43b4998b4 --- /dev/null +++ b/packages/typing-fixtures/reference/words/__snapshots__/funbox-arrows.json @@ -0,0 +1,33 @@ +{ + "words": [ + "→←→↑", + "↓↑→←", + "↑←→↓", + "↑←↓↑", + "→←→↓", + "↑↓→←", + "↑←→↑", + "→←↓↑", + "→←→↓", + "↑↓→←", + "→↑↓←", + "→←↑↓", + "→↑↓←", + "↓↑→↓", + "↑←→↓", + "→←↓↑", + "↓←→↓", + "↑↓→←", + "↓←→↑", + "↓←→↓" + ], + "sectionIndexes": [ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 + ], + "hasTab": false, + "hasNewline": false, + "allJoiningScript": false, + "extra": [], + "allWordsGenerated": true, + "currentQuote": null +} diff --git a/packages/typing-fixtures/reference/words/__snapshots__/funbox-ascii.json b/packages/typing-fixtures/reference/words/__snapshots__/funbox-ascii.json new file mode 100644 index 000000000..544e1dec0 --- /dev/null +++ b/packages/typing-fixtures/reference/words/__snapshots__/funbox-ascii.json @@ -0,0 +1,33 @@ +{ + "words": [ + "6 ", + "r%!T:! ", + "YBq}D]w1/d ", + "2% ", + "JY2 ", + "+=W ", + "}h.VeU@ ", + "MD88Dq&E4 ", + "oGEq[AILv/ ", + "9%# ", + "7 ", + "I ", + "vWV&Gp#T: ", + "v% ", + "AM! ", + ".; ", + ". ", + ">@~%+ ", + "?$\"./_ ", + "|#: ", + "#$`}\\? ", + ".. ", + "[@=&\"`< ", + "`_{)(~` ", + "?|#-? ", + ";%.#}< ", + "$ ", + "|!\\$ ", + ")={$ ", + "%\\# " + ], + "sectionIndexes": [ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 + ], + "hasTab": false, + "hasNewline": false, + "allJoiningScript": false, + "extra": [], + "allWordsGenerated": true, + "currentQuote": null +} diff --git a/packages/typing-fixtures/reference/words/__snapshots__/funbox-underscore_spaces.json b/packages/typing-fixtures/reference/words/__snapshots__/funbox-underscore_spaces.json new file mode 100644 index 000000000..80d328ae9 --- /dev/null +++ b/packages/typing-fixtures/reference/words/__snapshots__/funbox-underscore_spaces.json @@ -0,0 +1,33 @@ +{ + "words": [ + "off_", + "follow_", + "feel_", + "mean_", + "because_", + "such_", + "eye_", + "most_", + "first_", + "under_", + "before_", + "plan_", + "how_", + "home_", + "if_", + "develop_", + "over_", + "present_", + "by_", + "all" + ], + "sectionIndexes": [ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 + ], + "hasTab": false, + "hasNewline": false, + "allJoiningScript": false, + "extra": [], + "allWordsGenerated": true, + "currentQuote": null +} diff --git a/packages/typing-fixtures/reference/words/__snapshots__/funbox-weakspot.json b/packages/typing-fixtures/reference/words/__snapshots__/funbox-weakspot.json new file mode 100644 index 000000000..ff98f4faf --- /dev/null +++ b/packages/typing-fixtures/reference/words/__snapshots__/funbox-weakspot.json @@ -0,0 +1,33 @@ +{ + "words": [ + "all ", + "write ", + "give ", + "first ", + "early ", + "plan ", + "thing ", + "from ", + "old ", + "school ", + "stand ", + "will ", + "move ", + "back ", + "just ", + "move ", + "then ", + "few ", + "as ", + "own " + ], + "sectionIndexes": [ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 + ], + "hasTab": false, + "hasNewline": false, + "allJoiningScript": false, + "extra": [], + "allWordsGenerated": true, + "currentQuote": null +} diff --git a/packages/typing-fixtures/reference/words/__snapshots__/funbox-zipf.json b/packages/typing-fixtures/reference/words/__snapshots__/funbox-zipf.json new file mode 100644 index 000000000..bd4e070e1 --- /dev/null +++ b/packages/typing-fixtures/reference/words/__snapshots__/funbox-zipf.json @@ -0,0 +1,33 @@ +{ + "words": [ + "in ", + "the ", + "there ", + "it ", + "the ", + "now ", + "because ", + "only ", + "the ", + "be ", + "life ", + "not ", + "it ", + "all ", + "in ", + "the ", + "to ", + "that ", + "possible ", + "the " + ], + "sectionIndexes": [ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 + ], + "hasTab": false, + "hasNewline": false, + "allJoiningScript": false, + "extra": [], + "allWordsGenerated": true, + "currentQuote": null +} diff --git a/packages/typing-fixtures/reference/words/__snapshots__/quote-all.json b/packages/typing-fixtures/reference/words/__snapshots__/quote-all.json new file mode 100644 index 000000000..db18157bf --- /dev/null +++ b/packages/typing-fixtures/reference/words/__snapshots__/quote-all.json @@ -0,0 +1,28 @@ +{ + "words": [ + "And ", + "every ", + "time ", + "I've ", + "held ", + "a ", + "rose, ", + "it ", + "seems ", + "I ", + "only ", + "felt ", + "the ", + "thorns. " + ], + "sectionIndexes": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14], + "hasTab": false, + "hasNewline": false, + "allJoiningScript": false, + "extra": [], + "allWordsGenerated": true, + "currentQuote": { + "id": 5649, + "group": 0 + } +} diff --git a/packages/typing-fixtures/reference/words/__snapshots__/quote-british-english.json b/packages/typing-fixtures/reference/words/__snapshots__/quote-british-english.json new file mode 100644 index 000000000..96756f1a0 --- /dev/null +++ b/packages/typing-fixtures/reference/words/__snapshots__/quote-british-english.json @@ -0,0 +1,26 @@ +{ + "words": [ + "If ", + "there ", + "is ", + "magic ", + "on ", + "this ", + "planet, ", + "it ", + "is ", + "contained ", + "in ", + "water. " + ], + "sectionIndexes": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], + "hasTab": false, + "hasNewline": false, + "allJoiningScript": false, + "extra": [], + "allWordsGenerated": true, + "currentQuote": { + "id": 7027, + "group": 0 + } +} diff --git a/packages/typing-fixtures/reference/words/__snapshots__/quote-by-id.json b/packages/typing-fixtures/reference/words/__snapshots__/quote-by-id.json new file mode 100644 index 000000000..be07042dc --- /dev/null +++ b/packages/typing-fixtures/reference/words/__snapshots__/quote-by-id.json @@ -0,0 +1,29 @@ +{ + "words": [ + "The ", + "person ", + "that ", + "has ", + "the ", + "most ", + "to ", + "do ", + "with ", + "what ", + "happens ", + "to ", + "you ", + "is ", + "you! " + ], + "sectionIndexes": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], + "hasTab": false, + "hasNewline": false, + "allJoiningScript": false, + "extra": [], + "allWordsGenerated": true, + "currentQuote": { + "id": 13, + "group": 0 + } +} diff --git a/packages/typing-fixtures/reference/words/__snapshots__/quote-repeated.json b/packages/typing-fixtures/reference/words/__snapshots__/quote-repeated.json new file mode 100644 index 000000000..c500783e1 --- /dev/null +++ b/packages/typing-fixtures/reference/words/__snapshots__/quote-repeated.json @@ -0,0 +1,30 @@ +{ + "words": [ + "I'm ", + "having ", + "a ", + "birthday ", + "party, ", + "but ", + "you're ", + "not ", + "invited, ", + "but ", + "you ", + "can ", + "come ", + "if ", + "you ", + "want. " + ], + "sectionIndexes": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], + "hasTab": false, + "hasNewline": false, + "allJoiningScript": false, + "extra": [], + "allWordsGenerated": true, + "currentQuote": { + "id": 3780, + "group": 0 + } +} diff --git a/packages/typing-fixtures/reference/words/__snapshots__/quote-short.json b/packages/typing-fixtures/reference/words/__snapshots__/quote-short.json new file mode 100644 index 000000000..b9daea05d --- /dev/null +++ b/packages/typing-fixtures/reference/words/__snapshots__/quote-short.json @@ -0,0 +1,31 @@ +{ + "words": [ + "She ", + "had ", + "waited ", + "all ", + "her ", + "life ", + "for ", + "something ", + "and ", + "it ", + "had ", + "killed ", + "her ", + "when ", + "it ", + "found ", + "her. " + ], + "sectionIndexes": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17], + "hasTab": false, + "hasNewline": false, + "allJoiningScript": false, + "extra": [], + "allWordsGenerated": true, + "currentQuote": { + "id": 59, + "group": 0 + } +} diff --git a/packages/typing-fixtures/reference/words/__snapshots__/time-15-english-1k.json b/packages/typing-fixtures/reference/words/__snapshots__/time-15-english-1k.json new file mode 100644 index 000000000..9b81e9327 --- /dev/null +++ b/packages/typing-fixtures/reference/words/__snapshots__/time-15-english-1k.json @@ -0,0 +1,118 @@ +{ + "words": [ + "also ", + "century ", + "plain ", + "smile ", + "bright ", + "inch ", + "dollar ", + "left ", + "present ", + "them ", + "material ", + "has ", + "winter ", + "keep ", + "put ", + "substance ", + "twenty ", + "than ", + "through ", + "all ", + "summer ", + "clear ", + "practice ", + "expect ", + "all ", + "stay ", + "step ", + "ball ", + "measure ", + "speak ", + "under ", + "opposite ", + "product ", + "lost ", + "sugar ", + "arm ", + "poor ", + "difficult ", + "ear ", + "red ", + "book ", + "take ", + "feel ", + "universe ", + "joy ", + "certain ", + "an ", + "mind ", + "capital ", + "program ", + "open ", + "opposite ", + "has ", + "ride ", + "length ", + "ask ", + "fall ", + "man ", + "made ", + "teeth ", + "allow ", + "again ", + "one ", + "spend ", + "solve ", + "thick ", + "power ", + "tail ", + "word ", + "field ", + "plain ", + "receive ", + "of ", + "practice ", + "ten ", + "got ", + "support ", + "gray ", + "spot ", + "month ", + "method ", + "substance ", + "feel ", + "may ", + "fun ", + "cold ", + "order ", + "salt ", + "product ", + "believe ", + "idea ", + "blow ", + "reason ", + "certain ", + "choose ", + "speak ", + "join ", + "hurry ", + "interest ", + "mark " + ], + "sectionIndexes": [ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, + 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, + 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, + 98, 99, 100 + ], + "hasTab": false, + "hasNewline": false, + "allJoiningScript": false, + "extra": [], + "allWordsGenerated": false, + "currentQuote": null +} diff --git a/packages/typing-fixtures/reference/words/__snapshots__/time-30.json b/packages/typing-fixtures/reference/words/__snapshots__/time-30.json new file mode 100644 index 000000000..a60980901 --- /dev/null +++ b/packages/typing-fixtures/reference/words/__snapshots__/time-30.json @@ -0,0 +1,169 @@ +{ + "words": [ + "turn ", + "he ", + "long ", + "with ", + "child ", + "people ", + "down ", + "will ", + "into ", + "little ", + "can ", + "not ", + "number ", + "well ", + "late ", + "head ", + "use ", + "same ", + "year ", + "thing ", + "seem ", + "follow ", + "high ", + "of ", + "head ", + "know ", + "run ", + "few ", + "many ", + "number ", + "would ", + "such ", + "turn ", + "the ", + "be ", + "well ", + "govern ", + "become ", + "school ", + "ask ", + "old ", + "with ", + "give ", + "how ", + "during ", + "after ", + "where ", + "just ", + "nation ", + "since ", + "hold ", + "right ", + "give ", + "never ", + "another ", + "much ", + "early ", + "consider ", + "number ", + "general ", + "fact ", + "few ", + "for ", + "high ", + "around ", + "first ", + "too ", + "year ", + "where ", + "little ", + "hand ", + "even ", + "off ", + "say ", + "fact ", + "call ", + "form ", + "few ", + "they ", + "after ", + "would ", + "same ", + "small ", + "both ", + "change ", + "help ", + "course ", + "old ", + "word ", + "fact ", + "person ", + "see ", + "between ", + "keep ", + "even ", + "write ", + "which ", + "need ", + "place ", + "than " + ], + "sectionIndexes": [ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, + 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, + 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, + 98, 99, 100 + ], + "hasTab": false, + "hasNewline": false, + "allJoiningScript": false, + "extra": [ + { + "word": "consider ", + "wordRaw": "consider", + "sectionIndex": 101 + }, + { + "word": "it ", + "wordRaw": "it", + "sectionIndex": 102 + }, + { + "word": "without ", + "wordRaw": "without", + "sectionIndex": 103 + }, + { + "word": "on ", + "wordRaw": "on", + "sectionIndex": 104 + }, + { + "word": "many ", + "wordRaw": "many", + "sectionIndex": 105 + }, + { + "word": "word ", + "wordRaw": "word", + "sectionIndex": 106 + }, + { + "word": "to ", + "wordRaw": "to", + "sectionIndex": 107 + }, + { + "word": "thing ", + "wordRaw": "thing", + "sectionIndex": 108 + }, + { + "word": "by ", + "wordRaw": "by", + "sectionIndex": 109 + }, + { + "word": "year ", + "wordRaw": "year", + "sectionIndex": 110 + } + ], + "allWordsGenerated": false, + "currentQuote": null +} diff --git a/packages/typing-fixtures/reference/words/__snapshots__/words-0-infinite.json b/packages/typing-fixtures/reference/words/__snapshots__/words-0-infinite.json new file mode 100644 index 000000000..8da5ccf0d --- /dev/null +++ b/packages/typing-fixtures/reference/words/__snapshots__/words-0-infinite.json @@ -0,0 +1,144 @@ +{ + "words": [ + "leave ", + "use ", + "year ", + "high ", + "head ", + "need ", + "just ", + "now ", + "against ", + "we ", + "ask ", + "if ", + "many ", + "follow ", + "old ", + "which ", + "thing ", + "such ", + "increase ", + "he ", + "set ", + "they ", + "but ", + "can ", + "be ", + "keep ", + "through ", + "need ", + "change ", + "what ", + "much ", + "any ", + "that ", + "feel ", + "at ", + "for ", + "play ", + "large ", + "the ", + "course ", + "with ", + "increase ", + "seem ", + "can ", + "general ", + "that ", + "since ", + "system ", + "public ", + "back ", + "face ", + "with ", + "number ", + "course ", + "during ", + "call ", + "feel ", + "in ", + "without ", + "during ", + "place ", + "know ", + "however ", + "eye ", + "back ", + "any ", + "and ", + "fact ", + "know ", + "before ", + "home ", + "around ", + "set ", + "look ", + "no ", + "develop ", + "both ", + "number ", + "run ", + "no ", + "only ", + "any ", + "high ", + "to ", + "year ", + "follow ", + "there ", + "since ", + "even ", + "way ", + "find ", + "present ", + "well ", + "change ", + "than ", + "on ", + "while ", + "a ", + "leave ", + "against " + ], + "sectionIndexes": [ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, + 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, + 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, + 98, 99, 100 + ], + "hasTab": false, + "hasNewline": false, + "allJoiningScript": false, + "extra": [ + { + "word": "develop ", + "wordRaw": "develop", + "sectionIndex": 101 + }, + { + "word": "year ", + "wordRaw": "year", + "sectionIndex": 102 + }, + { + "word": "get ", + "wordRaw": "get", + "sectionIndex": 103 + }, + { + "word": "under ", + "wordRaw": "under", + "sectionIndex": 104 + }, + { + "word": "this ", + "wordRaw": "this", + "sectionIndex": 105 + } + ], + "allWordsGenerated": false, + "currentQuote": null +} diff --git a/packages/typing-fixtures/reference/words/__snapshots__/words-10.json b/packages/typing-fixtures/reference/words/__snapshots__/words-10.json new file mode 100644 index 000000000..a5702f9d3 --- /dev/null +++ b/packages/typing-fixtures/reference/words/__snapshots__/words-10.json @@ -0,0 +1,21 @@ +{ + "words": [ + "between ", + "the ", + "seem ", + "course ", + "stand ", + "new ", + "show ", + "turn ", + "must ", + "help " + ], + "sectionIndexes": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + "hasTab": false, + "hasNewline": false, + "allJoiningScript": false, + "extra": [], + "allWordsGenerated": true, + "currentQuote": null +} diff --git a/packages/typing-fixtures/reference/words/__snapshots__/words-100-show-all-lines.json b/packages/typing-fixtures/reference/words/__snapshots__/words-100-show-all-lines.json new file mode 100644 index 000000000..eea8df2ae --- /dev/null +++ b/packages/typing-fixtures/reference/words/__snapshots__/words-100-show-all-lines.json @@ -0,0 +1,171 @@ +{ + "words": [ + "of ", + "they ", + "early ", + "another ", + "feel ", + "also ", + "much ", + "time ", + "little ", + "real ", + "than ", + "all ", + "child ", + "those ", + "more ", + "such ", + "some ", + "how ", + "take ", + "early ", + "go ", + "head ", + "can ", + "which ", + "would ", + "some ", + "place ", + "child ", + "with ", + "system ", + "little ", + "both ", + "school ", + "stand ", + "both ", + "order ", + "public ", + "he ", + "say ", + "now ", + "must ", + "a ", + "on ", + "few ", + "such ", + "must ", + "much ", + "face ", + "begin ", + "never ", + "like ", + "now ", + "or ", + "from ", + "system ", + "use ", + "can ", + "need ", + "then ", + "have ", + "school ", + "order ", + "place ", + "public ", + "last ", + "set ", + "you ", + "seem ", + "develop ", + "program ", + "play ", + "during ", + "tell ", + "play ", + "stand ", + "place ", + "end ", + "be ", + "those ", + "get ", + "turn ", + "place ", + "off ", + "early ", + "world ", + "open ", + "must ", + "call ", + "might ", + "little ", + "know ", + "time ", + "be ", + "a ", + "keep ", + "follow ", + "need ", + "of ", + "house ", + "tell ", + "part ", + "help ", + "only ", + "change ", + "develop ", + "interest ", + "both ", + "follow ", + "or ", + "help ", + "good ", + "still ", + "will ", + "find ", + "man ", + "person ", + "govern ", + "might ", + "last ", + "help ", + "here ", + "might ", + "group ", + "down ", + "so ", + "old ", + "home ", + "again ", + "when ", + "little ", + "well ", + "seem ", + "make ", + "about ", + "before ", + "well ", + "say ", + "the ", + "a ", + "this ", + "head ", + "course ", + "they ", + "say ", + "also ", + "just ", + "leave ", + "such ", + "may ", + "late " + ], + "sectionIndexes": [ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, + 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, + 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, + 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, + 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, + 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, + 143, 144, 145, 146, 147, 148, 149, 150 + ], + "hasTab": false, + "hasNewline": false, + "allJoiningScript": false, + "extra": [], + "allWordsGenerated": true, + "currentQuote": null +} diff --git a/packages/typing-fixtures/reference/words/__snapshots__/words-20-repeated.json b/packages/typing-fixtures/reference/words/__snapshots__/words-20-repeated.json new file mode 100644 index 000000000..942c5bc7a --- /dev/null +++ b/packages/typing-fixtures/reference/words/__snapshots__/words-20-repeated.json @@ -0,0 +1,33 @@ +{ + "words": [ + "back ", + "must ", + "each ", + "work ", + "can ", + "must ", + "thing ", + "of ", + "run ", + "head ", + "may ", + "they ", + "off ", + "of ", + "will ", + "then ", + "must ", + "plan ", + "call ", + "than " + ], + "sectionIndexes": [ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 + ], + "hasTab": false, + "hasNewline": false, + "allJoiningScript": false, + "extra": [], + "allWordsGenerated": true, + "currentQuote": null +} diff --git a/packages/typing-fixtures/reference/words/__snapshots__/words-25-numbers.json b/packages/typing-fixtures/reference/words/__snapshots__/words-25-numbers.json new file mode 100644 index 000000000..c27da7ecb --- /dev/null +++ b/packages/typing-fixtures/reference/words/__snapshots__/words-25-numbers.json @@ -0,0 +1,39 @@ +{ + "words": [ + "never ", + "out ", + "on ", + "turn ", + "lead ", + "since ", + "38 ", + "year ", + "turn ", + "and ", + "no ", + "do ", + "help ", + "not ", + "see ", + "change ", + "most ", + "around ", + "head ", + "large ", + "or ", + "hand ", + "since ", + "be ", + "first " + ], + "sectionIndexes": [ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25 + ], + "hasTab": false, + "hasNewline": false, + "allJoiningScript": false, + "extra": [], + "allWordsGenerated": true, + "currentQuote": null +} diff --git a/packages/typing-fixtures/reference/words/__snapshots__/words-25-punctuation.json b/packages/typing-fixtures/reference/words/__snapshots__/words-25-punctuation.json new file mode 100644 index 000000000..33a6f07a1 --- /dev/null +++ b/packages/typing-fixtures/reference/words/__snapshots__/words-25-punctuation.json @@ -0,0 +1,39 @@ +{ + "words": [ + "Order ", + "like ", + "leave ", + "- ", + "down, ", + "both ", + "'I' ", + "people ", + "time ", + "few ", + "not. ", + "Move ", + "you ", + "here ", + "line ", + "show ", + "with ", + "however ", + "or ", + "should, ", + "will ", + "run ", + "might ", + "part ", + "face. " + ], + "sectionIndexes": [ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25 + ], + "hasTab": false, + "hasNewline": false, + "allJoiningScript": false, + "extra": [], + "allWordsGenerated": true, + "currentQuote": null +} diff --git a/packages/typing-fixtures/reference/words/__snapshots__/words-30-arabic-numbers.json b/packages/typing-fixtures/reference/words/__snapshots__/words-30-arabic-numbers.json new file mode 100644 index 000000000..c9f5a2150 --- /dev/null +++ b/packages/typing-fixtures/reference/words/__snapshots__/words-30-arabic-numbers.json @@ -0,0 +1,45 @@ +{ + "words": [ + "فَضَلَكَ ", + "4 ", + "الْفَضَاءُ ", + "93 ", + "مُدْهِشٌ، ", + "بِالطَّبْعِ، ", + "هَادِئٌ ", + "الْوَقْتُ ", + "الْمَنْزِلُ، ", + "بِالطَّبْعِ، ", + "الذَّهَابُ ", + "الثَّالِثَةَ ", + "أَلِأرْضٍ ", + "مُمَارَسَةٌ. ", + "مُضَاعَفَةٌ ", + "عَظِيمٌ ", + "دَرَجَةٌ ", + "بِالطَّبْعِ ", + "مُضَاعَفَةٌ ", + "عَظِيمٌ ", + "125 ", + "خِلَالَ ", + "وَظِيفَةٌ ", + "الْمُفَضَّلُ ", + "الذَّهَابُ ", + "حَجْمً ", + "دُكْتورٌ ", + "مَاذَا، ", + "'مَسَاءَ' ", + "صَدِيقِي. " + ], + "sectionIndexes": [ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30 + ], + "hasTab": false, + "hasNewline": false, + "allRightToLeft": true, + "allJoiningScript": true, + "extra": [], + "allWordsGenerated": true, + "currentQuote": null +} diff --git a/packages/typing-fixtures/reference/words/__snapshots__/words-30-code-javascript-punctuation.json b/packages/typing-fixtures/reference/words/__snapshots__/words-30-code-javascript-punctuation.json new file mode 100644 index 000000000..d15bd34da --- /dev/null +++ b/packages/typing-fixtures/reference/words/__snapshots__/words-30-code-javascript-punctuation.json @@ -0,0 +1,44 @@ +{ + "words": [ + "Symbol(). ", + "createUploadthing ", + "onmouseup ", + "[].map() ", + "Math.trunc(), ", + "i++ ", + "includes(). ", + "this ", + "onmouseup, ", + "onNext() ", + ".input ", + "Math.pow(), ", + "return ", + "in. ", + "do ", + "{ ", + "isNaN(), ", + "|| ", + "nodeType ", + "- ", + "` ", + "[ ", + "% ", + "trim(), ", + "await ", + "attributes ", + "object ", + "switch ", + "console ", + "await. " + ], + "sectionIndexes": [ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30 + ], + "hasTab": false, + "hasNewline": false, + "allJoiningScript": false, + "extra": [], + "allWordsGenerated": true, + "currentQuote": null +} diff --git a/packages/typing-fixtures/reference/words/__snapshots__/words-30-french-punctuation.json b/packages/typing-fixtures/reference/words/__snapshots__/words-30-french-punctuation.json new file mode 100644 index 000000000..839b1c64b --- /dev/null +++ b/packages/typing-fixtures/reference/words/__snapshots__/words-30-french-punctuation.json @@ -0,0 +1,44 @@ +{ + "words": [ + "Les ", + "trois ", + "forme ", + "juste, ", + "côté ", + "trois, ", + "port ", + "après ", + "(essayer) ", + "côté ", + "penser ", + "temps ", + "est ", + "venu ", + "air ", + "de ", + "fabriqué ", + "est, ", + "année ", + "utiliser, ", + "père ", + "eau ", + "première ", + "où ", + "les ", + "trois ", + "appel ", + "pourrait ", + "plus ", + "image. " + ], + "sectionIndexes": [ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 13, 14, 15, 16, 17, 18, 19, 20, + 21, 22, 23, 24, 25, 26, 27, 28, 29 + ], + "hasTab": false, + "hasNewline": false, + "allJoiningScript": false, + "extra": [], + "allWordsGenerated": true, + "currentQuote": null +} diff --git a/packages/typing-fixtures/reference/words/__snapshots__/words-30-german.json b/packages/typing-fixtures/reference/words/__snapshots__/words-30-german.json new file mode 100644 index 000000000..6a74dea3c --- /dev/null +++ b/packages/typing-fixtures/reference/words/__snapshots__/words-30-german.json @@ -0,0 +1,44 @@ +{ + "words": [ + "drei ", + "neuen ", + "ihren ", + "nun ", + "wo ", + "bereits ", + "nicht ", + "damit ", + "ihren ", + "was ", + "muss ", + "gut ", + "Uhr ", + "Zeit ", + "ins ", + "einer ", + "sind ", + "sein ", + "diese ", + "eines ", + "selbst ", + "er ", + "etwa ", + "sondern ", + "Uhr ", + "gut ", + "denn ", + "Unternehmen ", + "können ", + "seien " + ], + "sectionIndexes": [ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30 + ], + "hasTab": false, + "hasNewline": false, + "allJoiningScript": false, + "extra": [], + "allWordsGenerated": true, + "currentQuote": null +} diff --git a/packages/typing-fixtures/reference/words/__snapshots__/words-30-lazy-french.json b/packages/typing-fixtures/reference/words/__snapshots__/words-30-lazy-french.json new file mode 100644 index 000000000..e9d3fd432 --- /dev/null +++ b/packages/typing-fixtures/reference/words/__snapshots__/words-30-lazy-french.json @@ -0,0 +1,44 @@ +{ + "words": [ + "notre ", + "seulement ", + "air ", + "penser ", + "etre ", + "pourrait ", + "notre ", + "chaque ", + "avec ", + "signifier ", + "autre ", + "faut ", + "de ", + "comme ", + "changement ", + "signifier ", + "haut ", + "eu ", + "vivre ", + "petit ", + "meme ", + "penser ", + "notre ", + "je ", + "montrer ", + "ces ", + "penser ", + "animal ", + "trois ", + "juste " + ], + "sectionIndexes": [ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30 + ], + "hasTab": false, + "hasNewline": false, + "allJoiningScript": false, + "extra": [], + "allWordsGenerated": true, + "currentQuote": null +} diff --git a/packages/typing-fixtures/reference/words/__snapshots__/words-30-lazy-polish-punctuation.json b/packages/typing-fixtures/reference/words/__snapshots__/words-30-lazy-polish-punctuation.json new file mode 100644 index 000000000..59f8fd36f --- /dev/null +++ b/packages/typing-fixtures/reference/words/__snapshots__/words-30-lazy-polish-punctuation.json @@ -0,0 +1,44 @@ +{ + "words": [ + "Brzmiec ", + "pozniej ", + "dlaczego ", + "potrzeba ", + "pisac ", + "(kiedy) ", + "wziac ", + "mnie ", + "odnalezc ", + "dzien, ", + "wielki ", + "zrobic ", + "czas. ", + "Miejsce ", + "dzien ", + "dwa ", + "puszka ", + "pomoc. ", + "Formularz ", + "gdzie, ", + "przed ", + "maly ", + "jest ", + "kto ", + "przyczyna ", + "mnie, ", + "odnalezc ", + "szukac ", + "zespol ", + "niz. " + ], + "sectionIndexes": [ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30 + ], + "hasTab": false, + "hasNewline": false, + "allJoiningScript": false, + "extra": [], + "allWordsGenerated": true, + "currentQuote": null +} diff --git a/packages/typing-fixtures/reference/words/__snapshots__/words-30-spanish-punctuation.json b/packages/typing-fixtures/reference/words/__snapshots__/words-30-spanish-punctuation.json new file mode 100644 index 000000000..04ceed0d8 --- /dev/null +++ b/packages/typing-fixtures/reference/words/__snapshots__/words-30-spanish-punctuation.json @@ -0,0 +1,44 @@ +{ + "words": [ + "¡Dicho ", + "de ", + "gente ", + "camino ", + "este, ", + "años ", + "causa, ", + "hasta ", + "muchos ", + "dar ", + "- ", + "tomar! ", + "Si ", + "eran ", + "segundo ", + "- ", + "cuerpo ", + "animal ", + "si, ", + "tenido ", + "poder ", + "algunos, ", + "todo ", + "sistema ", + "otros ", + "con ", + "hizo ", + "para ", + "cambio ", + "poco " + ], + "sectionIndexes": [ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30 + ], + "hasTab": false, + "hasNewline": false, + "allJoiningScript": false, + "extra": [], + "allWordsGenerated": true, + "currentQuote": null +} diff --git a/packages/typing-fixtures/reference/words/__snapshots__/words-30-swiss-german.json b/packages/typing-fixtures/reference/words/__snapshots__/words-30-swiss-german.json new file mode 100644 index 000000000..9176b2f95 --- /dev/null +++ b/packages/typing-fixtures/reference/words/__snapshots__/words-30-swiss-german.json @@ -0,0 +1,44 @@ +{ + "words": [ + "sind ", + "Zvieri ", + "aus ", + "Teil ", + "unsere ", + "denken ", + "für ", + "unten ", + "oben ", + "Kopf ", + "könnte ", + "Trottinett ", + "Welt ", + "hatte ", + "alt ", + "Trinken ", + "zwei ", + "jeder ", + "warum ", + "Znüni ", + "leben ", + "wenn ", + "Satz ", + "zu ", + "selbst ", + "müssen ", + "parkieren ", + "sein ", + "niedrig ", + "wollen " + ], + "sectionIndexes": [ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30 + ], + "hasTab": false, + "hasNewline": false, + "allJoiningScript": false, + "extra": [], + "allWordsGenerated": true, + "currentQuote": null +} diff --git a/packages/typing-fixtures/reference/words/__snapshots__/words-30-turkish-punctuation.json b/packages/typing-fixtures/reference/words/__snapshots__/words-30-turkish-punctuation.json new file mode 100644 index 000000000..950bad8af --- /dev/null +++ b/packages/typing-fixtures/reference/words/__snapshots__/words-30-turkish-punctuation.json @@ -0,0 +1,44 @@ +{ + "words": [ + "İle ", + "- ", + "anlam ", + "nasıl, ", + "bazı ", + "çıkarmak ", + "anlatmak ", + "dönmek ", + "grup ", + "yol, ", + "zor ", + "etki ", + "gelmek ", + "an ", + "kitap ", + "böyle ", + "o ", + "'tek' ", + "sorun ", + "(ülke) ", + "gelen ", + "kendi, ", + "arkadaş ", + "diye ", + "geri ", + "yapmak ", + "özellik ", + "bunu ", + "açmak, ", + "insan. " + ], + "sectionIndexes": [ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30 + ], + "hasTab": false, + "hasNewline": false, + "allJoiningScript": false, + "extra": [], + "allWordsGenerated": true, + "currentQuote": null +} diff --git a/packages/typing-fixtures/reference/words/__snapshots__/words-50-british-english.json b/packages/typing-fixtures/reference/words/__snapshots__/words-50-british-english.json new file mode 100644 index 000000000..7c070ef92 --- /dev/null +++ b/packages/typing-fixtures/reference/words/__snapshots__/words-50-british-english.json @@ -0,0 +1,65 @@ +{ + "words": [ + "Universe ", + "book, ", + "ease ", + "about ", + "farm? ", + "Success ", + "wide ", + "door ", + "happen ", + "agree ", + "fun. ", + "Sister ", + "multiply, ", + "red ", + "white ", + "only ", + "dear ", + "open ", + "drop ", + "ball ", + "fell, ", + "receive ", + "condition ", + "love ", + "wall ", + "father ", + "send ", + "rest ", + "tie ", + "'leave' ", + "train ", + "soil. ", + "Period ", + "neck, ", + "modern ", + "shape, ", + "atom ", + "pretty! ", + "Single ", + "wall. ", + "Connect ", + "should, ", + "fell ", + "nation ", + "possible, ", + "join ", + "problem, ", + "add ", + "gentle ", + "protect. " + ], + "sectionIndexes": [ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50 + ], + "hasTab": false, + "hasNewline": false, + "allJoiningScript": false, + "extra": [], + "allWordsGenerated": true, + "currentQuote": null +} diff --git a/packages/typing-fixtures/reference/words/__snapshots__/words-50-english-punctuation-contractions.json b/packages/typing-fixtures/reference/words/__snapshots__/words-50-english-punctuation-contractions.json new file mode 100644 index 000000000..6532ce725 --- /dev/null +++ b/packages/typing-fixtures/reference/words/__snapshots__/words-50-english-punctuation-contractions.json @@ -0,0 +1,65 @@ +{ + "words": [ + "Become ", + "back ", + "- ", + "shouldn't ", + "present ", + "no, ", + "last ", + "may ", + "open ", + "take ", + "know, ", + "while ", + "late: ", + "might, ", + "while ", + "say ", + "just ", + "same ", + "person, ", + "house ", + "I ", + "work ", + "and. ", + "Real ", + "group ", + "part ", + "these, ", + "the ", + "so ", + "who. ", + "For ", + "into ", + "if ", + "own ", + "move ", + "around ", + "when ", + "house ", + "not. ", + "Begin ", + "couldn't ", + "turn, ", + "for ", + "leave ", + "late ", + "last ", + "it'll ", + "on ", + "public ", + "may. " + ], + "sectionIndexes": [ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50 + ], + "hasTab": false, + "hasNewline": false, + "allJoiningScript": false, + "extra": [], + "allWordsGenerated": true, + "currentQuote": null +} diff --git a/packages/typing-fixtures/reference/words/__snapshots__/words-50-punctuation-numbers.json b/packages/typing-fixtures/reference/words/__snapshots__/words-50-punctuation-numbers.json new file mode 100644 index 000000000..7ac4a1cae --- /dev/null +++ b/packages/typing-fixtures/reference/words/__snapshots__/words-50-punctuation-numbers.json @@ -0,0 +1,65 @@ +{ + "words": [ + "250 ", + "the ", + "also ", + "out ", + "number ", + "than ", + "for ", + "all, ", + "take ", + "can: ", + "that, ", + "program ", + "plan ", + "around. ", + "Seem ", + "not ", + "eye ", + "to. ", + "Who ", + "early ", + "of ", + "course, ", + "head ", + "when ", + "some, ", + "into ", + "make ", + "by ", + "school, ", + "change ", + "the ", + "96 ", + "people ", + "another, ", + "open ", + "who ", + "out ", + "'I' ", + "98 ", + "number ", + "a ", + "3015 ", + "7814 ", + "some ", + "long, ", + "public ", + "when, ", + "could ", + "mean ", + "no! " + ], + "sectionIndexes": [ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50 + ], + "hasTab": false, + "hasNewline": false, + "allJoiningScript": false, + "extra": [], + "allWordsGenerated": true, + "currentQuote": null +} diff --git a/packages/typing-fixtures/reference/words/__snapshots__/zen.json b/packages/typing-fixtures/reference/words/__snapshots__/zen.json new file mode 100644 index 000000000..9d04943d5 --- /dev/null +++ b/packages/typing-fixtures/reference/words/__snapshots__/zen.json @@ -0,0 +1,10 @@ +{ + "words": [], + "sectionIndexes": [], + "hasTab": false, + "hasNewline": false, + "allJoiningScript": false, + "extra": [], + "allWordsGenerated": false, + "currentQuote": null +} diff --git a/packages/typing-fixtures/reference/words/cases.json b/packages/typing-fixtures/reference/words/cases.json new file mode 100644 index 000000000..eae7165ef --- /dev/null +++ b/packages/typing-fixtures/reference/words/cases.json @@ -0,0 +1,687 @@ +[ + { + "name": "words-10", + "phase": "base", + "seed": 1, + "config": { + "mode": "words", + "words": 10 + } + }, + { + "name": "words-0-infinite", + "phase": "base", + "seed": 2, + "config": { + "mode": "words", + "words": 0 + }, + "extraWords": 5 + }, + { + "name": "time-30", + "phase": "base", + "seed": 3, + "config": { + "mode": "time", + "time": 30 + }, + "extraWords": 10 + }, + { + "name": "words-25-punctuation", + "phase": "base", + "seed": 4, + "config": { + "mode": "words", + "words": 25, + "punctuation": true + } + }, + { + "name": "words-25-numbers", + "phase": "base", + "seed": 5, + "config": { + "mode": "words", + "words": 25, + "numbers": true + } + }, + { + "name": "words-50-punctuation-numbers", + "phase": "base", + "seed": 6, + "config": { + "mode": "words", + "words": 50, + "punctuation": true, + "numbers": true + } + }, + { + "name": "words-100-show-all-lines", + "phase": "base", + "seed": 7, + "config": { + "mode": "words", + "words": 150, + "showAllLines": true + } + }, + { + "name": "time-15-english-1k", + "phase": "base", + "seed": 8, + "config": { + "mode": "time", + "time": 15, + "language": "english_1k" + } + }, + { + "name": "words-30-spanish-punctuation", + "phase": "base", + "seed": 9, + "config": { + "mode": "words", + "words": 30, + "language": "spanish", + "punctuation": true + } + }, + { + "name": "words-30-french-punctuation", + "phase": "base", + "seed": 10, + "config": { + "mode": "words", + "words": 30, + "language": "french", + "punctuation": true + } + }, + { + "name": "words-30-german", + "phase": "base", + "seed": 11, + "config": { + "mode": "words", + "words": 30, + "language": "german" + } + }, + { + "name": "words-30-code-javascript-punctuation", + "phase": "base", + "seed": 12, + "config": { + "mode": "words", + "words": 30, + "language": "code_javascript", + "punctuation": true + } + }, + { + "name": "words-30-arabic-numbers", + "phase": "base", + "seed": 13, + "config": { + "mode": "words", + "words": 30, + "language": "arabic", + "numbers": true, + "punctuation": true + } + }, + { + "name": "words-20-repeated", + "phase": "base", + "seed": 14, + "config": { + "mode": "words", + "words": 20 + }, + "repeat": true + }, + { + "name": "quote-short", + "phase": "quote-custom-zen", + "seed": 20, + "config": { + "mode": "quote", + "quoteLength": [0] + } + }, + { + "name": "quote-all", + "phase": "quote-custom-zen", + "seed": 21, + "config": { + "mode": "quote", + "quoteLength": [0, 1, 2, 3] + } + }, + { + "name": "quote-repeated", + "phase": "quote-custom-zen", + "seed": 22, + "config": { + "mode": "quote", + "quoteLength": [1] + }, + "repeat": true + }, + { + "name": "quote-by-id", + "phase": "quote-custom-zen", + "seed": 23, + "config": { + "mode": "quote", + "quoteLength": [-2] + }, + "selectedQuoteId": 13 + }, + { + "name": "custom-repeat", + "phase": "quote-custom-zen", + "seed": 24, + "config": { + "mode": "custom" + }, + "customText": { + "text": [ + "The", + "quick", + "brown", + "fox", + "jumps", + "over", + "the", + "lazy", + "dog" + ], + "mode": "repeat", + "limit": { + "value": 9, + "mode": "word" + }, + "pipeDelimiter": false + } + }, + { + "name": "custom-random", + "phase": "quote-custom-zen", + "seed": 25, + "config": { + "mode": "custom" + }, + "customText": { + "text": ["alpha", "beta", "gamma", "delta", "epsilon", "zeta"], + "mode": "random", + "limit": { + "value": 30, + "mode": "word" + }, + "pipeDelimiter": false + } + }, + { + "name": "custom-shuffle", + "phase": "quote-custom-zen", + "seed": 26, + "config": { + "mode": "custom" + }, + "customText": { + "text": ["one", "two", "three", "four", "five"], + "mode": "shuffle", + "limit": { + "value": 10, + "mode": "word" + }, + "pipeDelimiter": false + } + }, + { + "name": "custom-time", + "phase": "quote-custom-zen", + "seed": 27, + "config": { + "mode": "custom" + }, + "customText": { + "text": ["tick", "tock", "clock"], + "mode": "random", + "limit": { + "value": 30, + "mode": "time" + }, + "pipeDelimiter": false + }, + "extraWords": 5 + }, + { + "name": "custom-sections", + "phase": "quote-custom-zen", + "seed": 28, + "config": { + "mode": "custom" + }, + "customText": { + "text": ["the cat sat", "on the mat", "a dog ran", "far away"], + "mode": "random", + "limit": { + "value": 5, + "mode": "section" + }, + "pipeDelimiter": true + } + }, + { + "name": "custom-newlines-tabs", + "phase": "quote-custom-zen", + "seed": 29, + "config": { + "mode": "custom" + }, + "customText": { + "text": ["line\n", "\tindented", "word", "end\n"], + "mode": "repeat", + "limit": { + "value": 4, + "mode": "word" + }, + "pipeDelimiter": false + } + }, + { + "name": "custom-repeated", + "phase": "quote-custom-zen", + "seed": 30, + "config": { + "mode": "custom" + }, + "customText": { + "text": ["red", "green", "blue", "cyan"], + "mode": "random", + "limit": { + "value": 12, + "mode": "word" + }, + "pipeDelimiter": false + }, + "repeat": true + }, + { + "name": "zen", + "phase": "quote-custom-zen", + "seed": 31, + "config": { + "mode": "zen" + } + }, + { + "name": "words-30-lazy-french", + "phase": "language-helpers", + "seed": 40, + "config": { + "mode": "words", + "words": 30, + "language": "french", + "lazyMode": true + } + }, + { + "name": "words-30-lazy-polish-punctuation", + "phase": "language-helpers", + "seed": 41, + "config": { + "mode": "words", + "words": 30, + "language": "polish", + "lazyMode": true, + "punctuation": true + } + }, + { + "name": "words-50-english-punctuation-contractions", + "phase": "language-helpers", + "seed": 42, + "config": { + "mode": "words", + "words": 50, + "punctuation": true + } + }, + { + "name": "words-50-british-english", + "phase": "language-helpers", + "seed": 43, + "config": { + "mode": "words", + "words": 50, + "language": "english_1k", + "britishEnglish": true, + "punctuation": true + } + }, + { + "name": "quote-british-english", + "phase": "language-helpers", + "seed": 44, + "config": { + "mode": "quote", + "quoteLength": [2], + "britishEnglish": true + } + }, + { + "name": "words-30-swiss-german", + "phase": "language-helpers", + "seed": 45, + "config": { + "mode": "words", + "words": 30, + "language": "swiss_german" + } + }, + { + "name": "words-30-turkish-punctuation", + "phase": "language-helpers", + "seed": 46, + "config": { + "mode": "words", + "words": 30, + "language": "turkish", + "punctuation": true + } + }, + { + "name": "funbox-58008", + "phase": "funbox", + "seed": 60, + "config": { + "mode": "words", + "words": 20, + "funbox": ["58008"], + "punctuation": true + } + }, + { + "name": "funbox-arrows", + "phase": "funbox", + "seed": 61, + "config": { + "mode": "words", + "words": 20, + "funbox": ["arrows"] + } + }, + { + "name": "funbox-rAnDoMcAsE", + "phase": "funbox", + "seed": 62, + "config": { + "mode": "words", + "words": 20, + "funbox": ["rAnDoMcAsE"] + } + }, + { + "name": "funbox-sPoNgEcAsE", + "phase": "funbox", + "seed": 63, + "config": { + "mode": "words", + "words": 20, + "funbox": ["sPoNgEcAsE"] + } + }, + { + "name": "funbox-capitals", + "phase": "funbox", + "seed": 64, + "config": { + "mode": "words", + "words": 20, + "funbox": ["capitals"] + } + }, + { + "name": "funbox-gibberish", + "phase": "funbox", + "seed": 65, + "config": { + "mode": "words", + "words": 20, + "funbox": ["gibberish"] + } + }, + { + "name": "funbox-ascii", + "phase": "funbox", + "seed": 66, + "config": { + "mode": "words", + "words": 20, + "funbox": ["ascii"] + } + }, + { + "name": "funbox-specials", + "phase": "funbox", + "seed": 67, + "config": { + "mode": "words", + "words": 20, + "funbox": ["specials"] + } + }, + { + "name": "funbox-plus_one", + "phase": "funbox", + "seed": 68, + "config": { + "mode": "words", + "words": 20, + "funbox": ["plus_one"] + } + }, + { + "name": "funbox-plus_three", + "phase": "funbox", + "seed": 69, + "config": { + "mode": "words", + "words": 20, + "funbox": ["plus_three"] + } + }, + { + "name": "funbox-nospace", + "phase": "funbox", + "seed": 70, + "config": { + "mode": "words", + "words": 20, + "funbox": ["nospace"] + } + }, + { + "name": "funbox-weakspot", + "phase": "funbox", + "seed": 71, + "config": { + "mode": "words", + "words": 20, + "funbox": ["weakspot"] + } + }, + { + "name": "funbox-pseudolang", + "phase": "funbox", + "seed": 72, + "config": { + "mode": "words", + "words": 20, + "funbox": ["pseudolang"] + } + }, + { + "name": "funbox-IPv4", + "phase": "funbox", + "seed": 73, + "config": { + "mode": "words", + "words": 20, + "funbox": ["IPv4"], + "punctuation": true + } + }, + { + "name": "funbox-IPv6", + "phase": "funbox", + "seed": 74, + "config": { + "mode": "words", + "words": 20, + "funbox": ["IPv6"], + "punctuation": true + } + }, + { + "name": "funbox-binary", + "phase": "funbox", + "seed": 75, + "config": { + "mode": "words", + "words": 20, + "funbox": ["binary"] + } + }, + { + "name": "funbox-hexadecimal", + "phase": "funbox", + "seed": 76, + "config": { + "mode": "words", + "words": 20, + "funbox": ["hexadecimal"], + "punctuation": true + } + }, + { + "name": "funbox-zipf", + "phase": "funbox", + "seed": 77, + "config": { + "mode": "words", + "words": 20, + "funbox": ["zipf"] + } + }, + { + "name": "funbox-morse", + "phase": "funbox", + "seed": 78, + "config": { + "mode": "words", + "words": 20, + "funbox": ["morse"] + } + }, + { + "name": "funbox-backwards", + "phase": "funbox", + "seed": 79, + "config": { + "mode": "words", + "words": 20, + "funbox": ["backwards"] + } + }, + { + "name": "funbox-ddoouubblleedd", + "phase": "funbox", + "seed": 80, + "config": { + "mode": "words", + "words": 20, + "funbox": ["ddoouubblleedd"] + } + }, + { + "name": "funbox-instant_messaging", + "phase": "funbox", + "seed": 81, + "config": { + "mode": "words", + "words": 20, + "funbox": ["instant_messaging"], + "punctuation": true + } + }, + { + "name": "funbox-underscore_spaces", + "phase": "funbox", + "seed": 82, + "config": { + "mode": "words", + "words": 20, + "funbox": ["underscore_spaces"] + } + }, + { + "name": "funbox-ALL_CAPS", + "phase": "funbox", + "seed": 83, + "config": { + "mode": "words", + "words": 20, + "funbox": ["ALL_CAPS"] + } + }, + { + "name": "funbox-rot13", + "phase": "funbox", + "seed": 84, + "config": { + "mode": "words", + "words": 20, + "funbox": ["rot13"] + } + }, + { + "name": "funbox-polyglot", + "phase": "funbox", + "seed": 85, + "config": { + "mode": "words", + "words": 30, + "funbox": ["polyglot"], + "customPolyglot": ["english", "spanish", "french"] + } + }, + { + "name": "funbox-rot13-capitals-time", + "phase": "funbox", + "seed": 86, + "config": { + "mode": "time", + "time": 30, + "funbox": ["rot13", "capitals"] + }, + "extraWords": 5 + }, + { + "name": "funbox-backwards-quote", + "phase": "funbox", + "seed": 87, + "config": { + "mode": "quote", + "quoteLength": [0], + "funbox": ["backwards"] + } + } +] From 0890a7f2ddc0d71f3c1c52f792b3cfe47f940f69 Mon Sep 17 00:00:00 2001 From: Lakshmi Tanmay Date: Thu, 22 Oct 2026 23:51:28 +0530 Subject: [PATCH 04/93] test(engine): freeze UTF-16 commands and intermediate TypeScript oracle --- docs/rust-engine/compatibility.md | 26 + docs/rust-engine/contract.md | 122 +++ docs/rust-engine/decisions.md | 13 + docs/rust-engine/progress.md | 12 +- .../typing-core/__test__/rust-oracle.spec.ts | 54 ++ .../scripts/capture-rust-oracle.ts | 17 + packages/typing-core/scripts/rust-oracle.ts | 148 +++ packages/typing-fixtures/commands.schema.json | 65 ++ .../typing-fixtures/commands/code-indent.json | 30 + .../commands/delete-error-letter.json | 34 + .../commands/delete-error-letter_hard.json | 34 + .../commands/delete-error-word.json | 34 + .../commands/delete-error-word_hard.json | 34 + .../typing-fixtures/commands/finite-edit.json | 51 ++ .../commands/freedom-regression.json | 34 + .../commands/ime-typography.json | 25 + .../commands/infinite-words.json | 26 + .../commands/master-finish-once.json | 37 + .../typing-fixtures/commands/stop-letter.json | 24 + .../typing-fixtures/commands/timer-stall.json | 38 + .../commands/unicode-halves.json | 33 + .../typing-fixtures/oracle/code-indent.json | 478 ++++++++++ .../oracle/delete-error-letter.json | 564 ++++++++++++ .../oracle/delete-error-letter_hard.json | 640 +++++++++++++ .../oracle/delete-error-word.json | 520 +++++++++++ .../oracle/delete-error-word_hard.json | 552 ++++++++++++ .../typing-fixtures/oracle/finite-edit.json | 842 ++++++++++++++++++ .../oracle/freedom-regression.json | 437 +++++++++ .../oracle/ime-typography.json | 190 ++++ .../oracle/infinite-words.json | 509 +++++++++++ .../oracle/master-finish-once.json | 323 +++++++ .../typing-fixtures/oracle/stop-letter.json | 206 +++++ .../typing-fixtures/oracle/timer-stall.json | 339 +++++++ .../oracle/unicode-halves.json | 282 ++++++ 34 files changed, 6769 insertions(+), 4 deletions(-) create mode 100644 docs/rust-engine/compatibility.md create mode 100644 docs/rust-engine/contract.md create mode 100644 docs/rust-engine/decisions.md create mode 100644 packages/typing-core/__test__/rust-oracle.spec.ts create mode 100644 packages/typing-core/scripts/capture-rust-oracle.ts create mode 100644 packages/typing-core/scripts/rust-oracle.ts create mode 100644 packages/typing-fixtures/commands.schema.json create mode 100644 packages/typing-fixtures/commands/code-indent.json create mode 100644 packages/typing-fixtures/commands/delete-error-letter.json create mode 100644 packages/typing-fixtures/commands/delete-error-letter_hard.json create mode 100644 packages/typing-fixtures/commands/delete-error-word.json create mode 100644 packages/typing-fixtures/commands/delete-error-word_hard.json create mode 100644 packages/typing-fixtures/commands/finite-edit.json create mode 100644 packages/typing-fixtures/commands/freedom-regression.json create mode 100644 packages/typing-fixtures/commands/ime-typography.json create mode 100644 packages/typing-fixtures/commands/infinite-words.json create mode 100644 packages/typing-fixtures/commands/master-finish-once.json create mode 100644 packages/typing-fixtures/commands/stop-letter.json create mode 100644 packages/typing-fixtures/commands/timer-stall.json create mode 100644 packages/typing-fixtures/commands/unicode-halves.json create mode 100644 packages/typing-fixtures/oracle/code-indent.json create mode 100644 packages/typing-fixtures/oracle/delete-error-letter.json create mode 100644 packages/typing-fixtures/oracle/delete-error-letter_hard.json create mode 100644 packages/typing-fixtures/oracle/delete-error-word.json create mode 100644 packages/typing-fixtures/oracle/delete-error-word_hard.json create mode 100644 packages/typing-fixtures/oracle/finite-edit.json create mode 100644 packages/typing-fixtures/oracle/freedom-regression.json create mode 100644 packages/typing-fixtures/oracle/ime-typography.json create mode 100644 packages/typing-fixtures/oracle/infinite-words.json create mode 100644 packages/typing-fixtures/oracle/master-finish-once.json create mode 100644 packages/typing-fixtures/oracle/stop-letter.json create mode 100644 packages/typing-fixtures/oracle/timer-stall.json create mode 100644 packages/typing-fixtures/oracle/unicode-halves.json diff --git a/docs/rust-engine/compatibility.md b/docs/rust-engine/compatibility.md new file mode 100644 index 000000000..46f0d4fee --- /dev/null +++ b/docs/rust-engine/compatibility.md @@ -0,0 +1,26 @@ +# Compatibility coverage map + +Never regenerate existing reference snapshots. New oracle traces were captured +once from the unchanged TypeScript session; capture refuses existing output. + +| Contract | Reference / additional required verification | +| --- | --- | +| Per-unit commits, edits, finish/reset | commands/finite-edit; existing session tests | +| Isolated UTF-16 halves, combining marks | commands/unicode-halves | +| IME metadata, typography/space normalization | commands/ime-typography; browser IME characterization required 1.4 | +| Backtracking | commands/freedom-regression; Firefox clearedNextWord browser case required 1.4 | +| Automatic code tabs/unindent | commands/code-indent; deferred browser task ordering required 1.4 | +| Timer stalls and finish/reset races | commands/timer-stall; existing timer specs | +| Infinite limits | commands/infinite-words; timed/zen zero-limit cases required 1.4 | +| Error policies | commands/delete-error-*; stop-letter; master-finish-once | +| Generation / random consumption | reference/generator + 61 existing snapshots; connect native/Wasm at 1.5 | +| Scores, hashes, history, replay | reference/keystrokes + snapshots; original frontend characterization at 1.6 | +| Backend rejection behavior | reference/anticheat; backend/__tests__/anticheat | +| Browser-only DOM/composition behavior | frontend/__tests__/input; e2e/migration-characterization; expand for recorded differences in contract | +| Transport schema | packages/schemas/src/results.ts; mechanical binding/CompletedEvent check at 1.6–1.7 | +| SSR and immutable asset pairing | Start delivery/update suites at 1.3 and 2.5 | + +These are classified gaps, not coverage claims. Phase 1.2 freezes the comparison +mechanism; later phases must fill their respective gaps before their gates pass. +The deliberate-mismatch test changes the first word index and requires comparison +to fail; fixture-integrity test checks every original and copied file hash. diff --git a/docs/rust-engine/contract.md b/docs/rust-engine/contract.md new file mode 100644 index 000000000..556c999af --- /dev/null +++ b/docs/rust-engine/contract.md @@ -0,0 +1,122 @@ +# Engine contract v1 + +Binding version 1; behavior version 1; stored web event-log version 1. These are +independent. Reject a binding version mismatch before creating a session. Existing +logs keep version 1 and their interpretation. This migration changes no score rule. + +## Units and representation + +`Text = u16[]`: JavaScript UTF-16 code units, including isolated surrogates. All +target words, committed text, input snapshots and composition text cross the +neutral boundary as Text. Rust stores these losslessly; never use `chars().count()` +for `charIndex`, input length, slicing, or scoring. Browser glue converts Text with +bounded `String.fromCharCode` chunks and `charCodeAt`. UTF-8 conversion is allowed +only for host display, with explicit replacement of isolated surrogates. Terminal +graphemes/cells are display units; retain UTF-16 offsets in results. + +`now`: finite monotonic milliseconds, host-captured. Recorder rounds to two decimal +places using existing JS roundTo2 semantics. Wall-clock `date` is separate explicit +Unix milliseconds, supplied by host, never used for input intervals. Native host +uses Instant for now. Equal timestamps preserve capture order internally; stored +logs preserve the legacy stable tie ordering: keyup, keydown, input, composition, +timer (input before composition when tied, matching recorder group merge). + +Optional properties are omitted, never replaced by false/null. Legacy JSON converts +nonfinite derived numbers to null. Published rounded scores require exact equality; +unrounded float fields compare exact IEEE-754 values unless a documented field +tolerance is established by cross-platform evidence. No blanket epsilon. + +## Ownership and commands + +One independent session owns word index, input history, rule config, recorder, +timer, generator, pace and completion. No global state, implicit clock, I/O, DOM, +callback that overrides progression, or platform RNG. Hosts own scheduling, +observations, assets, rendering, storage, transport and account fields. + +Commands are synchronous after module/content preparation. Initial proof commands: + +- `insert {data: Text, now, date, correctShiftUsed?, automatic?, isCompositionEnding?}` +- `delete {kind: deleteContentBackward|deleteWordBackward, now, date}` +- `start`, `advance`, `finish {reason?}`, `end`, `abandon` with now/date +- `observe {type: keydown|keyup|composition|timer, data, now}` +- `reset`: retain prepared content, clear recorder/timer/input/finish state + +Full port adds config projection/update, browser observations, content requests, +generation/repeat, pace/weak-spot operations, result derivation and historical replay. +Typed Rust declarations must define these before use; proof coverage does not imply +the full surface is ready. Full snapshots/event logs are explicit reads; hot-path +updates contain ordered new events, changed words, cursor/current input, status, +tick/finish notifications. No growing full log copied per key. + +## Lifecycle and failures + +Created → ready → active → ended; reset returns ready. Finish emits once. End and +abandon stop silently; abandon measures incomplete engaged time. After ended, +typing/ticks cannot restart; release observations can finalize held keys until +disposal. Disposal releases instance allocation; repeated disposal is harmless; +all other stale-handle commands fail structurally. A reset increments a generation +token. Async assets and queued automatic indentation carry that token; late replies +cannot affect another generation. Initialization failure leaves input disabled and +allows retry with a new instance. Commands validate before mutation. + +Content loading is host-owned. Rust requests a typed token; host supplies validated +content with the matching token/generation. While awaiting content, block new typing +as the current web does; do not queue unbounded text. Reset cancels pending requests. +Prepare content ahead where possible. Infinite generation stays bounded per request. + +## Browser observations that affect existing behavior + +Beforeinput observes actual textarea/sentinel deletion range, word visibility, +overflow measurements, modifiers/Shift correctness, composition state and captured +time. Rust decides acceptance/progression. DOM absence must not silently become +freedom to backtrack beyond the old visible window. Firefox can delete sentinel +plus residue; preserve clearedNextWord. Host callbacks do not supply word index. + +Known headless/web differences requiring recorded web cases before input cutover: + +- Web checks failure/completion after the last multi-input unit and outside active + composition; headless session checks every unit. +- Web schedules automatic tabs on a later task; headless session inserts recursively. +- Web records DOM-based limits/visible words; headless session has no observations. +- Web normalizes prior to ellipsis expansion; headless path checks blocking first. + +Do not assume passing TestSession fixtures proves these paths. Existing frontend +handler tests/browser characterization remain authoritative; add cases before 1.4. + +## Configuration and random source + +Legacy settings migration, labels, themes, display preferences remain TS and SSR +safe. Engine config is a normalized projection, never the entire web preferences +object. Preserve current effective live rule reads; capture config at each command. +Generation reads config at its existing generation boundaries. Do not freeze a +currently live setting silently. Rust defaults/capabilities generate checked host +metadata; transport Zod schemas remain authoritative and mechanically checked. + +Fixtures supply the exact floating-point random stream in [0,1); exhaustion is an +error, not reseeding. Production uses a specified seeded algorithm, host-provided +seed, stable draw order; seed algorithm/behavior version accompanies native history. +Legacy generator snapshots require their existing seedrandom stream, not matching +only distributions. Host asset loaders supply English punctuation/replacement data; +Rust decides transform order and random consumption. + +## Results, replay and host transport + +Rust derives shared scores/histories/eligibility and replay from versioned logs. +Physical observations are optional capabilities. No keyup capability means durations +unavailable, never invented zeros/releases. Browser synthetic releases retain the +existing estimated marker. Native summaries distinguish unavailable from measured +zero. Terminal stores no account credentials and submits no web results. + +Web host adds uid/tags/challenge/network metadata and runs the existing object-hash +helper after those fields. Preserve schemas, omitted fields and backend validation. +Hashing is transport, not a Rust JSON hash. Full historical fixtures and anticheat +rejection tests must pass before removing TS result/replay implementations. + +## Differential evidence + +Immutable original fixtures: packages/typing-fixtures/reference, copied byte-for-byte +from the starting commit; original hashes in reference-manifest.json. New command +fixtures: commands/*.json, with frozen TypeScript frames in oracle/*.json. +Compare every command's state and events, then final derived values. A harness +self-test must reject a deliberately altered word index. New captures refuse to +overwrite files. Existing snapshots are never updated by the harness. diff --git a/docs/rust-engine/decisions.md b/docs/rust-engine/decisions.md new file mode 100644 index 000000000..d20322e9e --- /dev/null +++ b/docs/rust-engine/decisions.md @@ -0,0 +1,13 @@ +# Migration decisions + +- Sole implementation owner; three stacked stage PRs, as requested. +- Text boundary uses UTF-16 unit arrays. Rust String cannot preserve an isolated + surrogate; unit arrays avoid a lossy JSON string roundtrip and work natively. +- Keep original fixtures during Stage 1; neutral reference package holds exact + copies, checked by SHA-256. Removal of old package waits for Stage 2 gate. +- Record web/headless differences in contract. Headless session is an oracle for + matching cases, not permission to change browser behavior. +- Use installed Rust 1.99.1 as candidate pin. Confirm target/tool compatibility + before finalizing Phase 1.3; no lower MSRV support claimed without a build. +- Offline terminal initial scope excludes accounts/sync. Publication follows + repository release authorization; no production deployment implied by this task. diff --git a/docs/rust-engine/progress.md b/docs/rust-engine/progress.md index 6cb95b86d..058151e82 100644 --- a/docs/rust-engine/progress.md +++ b/docs/rust-engine/progress.md @@ -12,15 +12,15 @@ empty commits to inflate count. Do not publish production or releases implicitly ## Current position -Stage 1, Phase 1.1 complete; Phase 1.2 in progress. Read root AGENTS.md and the complete migration +Stage 1, Phases 1.1–1.2 complete; Phase 1.3 in progress. Read root AGENTS.md and the complete migration plan before edits. Installed workspace dependencies with frozen lockfile. -Phase 1.1 gate passed: inventory, feature map, reproducible browser/performance baseline. Existing website engine remains authoritative. +Phase 1.1 and 1.2 gates passed: inventory/baselines, lossless contract, frozen oracle and mismatch detection. Existing website engine remains authoritative. | Phase | Status | Evidence / remaining gate | | --- | --- | --- | | 1.1 Inventory / baselines | Complete | See inventory.md, features.md, baseline.md and raw measurements. | -| 1.2 Contracts / fixtures | Active | Freeze neutral command contract and differential harness. | -| 1.3 Native / browser proof | Pending | Requires frozen contracts. | +| 1.2 Contracts / fixtures | Complete | 13 command traces, 15 oracle/integrity tests; contract and classified gaps. | +| 1.3 Native / browser proof | Active | Implement finite slice; native/browser/Cloudflare parity and measured budgets pending. | | 1.4 Input / lifecycle / timing | Pending | Requires production browser proof. | | 1.5 Generation / configuration | Pending | Requires contract and proof. | | 1.6 Events / results / replay | Pending | Requires input and generation. | @@ -35,6 +35,10 @@ Phase 1.1 gate passed: inventory, feature map, reproducible browser/performance - Environment: Linux, Node 24.21.0; installed Rust 1.99.1; GitHub CLI authenticated. Toolchain/binding pins not yet selected. Browser/OS support unverified. +- Phase 1.2: `vp test run packages/typing-core/__test__/rust-oracle.spec.ts`: 15 pass. +- Phase 1.2 type-aware lint: pass. Capture created new traces once, refuses overwrite. +- `vp exec rustup target add wasm32-unknown-unknown`: pass. + ## Decisions - User's three-stage PR structure overrides the plan's suggested per-phase PRs. diff --git a/packages/typing-core/__test__/rust-oracle.spec.ts b/packages/typing-core/__test__/rust-oracle.spec.ts new file mode 100644 index 000000000..fb62126bf --- /dev/null +++ b/packages/typing-core/__test__/rust-oracle.spec.ts @@ -0,0 +1,54 @@ +import { createHash } from "node:crypto"; +import { readdirSync, readFileSync } from "node:fs"; +import { resolve } from "node:path"; +import { expect, it } from "vite-plus/test"; +import { readCommandFixture, runOracle } from "../scripts/rust-oracle"; + +const root = resolve(import.meta.dirname, "../../typing-fixtures"); +const cases = readdirSync(resolve(root, "commands")).filter((name) => + name.endsWith(".json"), +); +for (const name of cases) { + it(`frozen intermediate command oracle: ${name}`, async () => { + const fixture = readCommandFixture(resolve(root, "commands", name)); + const expected: unknown = JSON.parse( + readFileSync(resolve(root, "oracle", name), "utf8"), + ); + expect(await runOracle(fixture)).toEqual(expected); + }); +} + +it("the harness rejects an introduced progression mismatch", async () => { + const fixture = readCommandFixture( + resolve(root, "commands/finite-edit.json"), + ); + const actual = await runOracle(fixture); + const wrong = structuredClone(actual) as { + frames: { state: { wordIndex: number } }[]; + }; + const first = wrong.frames[0]; + if (first === undefined) throw new Error("Missing frame"); + first.state.wordIndex++; + expect(() => expect(actual).toEqual(wrong)).toThrow(); +}); + +it("original and neutral reference fixtures retain the frozen bytes", () => { + const manifest = JSON.parse( + readFileSync( + resolve(root, "../../docs/rust-engine/reference-manifest.json"), + "utf8", + ), + ) as { sha256: Record }; + for (const [path, sha256] of Object.entries(manifest.sha256)) { + const relative = path.replace("packages/typing-core/__fixtures__/", ""); + for (const source of [ + resolve(root, "reference", relative), + resolve(root, "../../", path), + ]) { + expect( + createHash("sha256").update(readFileSync(source)).digest("hex"), + source, + ).toBe(sha256); + } + } +}); diff --git a/packages/typing-core/scripts/capture-rust-oracle.ts b/packages/typing-core/scripts/capture-rust-oracle.ts new file mode 100644 index 000000000..67a62d533 --- /dev/null +++ b/packages/typing-core/scripts/capture-rust-oracle.ts @@ -0,0 +1,17 @@ +import { readdirSync, writeFileSync } from "node:fs"; +import { resolve } from "node:path"; +import { readCommandFixture, runOracle } from "./rust-oracle"; + +const root = resolve(import.meta.dirname, "../../typing-fixtures"); +// Capture only NEW traces. Deliberately no overwrite/update flag. +for (const name of readdirSync(resolve(root, "commands")).filter((entry) => + entry.endsWith(".json"), +)) { + const fixture = readCommandFixture(resolve(root, "commands", name)); + const output = await runOracle(fixture); + writeFileSync( + resolve(root, "oracle", name), + `${JSON.stringify(output, null, 2)}\n`, + { flag: "wx" }, + ); +} diff --git a/packages/typing-core/scripts/rust-oracle.ts b/packages/typing-core/scripts/rust-oracle.ts new file mode 100644 index 000000000..1d3b330f4 --- /dev/null +++ b/packages/typing-core/scripts/rust-oracle.ts @@ -0,0 +1,148 @@ +import { readFileSync } from "node:fs"; +import { getDefaultConfig } from "../src/config/default-config"; +import { createTestSession, type SessionConfig } from "../src/session"; +import type { + EventLog, + TestEventData, + TestEventType, +} from "../src/events/types"; +import type { DeleteInputType } from "../src/input/input-type"; + +export type Text = number[]; +export type OracleCommand = { + op: + | "insert" + | "delete" + | "start" + | "advance" + | "finish" + | "end" + | "abandon" + | "reset" + | "observe"; + now: number; + date?: number; + data?: Text; + kind?: DeleteInputType; + reason?: string; + correctShiftUsed?: boolean | null; + automatic?: true; + isCompositionEnding?: true; + eventType?: TestEventType; + eventData?: TestEventData; +}; +export type CommandFixture = { + name: string; + config: Partial; + words: Text[]; + commands: OracleCommand[]; +}; + +export function units(value: string): Text { + return Array.from({ length: value.length }, (_, index) => + value.charCodeAt(index), + ); +} +export function fromUnits(value: Text): string { + // Fixtures are bounded; production conversion must use bounded chunks too. + let result = ""; + for (let i = 0; i < value.length; i += 4096) { + result += String.fromCharCode(...value.slice(i, i + 4096)); + } + return result; +} +export function neutralLog(log: EventLog): unknown { + return { + ...log, + context: { + ...log.context, + targetWords: log.context.targetWords.map(units), + }, + events: log.events.map((event) => ({ + ...event, + data: { + ...event.data, + ...("inputValue" in event.data + ? { inputValue: units(event.data.inputValue) } + : {}), + ...("data" in event.data ? { data: units(event.data.data) } : {}), + }, + })), + }; +} + +export async function runOracle(fixture: CommandFixture): Promise { + let date = 0; + const session = createTestSession( + { ...getDefaultConfig(), ...fixture.config }, + { + words: fixture.words.map(fromUnits), + dateNow: () => date, + }, + ); + const frames: unknown[] = []; + for (const command of fixture.commands) { + date = command.date ?? 0; + switch (command.op) { + case "insert": + await session.insert(fromUnits(command.data ?? []), command.now, { + correctShiftUsed: command.correctShiftUsed, + automatic: command.automatic, + isCompositionEnding: command.isCompositionEnding, + }); + break; + case "delete": + session.delete(command.kind ?? "deleteContentBackward", command.now); + break; + case "start": + session.start(command.now); + break; + case "advance": + session.advance(command.now); + break; + case "finish": + session.finish(command.now, command.reason); + break; + case "end": + session.end(command.now); + break; + case "abandon": + session.abandon(command.now); + break; + case "reset": + session.reset(); + break; + case "observe": + if ( + command.eventType === undefined || + command.eventData === undefined + ) { + throw new Error("Missing observation"); + } + session.record(command.eventType, command.now, command.eventData); + break; + } + const log = session.buildEventLog(); + frames.push({ + state: { + wordIndex: session.getActiveWordIndex(), + inputValue: units(session.recorder.getCurrentInput()), + active: session.isActive(), + ended: log.events.some( + (event) => event.type === "timer" && event.data.event === "end", + ), + ...(session.getFinishReason() === undefined + ? {} + : { reason: session.getFinishReason() }), + }, + log: neutralLog(log), + }); + } + // JSON is the legacy transport: omit undefined and map nonfinite values to null. + // oxlint-disable-next-line unicorn/prefer-structured-clone -- This captures JSON transport semantics, not a deep clone. + return JSON.parse(JSON.stringify({ name: fixture.name, frames })) as unknown; +} + +export function readCommandFixture(path: string): CommandFixture { + return JSON.parse(readFileSync(path, "utf8")) as CommandFixture; +} diff --git a/packages/typing-fixtures/commands.schema.json b/packages/typing-fixtures/commands.schema.json new file mode 100644 index 000000000..b8adf517e --- /dev/null +++ b/packages/typing-fixtures/commands.schema.json @@ -0,0 +1,65 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "oxytype:engine-command-fixture:1", + "title": "Oxytype deterministic command fixture v1", + "type": "object", + "required": ["name", "config", "words", "commands"], + "additionalProperties": false, + "$defs": { + "text": { + "type": "array", + "items": { "type": "integer", "minimum": 0, "maximum": 65535 } + } + }, + "properties": { + "name": { "type": "string", "minLength": 1 }, + "config": { + "type": "object", + "description": "Legacy Config projection; omitted fields use frozen oracle defaults. Rust bindings generate normalized config separately." + }, + "words": { "type": "array", "items": { "$ref": "#/$defs/text" } }, + "commands": { + "type": "array", + "items": { + "type": "object", + "required": ["op", "now"], + "additionalProperties": false, + "properties": { + "op": { + "enum": [ + "insert", + "delete", + "start", + "advance", + "finish", + "end", + "abandon", + "reset", + "observe" + ] + }, + "now": { "type": "number" }, + "date": { "type": "number" }, + "data": { "$ref": "#/$defs/text" }, + "kind": { "enum": ["deleteContentBackward", "deleteWordBackward"] }, + "reason": { "type": "string" }, + "correctShiftUsed": { "type": ["boolean", "null"] }, + "automatic": { "const": true }, + "isCompositionEnding": { "const": true }, + "eventType": { "enum": ["keydown", "keyup", "composition", "timer"] }, + "eventData": { "type": "object" } + }, + "allOf": [ + { + "if": { "properties": { "op": { "const": "insert" } } }, + "then": { "required": ["data"] } + }, + { + "if": { "properties": { "op": { "const": "observe" } } }, + "then": { "required": ["eventType", "eventData"] } + } + ] + } + } + } +} diff --git a/packages/typing-fixtures/commands/code-indent.json b/packages/typing-fixtures/commands/code-indent.json new file mode 100644 index 000000000..ca1b92d25 --- /dev/null +++ b/packages/typing-fixtures/commands/code-indent.json @@ -0,0 +1,30 @@ +{ + "name": "code-indent", + "config": { + "mode": "words", + "words": 2, + "language": "code_javascript", + "codeUnindentOnBackspace": true + }, + "words": [ + [111, 110, 101, 10], + [9, 9, 116, 119, 111] + ], + "commands": [ + { + "op": "insert", + "data": [111, 110, 101, 10], + "now": 0 + }, + { + "op": "delete", + "now": 100, + "kind": "deleteContentBackward" + }, + { + "op": "insert", + "data": [10, 111, 110, 101, 10], + "now": 200 + } + ] +} diff --git a/packages/typing-fixtures/commands/delete-error-letter.json b/packages/typing-fixtures/commands/delete-error-letter.json new file mode 100644 index 000000000..8b656ef39 --- /dev/null +++ b/packages/typing-fixtures/commands/delete-error-letter.json @@ -0,0 +1,34 @@ +{ + "name": "delete-error-letter", + "config": { + "mode": "words", + "words": 2, + "deleteOnError": "letter" + }, + "words": [ + [111, 110, 101, 32], + [116, 119, 111] + ], + "commands": [ + { + "op": "insert", + "data": [111, 120], + "now": 0 + }, + { + "op": "insert", + "data": [111, 110, 101, 32], + "now": 100 + }, + { + "op": "insert", + "data": [120], + "now": 200 + }, + { + "op": "insert", + "data": [116, 119, 111], + "now": 500 + } + ] +} diff --git a/packages/typing-fixtures/commands/delete-error-letter_hard.json b/packages/typing-fixtures/commands/delete-error-letter_hard.json new file mode 100644 index 000000000..b9171799c --- /dev/null +++ b/packages/typing-fixtures/commands/delete-error-letter_hard.json @@ -0,0 +1,34 @@ +{ + "name": "delete-error-letter_hard", + "config": { + "mode": "words", + "words": 2, + "deleteOnError": "letter_hard" + }, + "words": [ + [111, 110, 101, 32], + [116, 119, 111] + ], + "commands": [ + { + "op": "insert", + "data": [111, 120], + "now": 0 + }, + { + "op": "insert", + "data": [111, 110, 101, 32], + "now": 100 + }, + { + "op": "insert", + "data": [120], + "now": 200 + }, + { + "op": "insert", + "data": [116, 119, 111], + "now": 500 + } + ] +} diff --git a/packages/typing-fixtures/commands/delete-error-word.json b/packages/typing-fixtures/commands/delete-error-word.json new file mode 100644 index 000000000..d95cbfab7 --- /dev/null +++ b/packages/typing-fixtures/commands/delete-error-word.json @@ -0,0 +1,34 @@ +{ + "name": "delete-error-word", + "config": { + "mode": "words", + "words": 2, + "deleteOnError": "word" + }, + "words": [ + [111, 110, 101, 32], + [116, 119, 111] + ], + "commands": [ + { + "op": "insert", + "data": [111, 120], + "now": 0 + }, + { + "op": "insert", + "data": [111, 110, 101, 32], + "now": 100 + }, + { + "op": "insert", + "data": [120], + "now": 200 + }, + { + "op": "insert", + "data": [116, 119, 111], + "now": 500 + } + ] +} diff --git a/packages/typing-fixtures/commands/delete-error-word_hard.json b/packages/typing-fixtures/commands/delete-error-word_hard.json new file mode 100644 index 000000000..a4cf257ef --- /dev/null +++ b/packages/typing-fixtures/commands/delete-error-word_hard.json @@ -0,0 +1,34 @@ +{ + "name": "delete-error-word_hard", + "config": { + "mode": "words", + "words": 2, + "deleteOnError": "word_hard" + }, + "words": [ + [111, 110, 101, 32], + [116, 119, 111] + ], + "commands": [ + { + "op": "insert", + "data": [111, 120], + "now": 0 + }, + { + "op": "insert", + "data": [111, 110, 101, 32], + "now": 100 + }, + { + "op": "insert", + "data": [120], + "now": 200 + }, + { + "op": "insert", + "data": [116, 119, 111], + "now": 500 + } + ] +} diff --git a/packages/typing-fixtures/commands/finite-edit.json b/packages/typing-fixtures/commands/finite-edit.json new file mode 100644 index 000000000..f730a4799 --- /dev/null +++ b/packages/typing-fixtures/commands/finite-edit.json @@ -0,0 +1,51 @@ +{ + "name": "finite-edit", + "config": { + "mode": "words", + "words": 2 + }, + "words": [ + [111, 110, 101, 32], + [116, 119, 111] + ], + "commands": [ + { + "op": "insert", + "data": [111, 120], + "now": 0 + }, + { + "op": "delete", + "now": 50, + "kind": "deleteContentBackward" + }, + { + "op": "insert", + "data": [110, 101, 32], + "now": 100 + }, + { + "op": "insert", + "data": [116, 119, 111], + "now": 1100 + }, + { + "op": "insert", + "data": [120], + "now": 1200 + }, + { + "op": "finish", + "now": 1300 + }, + { + "op": "reset", + "now": 1400 + }, + { + "op": "insert", + "data": [111, 110, 101, 32, 116, 119, 111], + "now": 1500 + } + ] +} diff --git a/packages/typing-fixtures/commands/freedom-regression.json b/packages/typing-fixtures/commands/freedom-regression.json new file mode 100644 index 000000000..e7312f3b0 --- /dev/null +++ b/packages/typing-fixtures/commands/freedom-regression.json @@ -0,0 +1,34 @@ +{ + "name": "freedom-regression", + "config": { + "mode": "words", + "words": 2, + "freedomMode": true + }, + "words": [ + [111, 110, 101, 32], + [116, 119, 111] + ], + "commands": [ + { + "op": "insert", + "data": [111, 110, 101, 32], + "now": 0 + }, + { + "op": "delete", + "now": 100, + "kind": "deleteContentBackward" + }, + { + "op": "insert", + "data": [32], + "now": 200 + }, + { + "op": "insert", + "data": [116, 119, 111], + "now": 400 + } + ] +} diff --git a/packages/typing-fixtures/commands/ime-typography.json b/packages/typing-fixtures/commands/ime-typography.json new file mode 100644 index 000000000..e8fab4a9d --- /dev/null +++ b/packages/typing-fixtures/commands/ime-typography.json @@ -0,0 +1,25 @@ +{ + "name": "ime-typography", + "config": { + "mode": "words", + "words": 2 + }, + "words": [ + [97, 32], + [8220, 98, 8221, 8212] + ], + "commands": [ + { + "op": "insert", + "data": [97, 12288], + "now": 0, + "isCompositionEnding": true + }, + { + "op": "insert", + "data": [34, 98, 34, 45], + "now": 100, + "isCompositionEnding": true + } + ] +} diff --git a/packages/typing-fixtures/commands/infinite-words.json b/packages/typing-fixtures/commands/infinite-words.json new file mode 100644 index 000000000..555289a2b --- /dev/null +++ b/packages/typing-fixtures/commands/infinite-words.json @@ -0,0 +1,26 @@ +{ + "name": "infinite-words", + "config": { + "mode": "words", + "words": 0 + }, + "words": [ + [111, 110, 101, 32], + [116, 119, 111, 32] + ], + "commands": [ + { + "op": "insert", + "data": [111, 110, 101, 32, 116, 119, 111, 32], + "now": 0 + }, + { + "op": "advance", + "now": 5000 + }, + { + "op": "abandon", + "now": 6000 + } + ] +} diff --git a/packages/typing-fixtures/commands/master-finish-once.json b/packages/typing-fixtures/commands/master-finish-once.json new file mode 100644 index 000000000..368469662 --- /dev/null +++ b/packages/typing-fixtures/commands/master-finish-once.json @@ -0,0 +1,37 @@ +{ + "name": "master-finish-once", + "config": { + "mode": "words", + "words": 2, + "difficulty": "master" + }, + "words": [ + [111, 110, 101, 32], + [116, 119, 111] + ], + "commands": [ + { + "op": "insert", + "data": [120], + "now": 0 + }, + { + "op": "insert", + "data": [111, 110, 101, 32, 116, 119, 111], + "now": 100 + }, + { + "op": "finish", + "now": 100 + }, + { + "op": "reset", + "now": 200 + }, + { + "op": "insert", + "data": [111, 110, 101, 32, 116, 119, 111], + "now": 300 + } + ] +} diff --git a/packages/typing-fixtures/commands/stop-letter.json b/packages/typing-fixtures/commands/stop-letter.json new file mode 100644 index 000000000..8c37f5d2d --- /dev/null +++ b/packages/typing-fixtures/commands/stop-letter.json @@ -0,0 +1,24 @@ +{ + "name": "stop-letter", + "config": { + "mode": "words", + "words": 2, + "stopOnError": "letter" + }, + "words": [ + [111, 110, 101, 32], + [116, 119, 111] + ], + "commands": [ + { + "op": "insert", + "data": [111, 120], + "now": 0 + }, + { + "op": "insert", + "data": [110, 101, 32, 116, 119, 111], + "now": 100 + } + ] +} diff --git a/packages/typing-fixtures/commands/timer-stall.json b/packages/typing-fixtures/commands/timer-stall.json new file mode 100644 index 000000000..e3ece2add --- /dev/null +++ b/packages/typing-fixtures/commands/timer-stall.json @@ -0,0 +1,38 @@ +{ + "name": "timer-stall", + "config": { + "mode": "words", + "words": 2 + }, + "words": [ + [111, 110, 101, 32], + [116, 119, 111] + ], + "commands": [ + { + "op": "insert", + "data": [111], + "now": 100 + }, + { + "op": "advance", + "now": 3400 + }, + { + "op": "advance", + "now": 5000 + }, + { + "op": "finish", + "now": 5000 + }, + { + "op": "reset", + "now": 5001 + }, + { + "op": "advance", + "now": 9000 + } + ] +} diff --git a/packages/typing-fixtures/commands/unicode-halves.json b/packages/typing-fixtures/commands/unicode-halves.json new file mode 100644 index 000000000..b1487ca4a --- /dev/null +++ b/packages/typing-fixtures/commands/unicode-halves.json @@ -0,0 +1,33 @@ +{ + "name": "unicode-halves", + "config": { + "mode": "words", + "words": 2 + }, + "words": [ + [55357, 56832, 32], + [101, 769] + ], + "commands": [ + { + "op": "insert", + "data": [55357], + "now": 0 + }, + { + "op": "insert", + "data": [56832], + "now": 20 + }, + { + "op": "insert", + "data": [32], + "now": 40 + }, + { + "op": "insert", + "data": [101, 769], + "now": 100 + } + ] +} diff --git a/packages/typing-fixtures/oracle/code-indent.json b/packages/typing-fixtures/oracle/code-indent.json new file mode 100644 index 000000000..eb0e3f8e2 --- /dev/null +++ b/packages/typing-fixtures/oracle/code-indent.json @@ -0,0 +1,478 @@ +{ + "name": "code-indent", + "frames": [ + { + "state": { + "wordIndex": 1, + "inputValue": [9, 9], + "active": true, + "ended": false + }, + "log": { + "version": 1, + "events": [ + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [111], + "correct": true, + "wordIndex": 0, + "charIndex": 0, + "inputValue": [111] + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [110], + "correct": true, + "wordIndex": 0, + "charIndex": 1, + "inputValue": [111, 110] + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [101], + "correct": true, + "wordIndex": 0, + "charIndex": 2, + "inputValue": [111, 110, 101] + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [10], + "correct": true, + "wordIndex": 0, + "charIndex": 3, + "inputValue": [111, 110, 101, 10], + "commitsWord": true + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [9], + "correct": true, + "wordIndex": 1, + "charIndex": 0, + "inputValue": [9], + "lastWord": true, + "automatic": true + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [9], + "correct": true, + "wordIndex": 1, + "charIndex": 1, + "inputValue": [9, 9], + "lastWord": true, + "automatic": true + } + }, + { + "type": "timer", + "testMs": 0, + "data": { + "event": "start", + "timer": 0, + "date": 0 + } + } + ], + "context": { + "targetWords": [ + [111, 110, 101, 10], + [9, 9, 116, 119, 111] + ], + "mode": "words", + "mode2": "2", + "koreanStatus": false, + "bailedOut": false, + "isFunboxWithNospacePropertyActive": false + } + } + }, + { + "state": { + "wordIndex": 0, + "inputValue": [111, 110, 101], + "active": true, + "ended": false + }, + "log": { + "version": 1, + "events": [ + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [111], + "correct": true, + "wordIndex": 0, + "charIndex": 0, + "inputValue": [111] + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [110], + "correct": true, + "wordIndex": 0, + "charIndex": 1, + "inputValue": [111, 110] + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [101], + "correct": true, + "wordIndex": 0, + "charIndex": 2, + "inputValue": [111, 110, 101] + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [10], + "correct": true, + "wordIndex": 0, + "charIndex": 3, + "inputValue": [111, 110, 101, 10], + "commitsWord": true + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [9], + "correct": true, + "wordIndex": 1, + "charIndex": 0, + "inputValue": [9], + "lastWord": true, + "automatic": true + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [9], + "correct": true, + "wordIndex": 1, + "charIndex": 1, + "inputValue": [9, 9], + "lastWord": true, + "automatic": true + } + }, + { + "type": "timer", + "testMs": 0, + "data": { + "event": "start", + "timer": 0, + "date": 0 + } + }, + { + "type": "input", + "testMs": 100, + "data": { + "inputType": "deleteWordBackward", + "wordIndex": 1, + "charIndex": 2, + "inputValue": [] + } + }, + { + "type": "input", + "testMs": 100, + "data": { + "inputType": "deleteContentBackward", + "wordIndex": 0, + "charIndex": 3, + "inputValue": [111, 110, 101] + } + } + ], + "context": { + "targetWords": [ + [111, 110, 101, 10], + [9, 9, 116, 119, 111] + ], + "mode": "words", + "mode2": "2", + "koreanStatus": false, + "bailedOut": false, + "isFunboxWithNospacePropertyActive": false + } + } + }, + { + "state": { + "wordIndex": 1, + "inputValue": [9, 9, 111, 110, 101, 10], + "active": false, + "ended": true + }, + "log": { + "version": 1, + "events": [ + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [111], + "correct": true, + "wordIndex": 0, + "charIndex": 0, + "inputValue": [111] + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [110], + "correct": true, + "wordIndex": 0, + "charIndex": 1, + "inputValue": [111, 110] + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [101], + "correct": true, + "wordIndex": 0, + "charIndex": 2, + "inputValue": [111, 110, 101] + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [10], + "correct": true, + "wordIndex": 0, + "charIndex": 3, + "inputValue": [111, 110, 101, 10], + "commitsWord": true + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [9], + "correct": true, + "wordIndex": 1, + "charIndex": 0, + "inputValue": [9], + "lastWord": true, + "automatic": true + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [9], + "correct": true, + "wordIndex": 1, + "charIndex": 1, + "inputValue": [9, 9], + "lastWord": true, + "automatic": true + } + }, + { + "type": "timer", + "testMs": 0, + "data": { + "event": "start", + "timer": 0, + "date": 0 + } + }, + { + "type": "input", + "testMs": 100, + "data": { + "inputType": "deleteWordBackward", + "wordIndex": 1, + "charIndex": 2, + "inputValue": [] + } + }, + { + "type": "input", + "testMs": 100, + "data": { + "inputType": "deleteContentBackward", + "wordIndex": 0, + "charIndex": 3, + "inputValue": [111, 110, 101] + } + }, + { + "type": "input", + "testMs": 200, + "data": { + "inputType": "insertText", + "data": [10], + "correct": true, + "wordIndex": 0, + "charIndex": 3, + "inputValue": [111, 110, 101, 10], + "commitsWord": true + } + }, + { + "type": "input", + "testMs": 200, + "data": { + "inputType": "insertText", + "data": [9], + "correct": true, + "wordIndex": 1, + "charIndex": 0, + "inputValue": [9], + "lastWord": true, + "automatic": true + } + }, + { + "type": "input", + "testMs": 200, + "data": { + "inputType": "insertText", + "data": [9], + "correct": true, + "wordIndex": 1, + "charIndex": 1, + "inputValue": [9, 9], + "lastWord": true, + "automatic": true + } + }, + { + "type": "input", + "testMs": 200, + "data": { + "inputType": "insertText", + "data": [111], + "correct": false, + "wordIndex": 1, + "charIndex": 2, + "inputValue": [9, 9, 111], + "lastWord": true + } + }, + { + "type": "input", + "testMs": 200, + "data": { + "inputType": "insertText", + "data": [110], + "correct": false, + "wordIndex": 1, + "charIndex": 3, + "inputValue": [9, 9, 111, 110], + "lastWord": true + } + }, + { + "type": "input", + "testMs": 200, + "data": { + "inputType": "insertText", + "data": [101], + "correct": false, + "wordIndex": 1, + "charIndex": 4, + "inputValue": [9, 9, 111, 110, 101], + "lastWord": true + } + }, + { + "type": "input", + "testMs": 200, + "data": { + "inputType": "insertText", + "data": [10], + "correct": false, + "wordIndex": 1, + "charIndex": 5, + "inputValue": [9, 9, 111, 110, 101, 10], + "commitsWord": true, + "lastWord": true + } + }, + { + "type": "timer", + "testMs": 200, + "data": { + "event": "end", + "timer": 0, + "date": 0 + } + } + ], + "context": { + "targetWords": [ + [111, 110, 101, 10], + [9, 9, 116, 119, 111] + ], + "mode": "words", + "mode2": "2", + "koreanStatus": false, + "bailedOut": false, + "isFunboxWithNospacePropertyActive": false + } + } + } + ] +} diff --git a/packages/typing-fixtures/oracle/delete-error-letter.json b/packages/typing-fixtures/oracle/delete-error-letter.json new file mode 100644 index 000000000..9e38dd46f --- /dev/null +++ b/packages/typing-fixtures/oracle/delete-error-letter.json @@ -0,0 +1,564 @@ +{ + "name": "delete-error-letter", + "frames": [ + { + "state": { + "wordIndex": 0, + "inputValue": [], + "active": true, + "ended": false + }, + "log": { + "version": 1, + "events": [ + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [111], + "correct": true, + "wordIndex": 0, + "charIndex": 0, + "inputValue": [111] + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [120], + "correct": false, + "wordIndex": 0, + "charIndex": 1, + "inputValue": [111, 120] + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "deleteContentBackward", + "wordIndex": 0, + "charIndex": 2, + "inputValue": [111], + "automatic": true + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "deleteContentBackward", + "wordIndex": 0, + "charIndex": 1, + "inputValue": [], + "automatic": true + } + }, + { + "type": "timer", + "testMs": 0, + "data": { + "event": "start", + "timer": 0, + "date": 0 + } + } + ], + "context": { + "targetWords": [ + [111, 110, 101, 32], + [116, 119, 111] + ], + "mode": "words", + "mode2": "2", + "koreanStatus": false, + "bailedOut": false, + "isFunboxWithNospacePropertyActive": false + } + } + }, + { + "state": { + "wordIndex": 1, + "inputValue": [], + "active": true, + "ended": false + }, + "log": { + "version": 1, + "events": [ + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [111], + "correct": true, + "wordIndex": 0, + "charIndex": 0, + "inputValue": [111] + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [120], + "correct": false, + "wordIndex": 0, + "charIndex": 1, + "inputValue": [111, 120] + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "deleteContentBackward", + "wordIndex": 0, + "charIndex": 2, + "inputValue": [111], + "automatic": true + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "deleteContentBackward", + "wordIndex": 0, + "charIndex": 1, + "inputValue": [], + "automatic": true + } + }, + { + "type": "timer", + "testMs": 0, + "data": { + "event": "start", + "timer": 0, + "date": 0 + } + }, + { + "type": "input", + "testMs": 100, + "data": { + "inputType": "insertText", + "data": [111], + "correct": true, + "wordIndex": 0, + "charIndex": 0, + "inputValue": [111] + } + }, + { + "type": "input", + "testMs": 100, + "data": { + "inputType": "insertText", + "data": [110], + "correct": true, + "wordIndex": 0, + "charIndex": 1, + "inputValue": [111, 110] + } + }, + { + "type": "input", + "testMs": 100, + "data": { + "inputType": "insertText", + "data": [101], + "correct": true, + "wordIndex": 0, + "charIndex": 2, + "inputValue": [111, 110, 101] + } + }, + { + "type": "input", + "testMs": 100, + "data": { + "inputType": "insertText", + "data": [32], + "correct": true, + "wordIndex": 0, + "charIndex": 3, + "inputValue": [111, 110, 101, 32], + "commitsWord": true + } + } + ], + "context": { + "targetWords": [ + [111, 110, 101, 32], + [116, 119, 111] + ], + "mode": "words", + "mode2": "2", + "koreanStatus": false, + "bailedOut": false, + "isFunboxWithNospacePropertyActive": false + } + } + }, + { + "state": { + "wordIndex": 1, + "inputValue": [], + "active": true, + "ended": false + }, + "log": { + "version": 1, + "events": [ + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [111], + "correct": true, + "wordIndex": 0, + "charIndex": 0, + "inputValue": [111] + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [120], + "correct": false, + "wordIndex": 0, + "charIndex": 1, + "inputValue": [111, 120] + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "deleteContentBackward", + "wordIndex": 0, + "charIndex": 2, + "inputValue": [111], + "automatic": true + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "deleteContentBackward", + "wordIndex": 0, + "charIndex": 1, + "inputValue": [], + "automatic": true + } + }, + { + "type": "timer", + "testMs": 0, + "data": { + "event": "start", + "timer": 0, + "date": 0 + } + }, + { + "type": "input", + "testMs": 100, + "data": { + "inputType": "insertText", + "data": [111], + "correct": true, + "wordIndex": 0, + "charIndex": 0, + "inputValue": [111] + } + }, + { + "type": "input", + "testMs": 100, + "data": { + "inputType": "insertText", + "data": [110], + "correct": true, + "wordIndex": 0, + "charIndex": 1, + "inputValue": [111, 110] + } + }, + { + "type": "input", + "testMs": 100, + "data": { + "inputType": "insertText", + "data": [101], + "correct": true, + "wordIndex": 0, + "charIndex": 2, + "inputValue": [111, 110, 101] + } + }, + { + "type": "input", + "testMs": 100, + "data": { + "inputType": "insertText", + "data": [32], + "correct": true, + "wordIndex": 0, + "charIndex": 3, + "inputValue": [111, 110, 101, 32], + "commitsWord": true + } + }, + { + "type": "input", + "testMs": 200, + "data": { + "inputType": "insertText", + "data": [120], + "correct": false, + "wordIndex": 1, + "charIndex": 0, + "inputValue": [120], + "lastWord": true + } + }, + { + "type": "input", + "testMs": 200, + "data": { + "inputType": "deleteContentBackward", + "wordIndex": 1, + "charIndex": 1, + "inputValue": [], + "automatic": true + } + } + ], + "context": { + "targetWords": [ + [111, 110, 101, 32], + [116, 119, 111] + ], + "mode": "words", + "mode2": "2", + "koreanStatus": false, + "bailedOut": false, + "isFunboxWithNospacePropertyActive": false + } + } + }, + { + "state": { + "wordIndex": 1, + "inputValue": [116, 119, 111], + "active": false, + "ended": true + }, + "log": { + "version": 1, + "events": [ + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [111], + "correct": true, + "wordIndex": 0, + "charIndex": 0, + "inputValue": [111] + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [120], + "correct": false, + "wordIndex": 0, + "charIndex": 1, + "inputValue": [111, 120] + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "deleteContentBackward", + "wordIndex": 0, + "charIndex": 2, + "inputValue": [111], + "automatic": true + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "deleteContentBackward", + "wordIndex": 0, + "charIndex": 1, + "inputValue": [], + "automatic": true + } + }, + { + "type": "timer", + "testMs": 0, + "data": { + "event": "start", + "timer": 0, + "date": 0 + } + }, + { + "type": "input", + "testMs": 100, + "data": { + "inputType": "insertText", + "data": [111], + "correct": true, + "wordIndex": 0, + "charIndex": 0, + "inputValue": [111] + } + }, + { + "type": "input", + "testMs": 100, + "data": { + "inputType": "insertText", + "data": [110], + "correct": true, + "wordIndex": 0, + "charIndex": 1, + "inputValue": [111, 110] + } + }, + { + "type": "input", + "testMs": 100, + "data": { + "inputType": "insertText", + "data": [101], + "correct": true, + "wordIndex": 0, + "charIndex": 2, + "inputValue": [111, 110, 101] + } + }, + { + "type": "input", + "testMs": 100, + "data": { + "inputType": "insertText", + "data": [32], + "correct": true, + "wordIndex": 0, + "charIndex": 3, + "inputValue": [111, 110, 101, 32], + "commitsWord": true + } + }, + { + "type": "input", + "testMs": 200, + "data": { + "inputType": "insertText", + "data": [120], + "correct": false, + "wordIndex": 1, + "charIndex": 0, + "inputValue": [120], + "lastWord": true + } + }, + { + "type": "input", + "testMs": 200, + "data": { + "inputType": "deleteContentBackward", + "wordIndex": 1, + "charIndex": 1, + "inputValue": [], + "automatic": true + } + }, + { + "type": "input", + "testMs": 500, + "data": { + "inputType": "insertText", + "data": [116], + "correct": true, + "wordIndex": 1, + "charIndex": 0, + "inputValue": [116], + "lastWord": true + } + }, + { + "type": "input", + "testMs": 500, + "data": { + "inputType": "insertText", + "data": [119], + "correct": true, + "wordIndex": 1, + "charIndex": 1, + "inputValue": [116, 119], + "lastWord": true + } + }, + { + "type": "input", + "testMs": 500, + "data": { + "inputType": "insertText", + "data": [111], + "correct": true, + "wordIndex": 1, + "charIndex": 2, + "inputValue": [116, 119, 111], + "lastWord": true + } + }, + { + "type": "timer", + "testMs": 500, + "data": { + "event": "end", + "timer": 0, + "date": 0 + } + } + ], + "context": { + "targetWords": [ + [111, 110, 101, 32], + [116, 119, 111] + ], + "mode": "words", + "mode2": "2", + "koreanStatus": false, + "bailedOut": false, + "isFunboxWithNospacePropertyActive": false + } + } + } + ] +} diff --git a/packages/typing-fixtures/oracle/delete-error-letter_hard.json b/packages/typing-fixtures/oracle/delete-error-letter_hard.json new file mode 100644 index 000000000..bbde8bd9c --- /dev/null +++ b/packages/typing-fixtures/oracle/delete-error-letter_hard.json @@ -0,0 +1,640 @@ +{ + "name": "delete-error-letter_hard", + "frames": [ + { + "state": { + "wordIndex": 0, + "inputValue": [], + "active": true, + "ended": false + }, + "log": { + "version": 1, + "events": [ + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [111], + "correct": true, + "wordIndex": 0, + "charIndex": 0, + "inputValue": [111] + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [120], + "correct": false, + "wordIndex": 0, + "charIndex": 1, + "inputValue": [111, 120] + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "deleteContentBackward", + "wordIndex": 0, + "charIndex": 2, + "inputValue": [111], + "automatic": true + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "deleteContentBackward", + "wordIndex": 0, + "charIndex": 1, + "inputValue": [], + "automatic": true + } + }, + { + "type": "timer", + "testMs": 0, + "data": { + "event": "start", + "timer": 0, + "date": 0 + } + } + ], + "context": { + "targetWords": [ + [111, 110, 101, 32], + [116, 119, 111] + ], + "mode": "words", + "mode2": "2", + "koreanStatus": false, + "bailedOut": false, + "isFunboxWithNospacePropertyActive": false + } + } + }, + { + "state": { + "wordIndex": 1, + "inputValue": [], + "active": true, + "ended": false + }, + "log": { + "version": 1, + "events": [ + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [111], + "correct": true, + "wordIndex": 0, + "charIndex": 0, + "inputValue": [111] + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [120], + "correct": false, + "wordIndex": 0, + "charIndex": 1, + "inputValue": [111, 120] + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "deleteContentBackward", + "wordIndex": 0, + "charIndex": 2, + "inputValue": [111], + "automatic": true + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "deleteContentBackward", + "wordIndex": 0, + "charIndex": 1, + "inputValue": [], + "automatic": true + } + }, + { + "type": "timer", + "testMs": 0, + "data": { + "event": "start", + "timer": 0, + "date": 0 + } + }, + { + "type": "input", + "testMs": 100, + "data": { + "inputType": "insertText", + "data": [111], + "correct": true, + "wordIndex": 0, + "charIndex": 0, + "inputValue": [111] + } + }, + { + "type": "input", + "testMs": 100, + "data": { + "inputType": "insertText", + "data": [110], + "correct": true, + "wordIndex": 0, + "charIndex": 1, + "inputValue": [111, 110] + } + }, + { + "type": "input", + "testMs": 100, + "data": { + "inputType": "insertText", + "data": [101], + "correct": true, + "wordIndex": 0, + "charIndex": 2, + "inputValue": [111, 110, 101] + } + }, + { + "type": "input", + "testMs": 100, + "data": { + "inputType": "insertText", + "data": [32], + "correct": true, + "wordIndex": 0, + "charIndex": 3, + "inputValue": [111, 110, 101, 32], + "commitsWord": true + } + } + ], + "context": { + "targetWords": [ + [111, 110, 101, 32], + [116, 119, 111] + ], + "mode": "words", + "mode2": "2", + "koreanStatus": false, + "bailedOut": false, + "isFunboxWithNospacePropertyActive": false + } + } + }, + { + "state": { + "wordIndex": 0, + "inputValue": [111, 110, 101], + "active": true, + "ended": false + }, + "log": { + "version": 1, + "events": [ + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [111], + "correct": true, + "wordIndex": 0, + "charIndex": 0, + "inputValue": [111] + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [120], + "correct": false, + "wordIndex": 0, + "charIndex": 1, + "inputValue": [111, 120] + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "deleteContentBackward", + "wordIndex": 0, + "charIndex": 2, + "inputValue": [111], + "automatic": true + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "deleteContentBackward", + "wordIndex": 0, + "charIndex": 1, + "inputValue": [], + "automatic": true + } + }, + { + "type": "timer", + "testMs": 0, + "data": { + "event": "start", + "timer": 0, + "date": 0 + } + }, + { + "type": "input", + "testMs": 100, + "data": { + "inputType": "insertText", + "data": [111], + "correct": true, + "wordIndex": 0, + "charIndex": 0, + "inputValue": [111] + } + }, + { + "type": "input", + "testMs": 100, + "data": { + "inputType": "insertText", + "data": [110], + "correct": true, + "wordIndex": 0, + "charIndex": 1, + "inputValue": [111, 110] + } + }, + { + "type": "input", + "testMs": 100, + "data": { + "inputType": "insertText", + "data": [101], + "correct": true, + "wordIndex": 0, + "charIndex": 2, + "inputValue": [111, 110, 101] + } + }, + { + "type": "input", + "testMs": 100, + "data": { + "inputType": "insertText", + "data": [32], + "correct": true, + "wordIndex": 0, + "charIndex": 3, + "inputValue": [111, 110, 101, 32], + "commitsWord": true + } + }, + { + "type": "input", + "testMs": 200, + "data": { + "inputType": "insertText", + "data": [120], + "correct": false, + "wordIndex": 1, + "charIndex": 0, + "inputValue": [120], + "lastWord": true + } + }, + { + "type": "input", + "testMs": 200, + "data": { + "inputType": "deleteContentBackward", + "wordIndex": 1, + "charIndex": 1, + "inputValue": [], + "automatic": true + } + }, + { + "type": "input", + "testMs": 200, + "data": { + "inputType": "deleteContentBackward", + "wordIndex": 0, + "charIndex": 3, + "inputValue": [111, 110, 101], + "automatic": true + } + } + ], + "context": { + "targetWords": [ + [111, 110, 101, 32], + [116, 119, 111] + ], + "mode": "words", + "mode2": "2", + "koreanStatus": false, + "bailedOut": false, + "isFunboxWithNospacePropertyActive": false + } + } + }, + { + "state": { + "wordIndex": 0, + "inputValue": [], + "active": true, + "ended": false + }, + "log": { + "version": 1, + "events": [ + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [111], + "correct": true, + "wordIndex": 0, + "charIndex": 0, + "inputValue": [111] + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [120], + "correct": false, + "wordIndex": 0, + "charIndex": 1, + "inputValue": [111, 120] + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "deleteContentBackward", + "wordIndex": 0, + "charIndex": 2, + "inputValue": [111], + "automatic": true + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "deleteContentBackward", + "wordIndex": 0, + "charIndex": 1, + "inputValue": [], + "automatic": true + } + }, + { + "type": "timer", + "testMs": 0, + "data": { + "event": "start", + "timer": 0, + "date": 0 + } + }, + { + "type": "input", + "testMs": 100, + "data": { + "inputType": "insertText", + "data": [111], + "correct": true, + "wordIndex": 0, + "charIndex": 0, + "inputValue": [111] + } + }, + { + "type": "input", + "testMs": 100, + "data": { + "inputType": "insertText", + "data": [110], + "correct": true, + "wordIndex": 0, + "charIndex": 1, + "inputValue": [111, 110] + } + }, + { + "type": "input", + "testMs": 100, + "data": { + "inputType": "insertText", + "data": [101], + "correct": true, + "wordIndex": 0, + "charIndex": 2, + "inputValue": [111, 110, 101] + } + }, + { + "type": "input", + "testMs": 100, + "data": { + "inputType": "insertText", + "data": [32], + "correct": true, + "wordIndex": 0, + "charIndex": 3, + "inputValue": [111, 110, 101, 32], + "commitsWord": true + } + }, + { + "type": "input", + "testMs": 200, + "data": { + "inputType": "insertText", + "data": [120], + "correct": false, + "wordIndex": 1, + "charIndex": 0, + "inputValue": [120], + "lastWord": true + } + }, + { + "type": "input", + "testMs": 200, + "data": { + "inputType": "deleteContentBackward", + "wordIndex": 1, + "charIndex": 1, + "inputValue": [], + "automatic": true + } + }, + { + "type": "input", + "testMs": 200, + "data": { + "inputType": "deleteContentBackward", + "wordIndex": 0, + "charIndex": 3, + "inputValue": [111, 110, 101], + "automatic": true + } + }, + { + "type": "input", + "testMs": 500, + "data": { + "inputType": "insertText", + "data": [116], + "correct": false, + "wordIndex": 0, + "charIndex": 3, + "inputValue": [111, 110, 101, 116] + } + }, + { + "type": "input", + "testMs": 500, + "data": { + "inputType": "deleteContentBackward", + "wordIndex": 0, + "charIndex": 4, + "inputValue": [111, 110, 101], + "automatic": true + } + }, + { + "type": "input", + "testMs": 500, + "data": { + "inputType": "deleteContentBackward", + "wordIndex": 0, + "charIndex": 3, + "inputValue": [111, 110], + "automatic": true + } + }, + { + "type": "input", + "testMs": 500, + "data": { + "inputType": "insertText", + "data": [119], + "correct": false, + "wordIndex": 0, + "charIndex": 2, + "inputValue": [111, 110, 119] + } + }, + { + "type": "input", + "testMs": 500, + "data": { + "inputType": "deleteContentBackward", + "wordIndex": 0, + "charIndex": 3, + "inputValue": [111, 110], + "automatic": true + } + }, + { + "type": "input", + "testMs": 500, + "data": { + "inputType": "deleteContentBackward", + "wordIndex": 0, + "charIndex": 2, + "inputValue": [111], + "automatic": true + } + }, + { + "type": "input", + "testMs": 500, + "data": { + "inputType": "insertText", + "data": [111], + "correct": false, + "wordIndex": 0, + "charIndex": 1, + "inputValue": [111, 111] + } + }, + { + "type": "input", + "testMs": 500, + "data": { + "inputType": "deleteContentBackward", + "wordIndex": 0, + "charIndex": 2, + "inputValue": [111], + "automatic": true + } + }, + { + "type": "input", + "testMs": 500, + "data": { + "inputType": "deleteContentBackward", + "wordIndex": 0, + "charIndex": 1, + "inputValue": [], + "automatic": true + } + } + ], + "context": { + "targetWords": [ + [111, 110, 101, 32], + [116, 119, 111] + ], + "mode": "words", + "mode2": "2", + "koreanStatus": false, + "bailedOut": false, + "isFunboxWithNospacePropertyActive": false + } + } + } + ] +} diff --git a/packages/typing-fixtures/oracle/delete-error-word.json b/packages/typing-fixtures/oracle/delete-error-word.json new file mode 100644 index 000000000..83b1dda4d --- /dev/null +++ b/packages/typing-fixtures/oracle/delete-error-word.json @@ -0,0 +1,520 @@ +{ + "name": "delete-error-word", + "frames": [ + { + "state": { + "wordIndex": 0, + "inputValue": [], + "active": true, + "ended": false + }, + "log": { + "version": 1, + "events": [ + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [111], + "correct": true, + "wordIndex": 0, + "charIndex": 0, + "inputValue": [111] + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [120], + "correct": false, + "wordIndex": 0, + "charIndex": 1, + "inputValue": [111, 120] + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "deleteWordBackward", + "wordIndex": 0, + "charIndex": 2, + "inputValue": [], + "automatic": true + } + }, + { + "type": "timer", + "testMs": 0, + "data": { + "event": "start", + "timer": 0, + "date": 0 + } + } + ], + "context": { + "targetWords": [ + [111, 110, 101, 32], + [116, 119, 111] + ], + "mode": "words", + "mode2": "2", + "koreanStatus": false, + "bailedOut": false, + "isFunboxWithNospacePropertyActive": false + } + } + }, + { + "state": { + "wordIndex": 1, + "inputValue": [], + "active": true, + "ended": false + }, + "log": { + "version": 1, + "events": [ + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [111], + "correct": true, + "wordIndex": 0, + "charIndex": 0, + "inputValue": [111] + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [120], + "correct": false, + "wordIndex": 0, + "charIndex": 1, + "inputValue": [111, 120] + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "deleteWordBackward", + "wordIndex": 0, + "charIndex": 2, + "inputValue": [], + "automatic": true + } + }, + { + "type": "timer", + "testMs": 0, + "data": { + "event": "start", + "timer": 0, + "date": 0 + } + }, + { + "type": "input", + "testMs": 100, + "data": { + "inputType": "insertText", + "data": [111], + "correct": true, + "wordIndex": 0, + "charIndex": 0, + "inputValue": [111] + } + }, + { + "type": "input", + "testMs": 100, + "data": { + "inputType": "insertText", + "data": [110], + "correct": true, + "wordIndex": 0, + "charIndex": 1, + "inputValue": [111, 110] + } + }, + { + "type": "input", + "testMs": 100, + "data": { + "inputType": "insertText", + "data": [101], + "correct": true, + "wordIndex": 0, + "charIndex": 2, + "inputValue": [111, 110, 101] + } + }, + { + "type": "input", + "testMs": 100, + "data": { + "inputType": "insertText", + "data": [32], + "correct": true, + "wordIndex": 0, + "charIndex": 3, + "inputValue": [111, 110, 101, 32], + "commitsWord": true + } + } + ], + "context": { + "targetWords": [ + [111, 110, 101, 32], + [116, 119, 111] + ], + "mode": "words", + "mode2": "2", + "koreanStatus": false, + "bailedOut": false, + "isFunboxWithNospacePropertyActive": false + } + } + }, + { + "state": { + "wordIndex": 1, + "inputValue": [], + "active": true, + "ended": false + }, + "log": { + "version": 1, + "events": [ + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [111], + "correct": true, + "wordIndex": 0, + "charIndex": 0, + "inputValue": [111] + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [120], + "correct": false, + "wordIndex": 0, + "charIndex": 1, + "inputValue": [111, 120] + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "deleteWordBackward", + "wordIndex": 0, + "charIndex": 2, + "inputValue": [], + "automatic": true + } + }, + { + "type": "timer", + "testMs": 0, + "data": { + "event": "start", + "timer": 0, + "date": 0 + } + }, + { + "type": "input", + "testMs": 100, + "data": { + "inputType": "insertText", + "data": [111], + "correct": true, + "wordIndex": 0, + "charIndex": 0, + "inputValue": [111] + } + }, + { + "type": "input", + "testMs": 100, + "data": { + "inputType": "insertText", + "data": [110], + "correct": true, + "wordIndex": 0, + "charIndex": 1, + "inputValue": [111, 110] + } + }, + { + "type": "input", + "testMs": 100, + "data": { + "inputType": "insertText", + "data": [101], + "correct": true, + "wordIndex": 0, + "charIndex": 2, + "inputValue": [111, 110, 101] + } + }, + { + "type": "input", + "testMs": 100, + "data": { + "inputType": "insertText", + "data": [32], + "correct": true, + "wordIndex": 0, + "charIndex": 3, + "inputValue": [111, 110, 101, 32], + "commitsWord": true + } + }, + { + "type": "input", + "testMs": 200, + "data": { + "inputType": "insertText", + "data": [120], + "correct": false, + "wordIndex": 1, + "charIndex": 0, + "inputValue": [120], + "lastWord": true + } + }, + { + "type": "input", + "testMs": 200, + "data": { + "inputType": "deleteWordBackward", + "wordIndex": 1, + "charIndex": 1, + "inputValue": [], + "automatic": true + } + } + ], + "context": { + "targetWords": [ + [111, 110, 101, 32], + [116, 119, 111] + ], + "mode": "words", + "mode2": "2", + "koreanStatus": false, + "bailedOut": false, + "isFunboxWithNospacePropertyActive": false + } + } + }, + { + "state": { + "wordIndex": 1, + "inputValue": [116, 119, 111], + "active": false, + "ended": true + }, + "log": { + "version": 1, + "events": [ + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [111], + "correct": true, + "wordIndex": 0, + "charIndex": 0, + "inputValue": [111] + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [120], + "correct": false, + "wordIndex": 0, + "charIndex": 1, + "inputValue": [111, 120] + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "deleteWordBackward", + "wordIndex": 0, + "charIndex": 2, + "inputValue": [], + "automatic": true + } + }, + { + "type": "timer", + "testMs": 0, + "data": { + "event": "start", + "timer": 0, + "date": 0 + } + }, + { + "type": "input", + "testMs": 100, + "data": { + "inputType": "insertText", + "data": [111], + "correct": true, + "wordIndex": 0, + "charIndex": 0, + "inputValue": [111] + } + }, + { + "type": "input", + "testMs": 100, + "data": { + "inputType": "insertText", + "data": [110], + "correct": true, + "wordIndex": 0, + "charIndex": 1, + "inputValue": [111, 110] + } + }, + { + "type": "input", + "testMs": 100, + "data": { + "inputType": "insertText", + "data": [101], + "correct": true, + "wordIndex": 0, + "charIndex": 2, + "inputValue": [111, 110, 101] + } + }, + { + "type": "input", + "testMs": 100, + "data": { + "inputType": "insertText", + "data": [32], + "correct": true, + "wordIndex": 0, + "charIndex": 3, + "inputValue": [111, 110, 101, 32], + "commitsWord": true + } + }, + { + "type": "input", + "testMs": 200, + "data": { + "inputType": "insertText", + "data": [120], + "correct": false, + "wordIndex": 1, + "charIndex": 0, + "inputValue": [120], + "lastWord": true + } + }, + { + "type": "input", + "testMs": 200, + "data": { + "inputType": "deleteWordBackward", + "wordIndex": 1, + "charIndex": 1, + "inputValue": [], + "automatic": true + } + }, + { + "type": "input", + "testMs": 500, + "data": { + "inputType": "insertText", + "data": [116], + "correct": true, + "wordIndex": 1, + "charIndex": 0, + "inputValue": [116], + "lastWord": true + } + }, + { + "type": "input", + "testMs": 500, + "data": { + "inputType": "insertText", + "data": [119], + "correct": true, + "wordIndex": 1, + "charIndex": 1, + "inputValue": [116, 119], + "lastWord": true + } + }, + { + "type": "input", + "testMs": 500, + "data": { + "inputType": "insertText", + "data": [111], + "correct": true, + "wordIndex": 1, + "charIndex": 2, + "inputValue": [116, 119, 111], + "lastWord": true + } + }, + { + "type": "timer", + "testMs": 500, + "data": { + "event": "end", + "timer": 0, + "date": 0 + } + } + ], + "context": { + "targetWords": [ + [111, 110, 101, 32], + [116, 119, 111] + ], + "mode": "words", + "mode2": "2", + "koreanStatus": false, + "bailedOut": false, + "isFunboxWithNospacePropertyActive": false + } + } + } + ] +} diff --git a/packages/typing-fixtures/oracle/delete-error-word_hard.json b/packages/typing-fixtures/oracle/delete-error-word_hard.json new file mode 100644 index 000000000..7d772cda0 --- /dev/null +++ b/packages/typing-fixtures/oracle/delete-error-word_hard.json @@ -0,0 +1,552 @@ +{ + "name": "delete-error-word_hard", + "frames": [ + { + "state": { + "wordIndex": 0, + "inputValue": [], + "active": true, + "ended": false + }, + "log": { + "version": 1, + "events": [ + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [111], + "correct": true, + "wordIndex": 0, + "charIndex": 0, + "inputValue": [111] + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [120], + "correct": false, + "wordIndex": 0, + "charIndex": 1, + "inputValue": [111, 120] + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "deleteWordBackward", + "wordIndex": 0, + "charIndex": 2, + "inputValue": [], + "automatic": true + } + }, + { + "type": "timer", + "testMs": 0, + "data": { + "event": "start", + "timer": 0, + "date": 0 + } + } + ], + "context": { + "targetWords": [ + [111, 110, 101, 32], + [116, 119, 111] + ], + "mode": "words", + "mode2": "2", + "koreanStatus": false, + "bailedOut": false, + "isFunboxWithNospacePropertyActive": false + } + } + }, + { + "state": { + "wordIndex": 1, + "inputValue": [], + "active": true, + "ended": false + }, + "log": { + "version": 1, + "events": [ + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [111], + "correct": true, + "wordIndex": 0, + "charIndex": 0, + "inputValue": [111] + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [120], + "correct": false, + "wordIndex": 0, + "charIndex": 1, + "inputValue": [111, 120] + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "deleteWordBackward", + "wordIndex": 0, + "charIndex": 2, + "inputValue": [], + "automatic": true + } + }, + { + "type": "timer", + "testMs": 0, + "data": { + "event": "start", + "timer": 0, + "date": 0 + } + }, + { + "type": "input", + "testMs": 100, + "data": { + "inputType": "insertText", + "data": [111], + "correct": true, + "wordIndex": 0, + "charIndex": 0, + "inputValue": [111] + } + }, + { + "type": "input", + "testMs": 100, + "data": { + "inputType": "insertText", + "data": [110], + "correct": true, + "wordIndex": 0, + "charIndex": 1, + "inputValue": [111, 110] + } + }, + { + "type": "input", + "testMs": 100, + "data": { + "inputType": "insertText", + "data": [101], + "correct": true, + "wordIndex": 0, + "charIndex": 2, + "inputValue": [111, 110, 101] + } + }, + { + "type": "input", + "testMs": 100, + "data": { + "inputType": "insertText", + "data": [32], + "correct": true, + "wordIndex": 0, + "charIndex": 3, + "inputValue": [111, 110, 101, 32], + "commitsWord": true + } + } + ], + "context": { + "targetWords": [ + [111, 110, 101, 32], + [116, 119, 111] + ], + "mode": "words", + "mode2": "2", + "koreanStatus": false, + "bailedOut": false, + "isFunboxWithNospacePropertyActive": false + } + } + }, + { + "state": { + "wordIndex": 0, + "inputValue": [], + "active": true, + "ended": false + }, + "log": { + "version": 1, + "events": [ + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [111], + "correct": true, + "wordIndex": 0, + "charIndex": 0, + "inputValue": [111] + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [120], + "correct": false, + "wordIndex": 0, + "charIndex": 1, + "inputValue": [111, 120] + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "deleteWordBackward", + "wordIndex": 0, + "charIndex": 2, + "inputValue": [], + "automatic": true + } + }, + { + "type": "timer", + "testMs": 0, + "data": { + "event": "start", + "timer": 0, + "date": 0 + } + }, + { + "type": "input", + "testMs": 100, + "data": { + "inputType": "insertText", + "data": [111], + "correct": true, + "wordIndex": 0, + "charIndex": 0, + "inputValue": [111] + } + }, + { + "type": "input", + "testMs": 100, + "data": { + "inputType": "insertText", + "data": [110], + "correct": true, + "wordIndex": 0, + "charIndex": 1, + "inputValue": [111, 110] + } + }, + { + "type": "input", + "testMs": 100, + "data": { + "inputType": "insertText", + "data": [101], + "correct": true, + "wordIndex": 0, + "charIndex": 2, + "inputValue": [111, 110, 101] + } + }, + { + "type": "input", + "testMs": 100, + "data": { + "inputType": "insertText", + "data": [32], + "correct": true, + "wordIndex": 0, + "charIndex": 3, + "inputValue": [111, 110, 101, 32], + "commitsWord": true + } + }, + { + "type": "input", + "testMs": 200, + "data": { + "inputType": "insertText", + "data": [120], + "correct": false, + "wordIndex": 1, + "charIndex": 0, + "inputValue": [120], + "lastWord": true + } + }, + { + "type": "input", + "testMs": 200, + "data": { + "inputType": "deleteWordBackward", + "wordIndex": 1, + "charIndex": 1, + "inputValue": [], + "automatic": true + } + }, + { + "type": "input", + "testMs": 200, + "data": { + "inputType": "deleteWordBackward", + "wordIndex": 0, + "charIndex": 0, + "inputValue": [], + "automatic": true + } + } + ], + "context": { + "targetWords": [ + [111, 110, 101, 32], + [116, 119, 111] + ], + "mode": "words", + "mode2": "2", + "koreanStatus": false, + "bailedOut": false, + "isFunboxWithNospacePropertyActive": false + } + } + }, + { + "state": { + "wordIndex": 0, + "inputValue": [111], + "active": true, + "ended": false + }, + "log": { + "version": 1, + "events": [ + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [111], + "correct": true, + "wordIndex": 0, + "charIndex": 0, + "inputValue": [111] + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [120], + "correct": false, + "wordIndex": 0, + "charIndex": 1, + "inputValue": [111, 120] + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "deleteWordBackward", + "wordIndex": 0, + "charIndex": 2, + "inputValue": [], + "automatic": true + } + }, + { + "type": "timer", + "testMs": 0, + "data": { + "event": "start", + "timer": 0, + "date": 0 + } + }, + { + "type": "input", + "testMs": 100, + "data": { + "inputType": "insertText", + "data": [111], + "correct": true, + "wordIndex": 0, + "charIndex": 0, + "inputValue": [111] + } + }, + { + "type": "input", + "testMs": 100, + "data": { + "inputType": "insertText", + "data": [110], + "correct": true, + "wordIndex": 0, + "charIndex": 1, + "inputValue": [111, 110] + } + }, + { + "type": "input", + "testMs": 100, + "data": { + "inputType": "insertText", + "data": [101], + "correct": true, + "wordIndex": 0, + "charIndex": 2, + "inputValue": [111, 110, 101] + } + }, + { + "type": "input", + "testMs": 100, + "data": { + "inputType": "insertText", + "data": [32], + "correct": true, + "wordIndex": 0, + "charIndex": 3, + "inputValue": [111, 110, 101, 32], + "commitsWord": true + } + }, + { + "type": "input", + "testMs": 200, + "data": { + "inputType": "insertText", + "data": [120], + "correct": false, + "wordIndex": 1, + "charIndex": 0, + "inputValue": [120], + "lastWord": true + } + }, + { + "type": "input", + "testMs": 200, + "data": { + "inputType": "deleteWordBackward", + "wordIndex": 1, + "charIndex": 1, + "inputValue": [], + "automatic": true + } + }, + { + "type": "input", + "testMs": 200, + "data": { + "inputType": "deleteWordBackward", + "wordIndex": 0, + "charIndex": 0, + "inputValue": [], + "automatic": true + } + }, + { + "type": "input", + "testMs": 500, + "data": { + "inputType": "insertText", + "data": [116], + "correct": false, + "wordIndex": 0, + "charIndex": 0, + "inputValue": [116] + } + }, + { + "type": "input", + "testMs": 500, + "data": { + "inputType": "deleteWordBackward", + "wordIndex": 0, + "charIndex": 1, + "inputValue": [], + "automatic": true + } + }, + { + "type": "input", + "testMs": 500, + "data": { + "inputType": "insertText", + "data": [119], + "correct": false, + "wordIndex": 0, + "charIndex": 0, + "inputValue": [119] + } + }, + { + "type": "input", + "testMs": 500, + "data": { + "inputType": "deleteWordBackward", + "wordIndex": 0, + "charIndex": 1, + "inputValue": [], + "automatic": true + } + }, + { + "type": "input", + "testMs": 500, + "data": { + "inputType": "insertText", + "data": [111], + "correct": true, + "wordIndex": 0, + "charIndex": 0, + "inputValue": [111] + } + } + ], + "context": { + "targetWords": [ + [111, 110, 101, 32], + [116, 119, 111] + ], + "mode": "words", + "mode2": "2", + "koreanStatus": false, + "bailedOut": false, + "isFunboxWithNospacePropertyActive": false + } + } + } + ] +} diff --git a/packages/typing-fixtures/oracle/finite-edit.json b/packages/typing-fixtures/oracle/finite-edit.json new file mode 100644 index 000000000..a0905e71d --- /dev/null +++ b/packages/typing-fixtures/oracle/finite-edit.json @@ -0,0 +1,842 @@ +{ + "name": "finite-edit", + "frames": [ + { + "state": { + "wordIndex": 0, + "inputValue": [111, 120], + "active": true, + "ended": false + }, + "log": { + "version": 1, + "events": [ + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [111], + "correct": true, + "wordIndex": 0, + "charIndex": 0, + "inputValue": [111] + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [120], + "correct": false, + "wordIndex": 0, + "charIndex": 1, + "inputValue": [111, 120] + } + }, + { + "type": "timer", + "testMs": 0, + "data": { + "event": "start", + "timer": 0, + "date": 0 + } + } + ], + "context": { + "targetWords": [ + [111, 110, 101, 32], + [116, 119, 111] + ], + "mode": "words", + "mode2": "2", + "koreanStatus": false, + "bailedOut": false, + "isFunboxWithNospacePropertyActive": false + } + } + }, + { + "state": { + "wordIndex": 0, + "inputValue": [111], + "active": true, + "ended": false + }, + "log": { + "version": 1, + "events": [ + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [111], + "correct": true, + "wordIndex": 0, + "charIndex": 0, + "inputValue": [111] + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [120], + "correct": false, + "wordIndex": 0, + "charIndex": 1, + "inputValue": [111, 120] + } + }, + { + "type": "timer", + "testMs": 0, + "data": { + "event": "start", + "timer": 0, + "date": 0 + } + }, + { + "type": "input", + "testMs": 50, + "data": { + "inputType": "deleteContentBackward", + "wordIndex": 0, + "charIndex": 2, + "inputValue": [111] + } + } + ], + "context": { + "targetWords": [ + [111, 110, 101, 32], + [116, 119, 111] + ], + "mode": "words", + "mode2": "2", + "koreanStatus": false, + "bailedOut": false, + "isFunboxWithNospacePropertyActive": false + } + } + }, + { + "state": { + "wordIndex": 1, + "inputValue": [], + "active": true, + "ended": false + }, + "log": { + "version": 1, + "events": [ + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [111], + "correct": true, + "wordIndex": 0, + "charIndex": 0, + "inputValue": [111] + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [120], + "correct": false, + "wordIndex": 0, + "charIndex": 1, + "inputValue": [111, 120] + } + }, + { + "type": "timer", + "testMs": 0, + "data": { + "event": "start", + "timer": 0, + "date": 0 + } + }, + { + "type": "input", + "testMs": 50, + "data": { + "inputType": "deleteContentBackward", + "wordIndex": 0, + "charIndex": 2, + "inputValue": [111] + } + }, + { + "type": "input", + "testMs": 100, + "data": { + "inputType": "insertText", + "data": [110], + "correct": true, + "wordIndex": 0, + "charIndex": 1, + "inputValue": [111, 110] + } + }, + { + "type": "input", + "testMs": 100, + "data": { + "inputType": "insertText", + "data": [101], + "correct": true, + "wordIndex": 0, + "charIndex": 2, + "inputValue": [111, 110, 101] + } + }, + { + "type": "input", + "testMs": 100, + "data": { + "inputType": "insertText", + "data": [32], + "correct": true, + "wordIndex": 0, + "charIndex": 3, + "inputValue": [111, 110, 101, 32], + "commitsWord": true + } + } + ], + "context": { + "targetWords": [ + [111, 110, 101, 32], + [116, 119, 111] + ], + "mode": "words", + "mode2": "2", + "koreanStatus": false, + "bailedOut": false, + "isFunboxWithNospacePropertyActive": false + } + } + }, + { + "state": { + "wordIndex": 1, + "inputValue": [116, 119, 111], + "active": false, + "ended": true + }, + "log": { + "version": 1, + "events": [ + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [111], + "correct": true, + "wordIndex": 0, + "charIndex": 0, + "inputValue": [111] + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [120], + "correct": false, + "wordIndex": 0, + "charIndex": 1, + "inputValue": [111, 120] + } + }, + { + "type": "timer", + "testMs": 0, + "data": { + "event": "start", + "timer": 0, + "date": 0 + } + }, + { + "type": "input", + "testMs": 50, + "data": { + "inputType": "deleteContentBackward", + "wordIndex": 0, + "charIndex": 2, + "inputValue": [111] + } + }, + { + "type": "input", + "testMs": 100, + "data": { + "inputType": "insertText", + "data": [110], + "correct": true, + "wordIndex": 0, + "charIndex": 1, + "inputValue": [111, 110] + } + }, + { + "type": "input", + "testMs": 100, + "data": { + "inputType": "insertText", + "data": [101], + "correct": true, + "wordIndex": 0, + "charIndex": 2, + "inputValue": [111, 110, 101] + } + }, + { + "type": "input", + "testMs": 100, + "data": { + "inputType": "insertText", + "data": [32], + "correct": true, + "wordIndex": 0, + "charIndex": 3, + "inputValue": [111, 110, 101, 32], + "commitsWord": true + } + }, + { + "type": "input", + "testMs": 1100, + "data": { + "inputType": "insertText", + "data": [116], + "correct": true, + "wordIndex": 1, + "charIndex": 0, + "inputValue": [116], + "lastWord": true + } + }, + { + "type": "input", + "testMs": 1100, + "data": { + "inputType": "insertText", + "data": [119], + "correct": true, + "wordIndex": 1, + "charIndex": 1, + "inputValue": [116, 119], + "lastWord": true + } + }, + { + "type": "input", + "testMs": 1100, + "data": { + "inputType": "insertText", + "data": [111], + "correct": true, + "wordIndex": 1, + "charIndex": 2, + "inputValue": [116, 119, 111], + "lastWord": true + } + }, + { + "type": "timer", + "testMs": 1100, + "data": { + "event": "end", + "timer": 1, + "date": 0 + } + } + ], + "context": { + "targetWords": [ + [111, 110, 101, 32], + [116, 119, 111] + ], + "mode": "words", + "mode2": "2", + "koreanStatus": false, + "bailedOut": false, + "isFunboxWithNospacePropertyActive": false + } + } + }, + { + "state": { + "wordIndex": 1, + "inputValue": [116, 119, 111], + "active": false, + "ended": true + }, + "log": { + "version": 1, + "events": [ + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [111], + "correct": true, + "wordIndex": 0, + "charIndex": 0, + "inputValue": [111] + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [120], + "correct": false, + "wordIndex": 0, + "charIndex": 1, + "inputValue": [111, 120] + } + }, + { + "type": "timer", + "testMs": 0, + "data": { + "event": "start", + "timer": 0, + "date": 0 + } + }, + { + "type": "input", + "testMs": 50, + "data": { + "inputType": "deleteContentBackward", + "wordIndex": 0, + "charIndex": 2, + "inputValue": [111] + } + }, + { + "type": "input", + "testMs": 100, + "data": { + "inputType": "insertText", + "data": [110], + "correct": true, + "wordIndex": 0, + "charIndex": 1, + "inputValue": [111, 110] + } + }, + { + "type": "input", + "testMs": 100, + "data": { + "inputType": "insertText", + "data": [101], + "correct": true, + "wordIndex": 0, + "charIndex": 2, + "inputValue": [111, 110, 101] + } + }, + { + "type": "input", + "testMs": 100, + "data": { + "inputType": "insertText", + "data": [32], + "correct": true, + "wordIndex": 0, + "charIndex": 3, + "inputValue": [111, 110, 101, 32], + "commitsWord": true + } + }, + { + "type": "input", + "testMs": 1100, + "data": { + "inputType": "insertText", + "data": [116], + "correct": true, + "wordIndex": 1, + "charIndex": 0, + "inputValue": [116], + "lastWord": true + } + }, + { + "type": "input", + "testMs": 1100, + "data": { + "inputType": "insertText", + "data": [119], + "correct": true, + "wordIndex": 1, + "charIndex": 1, + "inputValue": [116, 119], + "lastWord": true + } + }, + { + "type": "input", + "testMs": 1100, + "data": { + "inputType": "insertText", + "data": [111], + "correct": true, + "wordIndex": 1, + "charIndex": 2, + "inputValue": [116, 119, 111], + "lastWord": true + } + }, + { + "type": "timer", + "testMs": 1100, + "data": { + "event": "end", + "timer": 1, + "date": 0 + } + } + ], + "context": { + "targetWords": [ + [111, 110, 101, 32], + [116, 119, 111] + ], + "mode": "words", + "mode2": "2", + "koreanStatus": false, + "bailedOut": false, + "isFunboxWithNospacePropertyActive": false + } + } + }, + { + "state": { + "wordIndex": 1, + "inputValue": [116, 119, 111], + "active": false, + "ended": true + }, + "log": { + "version": 1, + "events": [ + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [111], + "correct": true, + "wordIndex": 0, + "charIndex": 0, + "inputValue": [111] + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [120], + "correct": false, + "wordIndex": 0, + "charIndex": 1, + "inputValue": [111, 120] + } + }, + { + "type": "timer", + "testMs": 0, + "data": { + "event": "start", + "timer": 0, + "date": 0 + } + }, + { + "type": "input", + "testMs": 50, + "data": { + "inputType": "deleteContentBackward", + "wordIndex": 0, + "charIndex": 2, + "inputValue": [111] + } + }, + { + "type": "input", + "testMs": 100, + "data": { + "inputType": "insertText", + "data": [110], + "correct": true, + "wordIndex": 0, + "charIndex": 1, + "inputValue": [111, 110] + } + }, + { + "type": "input", + "testMs": 100, + "data": { + "inputType": "insertText", + "data": [101], + "correct": true, + "wordIndex": 0, + "charIndex": 2, + "inputValue": [111, 110, 101] + } + }, + { + "type": "input", + "testMs": 100, + "data": { + "inputType": "insertText", + "data": [32], + "correct": true, + "wordIndex": 0, + "charIndex": 3, + "inputValue": [111, 110, 101, 32], + "commitsWord": true + } + }, + { + "type": "input", + "testMs": 1100, + "data": { + "inputType": "insertText", + "data": [116], + "correct": true, + "wordIndex": 1, + "charIndex": 0, + "inputValue": [116], + "lastWord": true + } + }, + { + "type": "input", + "testMs": 1100, + "data": { + "inputType": "insertText", + "data": [119], + "correct": true, + "wordIndex": 1, + "charIndex": 1, + "inputValue": [116, 119], + "lastWord": true + } + }, + { + "type": "input", + "testMs": 1100, + "data": { + "inputType": "insertText", + "data": [111], + "correct": true, + "wordIndex": 1, + "charIndex": 2, + "inputValue": [116, 119, 111], + "lastWord": true + } + }, + { + "type": "timer", + "testMs": 1100, + "data": { + "event": "end", + "timer": 1, + "date": 0 + } + } + ], + "context": { + "targetWords": [ + [111, 110, 101, 32], + [116, 119, 111] + ], + "mode": "words", + "mode2": "2", + "koreanStatus": false, + "bailedOut": false, + "isFunboxWithNospacePropertyActive": false + } + } + }, + { + "state": { + "wordIndex": 0, + "inputValue": [], + "active": false, + "ended": false + }, + "log": { + "version": 1, + "events": [], + "context": { + "targetWords": [ + [111, 110, 101, 32], + [116, 119, 111] + ], + "mode": "words", + "mode2": "2", + "koreanStatus": false, + "bailedOut": false, + "isFunboxWithNospacePropertyActive": false + } + } + }, + { + "state": { + "wordIndex": 1, + "inputValue": [116, 119, 111], + "active": false, + "ended": true + }, + "log": { + "version": 1, + "events": [ + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [111], + "correct": true, + "wordIndex": 0, + "charIndex": 0, + "inputValue": [111] + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [110], + "correct": true, + "wordIndex": 0, + "charIndex": 1, + "inputValue": [111, 110] + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [101], + "correct": true, + "wordIndex": 0, + "charIndex": 2, + "inputValue": [111, 110, 101] + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [32], + "correct": true, + "wordIndex": 0, + "charIndex": 3, + "inputValue": [111, 110, 101, 32], + "commitsWord": true + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [116], + "correct": true, + "wordIndex": 1, + "charIndex": 0, + "inputValue": [116], + "lastWord": true + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [119], + "correct": true, + "wordIndex": 1, + "charIndex": 1, + "inputValue": [116, 119], + "lastWord": true + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [111], + "correct": true, + "wordIndex": 1, + "charIndex": 2, + "inputValue": [116, 119, 111], + "lastWord": true + } + }, + { + "type": "timer", + "testMs": 0, + "data": { + "event": "start", + "timer": 0, + "date": 0 + } + }, + { + "type": "timer", + "testMs": 0, + "data": { + "event": "end", + "timer": 0, + "date": 0 + } + } + ], + "context": { + "targetWords": [ + [111, 110, 101, 32], + [116, 119, 111] + ], + "mode": "words", + "mode2": "2", + "koreanStatus": false, + "bailedOut": false, + "isFunboxWithNospacePropertyActive": false + } + } + } + ] +} diff --git a/packages/typing-fixtures/oracle/freedom-regression.json b/packages/typing-fixtures/oracle/freedom-regression.json new file mode 100644 index 000000000..c9d944eb6 --- /dev/null +++ b/packages/typing-fixtures/oracle/freedom-regression.json @@ -0,0 +1,437 @@ +{ + "name": "freedom-regression", + "frames": [ + { + "state": { + "wordIndex": 1, + "inputValue": [], + "active": true, + "ended": false + }, + "log": { + "version": 1, + "events": [ + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [111], + "correct": true, + "wordIndex": 0, + "charIndex": 0, + "inputValue": [111] + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [110], + "correct": true, + "wordIndex": 0, + "charIndex": 1, + "inputValue": [111, 110] + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [101], + "correct": true, + "wordIndex": 0, + "charIndex": 2, + "inputValue": [111, 110, 101] + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [32], + "correct": true, + "wordIndex": 0, + "charIndex": 3, + "inputValue": [111, 110, 101, 32], + "commitsWord": true + } + }, + { + "type": "timer", + "testMs": 0, + "data": { + "event": "start", + "timer": 0, + "date": 0 + } + } + ], + "context": { + "targetWords": [ + [111, 110, 101, 32], + [116, 119, 111] + ], + "mode": "words", + "mode2": "2", + "koreanStatus": false, + "bailedOut": false, + "isFunboxWithNospacePropertyActive": false + } + } + }, + { + "state": { + "wordIndex": 0, + "inputValue": [111, 110, 101], + "active": true, + "ended": false + }, + "log": { + "version": 1, + "events": [ + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [111], + "correct": true, + "wordIndex": 0, + "charIndex": 0, + "inputValue": [111] + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [110], + "correct": true, + "wordIndex": 0, + "charIndex": 1, + "inputValue": [111, 110] + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [101], + "correct": true, + "wordIndex": 0, + "charIndex": 2, + "inputValue": [111, 110, 101] + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [32], + "correct": true, + "wordIndex": 0, + "charIndex": 3, + "inputValue": [111, 110, 101, 32], + "commitsWord": true + } + }, + { + "type": "timer", + "testMs": 0, + "data": { + "event": "start", + "timer": 0, + "date": 0 + } + }, + { + "type": "input", + "testMs": 100, + "data": { + "inputType": "deleteContentBackward", + "wordIndex": 0, + "charIndex": 3, + "inputValue": [111, 110, 101] + } + } + ], + "context": { + "targetWords": [ + [111, 110, 101, 32], + [116, 119, 111] + ], + "mode": "words", + "mode2": "2", + "koreanStatus": false, + "bailedOut": false, + "isFunboxWithNospacePropertyActive": false + } + } + }, + { + "state": { + "wordIndex": 1, + "inputValue": [], + "active": true, + "ended": false + }, + "log": { + "version": 1, + "events": [ + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [111], + "correct": true, + "wordIndex": 0, + "charIndex": 0, + "inputValue": [111] + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [110], + "correct": true, + "wordIndex": 0, + "charIndex": 1, + "inputValue": [111, 110] + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [101], + "correct": true, + "wordIndex": 0, + "charIndex": 2, + "inputValue": [111, 110, 101] + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [32], + "correct": true, + "wordIndex": 0, + "charIndex": 3, + "inputValue": [111, 110, 101, 32], + "commitsWord": true + } + }, + { + "type": "timer", + "testMs": 0, + "data": { + "event": "start", + "timer": 0, + "date": 0 + } + }, + { + "type": "input", + "testMs": 100, + "data": { + "inputType": "deleteContentBackward", + "wordIndex": 0, + "charIndex": 3, + "inputValue": [111, 110, 101] + } + }, + { + "type": "input", + "testMs": 200, + "data": { + "inputType": "insertText", + "data": [32], + "correct": true, + "wordIndex": 0, + "charIndex": 3, + "inputValue": [111, 110, 101, 32], + "commitsWord": true + } + } + ], + "context": { + "targetWords": [ + [111, 110, 101, 32], + [116, 119, 111] + ], + "mode": "words", + "mode2": "2", + "koreanStatus": false, + "bailedOut": false, + "isFunboxWithNospacePropertyActive": false + } + } + }, + { + "state": { + "wordIndex": 1, + "inputValue": [116, 119, 111], + "active": false, + "ended": true + }, + "log": { + "version": 1, + "events": [ + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [111], + "correct": true, + "wordIndex": 0, + "charIndex": 0, + "inputValue": [111] + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [110], + "correct": true, + "wordIndex": 0, + "charIndex": 1, + "inputValue": [111, 110] + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [101], + "correct": true, + "wordIndex": 0, + "charIndex": 2, + "inputValue": [111, 110, 101] + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [32], + "correct": true, + "wordIndex": 0, + "charIndex": 3, + "inputValue": [111, 110, 101, 32], + "commitsWord": true + } + }, + { + "type": "timer", + "testMs": 0, + "data": { + "event": "start", + "timer": 0, + "date": 0 + } + }, + { + "type": "input", + "testMs": 100, + "data": { + "inputType": "deleteContentBackward", + "wordIndex": 0, + "charIndex": 3, + "inputValue": [111, 110, 101] + } + }, + { + "type": "input", + "testMs": 200, + "data": { + "inputType": "insertText", + "data": [32], + "correct": true, + "wordIndex": 0, + "charIndex": 3, + "inputValue": [111, 110, 101, 32], + "commitsWord": true + } + }, + { + "type": "input", + "testMs": 400, + "data": { + "inputType": "insertText", + "data": [116], + "correct": true, + "wordIndex": 1, + "charIndex": 0, + "inputValue": [116], + "lastWord": true + } + }, + { + "type": "input", + "testMs": 400, + "data": { + "inputType": "insertText", + "data": [119], + "correct": true, + "wordIndex": 1, + "charIndex": 1, + "inputValue": [116, 119], + "lastWord": true + } + }, + { + "type": "input", + "testMs": 400, + "data": { + "inputType": "insertText", + "data": [111], + "correct": true, + "wordIndex": 1, + "charIndex": 2, + "inputValue": [116, 119, 111], + "lastWord": true + } + }, + { + "type": "timer", + "testMs": 400, + "data": { + "event": "end", + "timer": 0, + "date": 0 + } + } + ], + "context": { + "targetWords": [ + [111, 110, 101, 32], + [116, 119, 111] + ], + "mode": "words", + "mode2": "2", + "koreanStatus": false, + "bailedOut": false, + "isFunboxWithNospacePropertyActive": false + } + } + } + ] +} diff --git a/packages/typing-fixtures/oracle/ime-typography.json b/packages/typing-fixtures/oracle/ime-typography.json new file mode 100644 index 000000000..c4eb8fd07 --- /dev/null +++ b/packages/typing-fixtures/oracle/ime-typography.json @@ -0,0 +1,190 @@ +{ + "name": "ime-typography", + "frames": [ + { + "state": { + "wordIndex": 1, + "inputValue": [], + "active": true, + "ended": false + }, + "log": { + "version": 1, + "events": [ + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [97], + "correct": true, + "wordIndex": 0, + "charIndex": 0, + "inputValue": [97], + "isCompositionEnding": true + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [32], + "correct": true, + "wordIndex": 0, + "charIndex": 1, + "inputValue": [97, 32], + "commitsWord": true, + "isCompositionEnding": true + } + }, + { + "type": "timer", + "testMs": 0, + "data": { + "event": "start", + "timer": 0, + "date": 0 + } + } + ], + "context": { + "targetWords": [ + [97, 32], + [8220, 98, 8221, 8212] + ], + "mode": "words", + "mode2": "2", + "koreanStatus": false, + "bailedOut": false, + "isFunboxWithNospacePropertyActive": false + } + } + }, + { + "state": { + "wordIndex": 1, + "inputValue": [8220, 98, 8221, 8212], + "active": false, + "ended": true + }, + "log": { + "version": 1, + "events": [ + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [97], + "correct": true, + "wordIndex": 0, + "charIndex": 0, + "inputValue": [97], + "isCompositionEnding": true + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [32], + "correct": true, + "wordIndex": 0, + "charIndex": 1, + "inputValue": [97, 32], + "commitsWord": true, + "isCompositionEnding": true + } + }, + { + "type": "timer", + "testMs": 0, + "data": { + "event": "start", + "timer": 0, + "date": 0 + } + }, + { + "type": "input", + "testMs": 100, + "data": { + "inputType": "insertText", + "data": [8220], + "correct": true, + "wordIndex": 1, + "charIndex": 0, + "inputValue": [8220], + "lastWord": true, + "isCompositionEnding": true + } + }, + { + "type": "input", + "testMs": 100, + "data": { + "inputType": "insertText", + "data": [98], + "correct": true, + "wordIndex": 1, + "charIndex": 1, + "inputValue": [8220, 98], + "lastWord": true, + "isCompositionEnding": true + } + }, + { + "type": "input", + "testMs": 100, + "data": { + "inputType": "insertText", + "data": [8221], + "correct": true, + "wordIndex": 1, + "charIndex": 2, + "inputValue": [8220, 98, 8221], + "lastWord": true, + "isCompositionEnding": true + } + }, + { + "type": "input", + "testMs": 100, + "data": { + "inputType": "insertText", + "data": [8212], + "correct": true, + "wordIndex": 1, + "charIndex": 3, + "inputValue": [8220, 98, 8221, 8212], + "lastWord": true, + "isCompositionEnding": true + } + }, + { + "type": "timer", + "testMs": 100, + "data": { + "event": "end", + "timer": 0, + "date": 0 + } + } + ], + "context": { + "targetWords": [ + [97, 32], + [8220, 98, 8221, 8212] + ], + "mode": "words", + "mode2": "2", + "koreanStatus": false, + "bailedOut": false, + "isFunboxWithNospacePropertyActive": false + } + } + } + ] +} diff --git a/packages/typing-fixtures/oracle/infinite-words.json b/packages/typing-fixtures/oracle/infinite-words.json new file mode 100644 index 000000000..dc5ee3380 --- /dev/null +++ b/packages/typing-fixtures/oracle/infinite-words.json @@ -0,0 +1,509 @@ +{ + "name": "infinite-words", + "frames": [ + { + "state": { + "wordIndex": 1, + "inputValue": [116, 119, 111, 32], + "active": true, + "ended": false + }, + "log": { + "version": 1, + "events": [ + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [111], + "correct": true, + "wordIndex": 0, + "charIndex": 0, + "inputValue": [111] + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [110], + "correct": true, + "wordIndex": 0, + "charIndex": 1, + "inputValue": [111, 110] + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [101], + "correct": true, + "wordIndex": 0, + "charIndex": 2, + "inputValue": [111, 110, 101] + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [32], + "correct": true, + "wordIndex": 0, + "charIndex": 3, + "inputValue": [111, 110, 101, 32], + "commitsWord": true + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [116], + "correct": true, + "wordIndex": 1, + "charIndex": 0, + "inputValue": [116], + "lastWord": true + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [119], + "correct": true, + "wordIndex": 1, + "charIndex": 1, + "inputValue": [116, 119], + "lastWord": true + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [111], + "correct": true, + "wordIndex": 1, + "charIndex": 2, + "inputValue": [116, 119, 111], + "lastWord": true + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [32], + "correct": true, + "wordIndex": 1, + "charIndex": 3, + "inputValue": [116, 119, 111, 32], + "commitsWord": true, + "lastWord": true + } + }, + { + "type": "timer", + "testMs": 0, + "data": { + "event": "start", + "timer": 0, + "date": 0 + } + } + ], + "context": { + "targetWords": [ + [111, 110, 101, 32], + [116, 119, 111, 32] + ], + "mode": "words", + "mode2": "0", + "koreanStatus": false, + "bailedOut": false, + "isFunboxWithNospacePropertyActive": false + } + } + }, + { + "state": { + "wordIndex": 1, + "inputValue": [116, 119, 111, 32], + "active": true, + "ended": false + }, + "log": { + "version": 1, + "events": [ + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [111], + "correct": true, + "wordIndex": 0, + "charIndex": 0, + "inputValue": [111] + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [110], + "correct": true, + "wordIndex": 0, + "charIndex": 1, + "inputValue": [111, 110] + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [101], + "correct": true, + "wordIndex": 0, + "charIndex": 2, + "inputValue": [111, 110, 101] + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [32], + "correct": true, + "wordIndex": 0, + "charIndex": 3, + "inputValue": [111, 110, 101, 32], + "commitsWord": true + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [116], + "correct": true, + "wordIndex": 1, + "charIndex": 0, + "inputValue": [116], + "lastWord": true + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [119], + "correct": true, + "wordIndex": 1, + "charIndex": 1, + "inputValue": [116, 119], + "lastWord": true + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [111], + "correct": true, + "wordIndex": 1, + "charIndex": 2, + "inputValue": [116, 119, 111], + "lastWord": true + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [32], + "correct": true, + "wordIndex": 1, + "charIndex": 3, + "inputValue": [116, 119, 111, 32], + "commitsWord": true, + "lastWord": true + } + }, + { + "type": "timer", + "testMs": 0, + "data": { + "event": "start", + "timer": 0, + "date": 0 + } + }, + { + "type": "timer", + "testMs": 5000, + "data": { + "event": "step", + "timer": 1, + "catchup": true + } + }, + { + "type": "timer", + "testMs": 5000, + "data": { + "event": "step", + "timer": 2, + "catchup": true + } + }, + { + "type": "timer", + "testMs": 5000, + "data": { + "event": "step", + "timer": 3, + "catchup": true + } + }, + { + "type": "timer", + "testMs": 5000, + "data": { + "event": "step", + "timer": 4, + "catchup": true + } + }, + { + "type": "timer", + "testMs": 5000, + "data": { + "event": "step", + "timer": 5, + "drift": 4000 + } + } + ], + "context": { + "targetWords": [ + [111, 110, 101, 32], + [116, 119, 111, 32] + ], + "mode": "words", + "mode2": "0", + "koreanStatus": false, + "bailedOut": false, + "isFunboxWithNospacePropertyActive": false + } + } + }, + { + "state": { + "wordIndex": 1, + "inputValue": [116, 119, 111, 32], + "active": false, + "ended": true + }, + "log": { + "version": 1, + "events": [ + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [111], + "correct": true, + "wordIndex": 0, + "charIndex": 0, + "inputValue": [111] + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [110], + "correct": true, + "wordIndex": 0, + "charIndex": 1, + "inputValue": [111, 110] + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [101], + "correct": true, + "wordIndex": 0, + "charIndex": 2, + "inputValue": [111, 110, 101] + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [32], + "correct": true, + "wordIndex": 0, + "charIndex": 3, + "inputValue": [111, 110, 101, 32], + "commitsWord": true + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [116], + "correct": true, + "wordIndex": 1, + "charIndex": 0, + "inputValue": [116], + "lastWord": true + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [119], + "correct": true, + "wordIndex": 1, + "charIndex": 1, + "inputValue": [116, 119], + "lastWord": true + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [111], + "correct": true, + "wordIndex": 1, + "charIndex": 2, + "inputValue": [116, 119, 111], + "lastWord": true + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [32], + "correct": true, + "wordIndex": 1, + "charIndex": 3, + "inputValue": [116, 119, 111, 32], + "commitsWord": true, + "lastWord": true + } + }, + { + "type": "timer", + "testMs": 0, + "data": { + "event": "start", + "timer": 0, + "date": 0 + } + }, + { + "type": "timer", + "testMs": 5000, + "data": { + "event": "step", + "timer": 1, + "catchup": true + } + }, + { + "type": "timer", + "testMs": 5000, + "data": { + "event": "step", + "timer": 2, + "catchup": true + } + }, + { + "type": "timer", + "testMs": 5000, + "data": { + "event": "step", + "timer": 3, + "catchup": true + } + }, + { + "type": "timer", + "testMs": 5000, + "data": { + "event": "step", + "timer": 4, + "catchup": true + } + }, + { + "type": "timer", + "testMs": 5000, + "data": { + "event": "step", + "timer": 5, + "drift": 4000 + } + }, + { + "type": "timer", + "testMs": 6000, + "data": { + "event": "end", + "timer": 6, + "date": 0 + } + } + ], + "context": { + "targetWords": [ + [111, 110, 101, 32], + [116, 119, 111, 32] + ], + "mode": "words", + "mode2": "0", + "koreanStatus": false, + "bailedOut": false, + "isFunboxWithNospacePropertyActive": false + } + } + } + ] +} diff --git a/packages/typing-fixtures/oracle/master-finish-once.json b/packages/typing-fixtures/oracle/master-finish-once.json new file mode 100644 index 000000000..68c44c5a6 --- /dev/null +++ b/packages/typing-fixtures/oracle/master-finish-once.json @@ -0,0 +1,323 @@ +{ + "name": "master-finish-once", + "frames": [ + { + "state": { + "wordIndex": 0, + "inputValue": [120], + "active": false, + "ended": true, + "reason": "difficulty" + }, + "log": { + "version": 1, + "events": [ + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [120], + "correct": false, + "wordIndex": 0, + "charIndex": 0, + "inputValue": [120] + } + }, + { + "type": "timer", + "testMs": 0, + "data": { + "event": "start", + "timer": 0, + "date": 0 + } + }, + { + "type": "timer", + "testMs": 0, + "data": { + "event": "end", + "timer": 0, + "date": 0 + } + } + ], + "context": { + "targetWords": [ + [111, 110, 101, 32], + [116, 119, 111] + ], + "mode": "words", + "mode2": "2", + "koreanStatus": false, + "bailedOut": false, + "isFunboxWithNospacePropertyActive": false + } + } + }, + { + "state": { + "wordIndex": 0, + "inputValue": [120], + "active": false, + "ended": true, + "reason": "difficulty" + }, + "log": { + "version": 1, + "events": [ + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [120], + "correct": false, + "wordIndex": 0, + "charIndex": 0, + "inputValue": [120] + } + }, + { + "type": "timer", + "testMs": 0, + "data": { + "event": "start", + "timer": 0, + "date": 0 + } + }, + { + "type": "timer", + "testMs": 0, + "data": { + "event": "end", + "timer": 0, + "date": 0 + } + } + ], + "context": { + "targetWords": [ + [111, 110, 101, 32], + [116, 119, 111] + ], + "mode": "words", + "mode2": "2", + "koreanStatus": false, + "bailedOut": false, + "isFunboxWithNospacePropertyActive": false + } + } + }, + { + "state": { + "wordIndex": 0, + "inputValue": [120], + "active": false, + "ended": true, + "reason": "difficulty" + }, + "log": { + "version": 1, + "events": [ + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [120], + "correct": false, + "wordIndex": 0, + "charIndex": 0, + "inputValue": [120] + } + }, + { + "type": "timer", + "testMs": 0, + "data": { + "event": "start", + "timer": 0, + "date": 0 + } + }, + { + "type": "timer", + "testMs": 0, + "data": { + "event": "end", + "timer": 0, + "date": 0 + } + } + ], + "context": { + "targetWords": [ + [111, 110, 101, 32], + [116, 119, 111] + ], + "mode": "words", + "mode2": "2", + "koreanStatus": false, + "bailedOut": false, + "isFunboxWithNospacePropertyActive": false + } + } + }, + { + "state": { + "wordIndex": 0, + "inputValue": [], + "active": false, + "ended": false + }, + "log": { + "version": 1, + "events": [], + "context": { + "targetWords": [ + [111, 110, 101, 32], + [116, 119, 111] + ], + "mode": "words", + "mode2": "2", + "koreanStatus": false, + "bailedOut": false, + "isFunboxWithNospacePropertyActive": false + } + } + }, + { + "state": { + "wordIndex": 1, + "inputValue": [116, 119, 111], + "active": false, + "ended": true + }, + "log": { + "version": 1, + "events": [ + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [111], + "correct": true, + "wordIndex": 0, + "charIndex": 0, + "inputValue": [111] + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [110], + "correct": true, + "wordIndex": 0, + "charIndex": 1, + "inputValue": [111, 110] + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [101], + "correct": true, + "wordIndex": 0, + "charIndex": 2, + "inputValue": [111, 110, 101] + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [32], + "correct": true, + "wordIndex": 0, + "charIndex": 3, + "inputValue": [111, 110, 101, 32], + "commitsWord": true + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [116], + "correct": true, + "wordIndex": 1, + "charIndex": 0, + "inputValue": [116], + "lastWord": true + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [119], + "correct": true, + "wordIndex": 1, + "charIndex": 1, + "inputValue": [116, 119], + "lastWord": true + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [111], + "correct": true, + "wordIndex": 1, + "charIndex": 2, + "inputValue": [116, 119, 111], + "lastWord": true + } + }, + { + "type": "timer", + "testMs": 0, + "data": { + "event": "start", + "timer": 0, + "date": 0 + } + }, + { + "type": "timer", + "testMs": 0, + "data": { + "event": "end", + "timer": 0, + "date": 0 + } + } + ], + "context": { + "targetWords": [ + [111, 110, 101, 32], + [116, 119, 111] + ], + "mode": "words", + "mode2": "2", + "koreanStatus": false, + "bailedOut": false, + "isFunboxWithNospacePropertyActive": false + } + } + } + ] +} diff --git a/packages/typing-fixtures/oracle/stop-letter.json b/packages/typing-fixtures/oracle/stop-letter.json new file mode 100644 index 000000000..cbb66b562 --- /dev/null +++ b/packages/typing-fixtures/oracle/stop-letter.json @@ -0,0 +1,206 @@ +{ + "name": "stop-letter", + "frames": [ + { + "state": { + "wordIndex": 0, + "inputValue": [111], + "active": true, + "ended": false + }, + "log": { + "version": 1, + "events": [ + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [111], + "correct": true, + "wordIndex": 0, + "charIndex": 0, + "inputValue": [111] + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [120], + "correct": false, + "wordIndex": 0, + "charIndex": 1, + "inputValue": [111], + "inputStopped": true + } + }, + { + "type": "timer", + "testMs": 0, + "data": { + "event": "start", + "timer": 0, + "date": 0 + } + } + ], + "context": { + "targetWords": [ + [111, 110, 101, 32], + [116, 119, 111] + ], + "mode": "words", + "mode2": "2", + "koreanStatus": false, + "bailedOut": false, + "isFunboxWithNospacePropertyActive": false + } + } + }, + { + "state": { + "wordIndex": 1, + "inputValue": [116, 119, 111], + "active": false, + "ended": true + }, + "log": { + "version": 1, + "events": [ + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [111], + "correct": true, + "wordIndex": 0, + "charIndex": 0, + "inputValue": [111] + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [120], + "correct": false, + "wordIndex": 0, + "charIndex": 1, + "inputValue": [111], + "inputStopped": true + } + }, + { + "type": "timer", + "testMs": 0, + "data": { + "event": "start", + "timer": 0, + "date": 0 + } + }, + { + "type": "input", + "testMs": 100, + "data": { + "inputType": "insertText", + "data": [110], + "correct": true, + "wordIndex": 0, + "charIndex": 1, + "inputValue": [111, 110] + } + }, + { + "type": "input", + "testMs": 100, + "data": { + "inputType": "insertText", + "data": [101], + "correct": true, + "wordIndex": 0, + "charIndex": 2, + "inputValue": [111, 110, 101] + } + }, + { + "type": "input", + "testMs": 100, + "data": { + "inputType": "insertText", + "data": [32], + "correct": true, + "wordIndex": 0, + "charIndex": 3, + "inputValue": [111, 110, 101, 32], + "commitsWord": true + } + }, + { + "type": "input", + "testMs": 100, + "data": { + "inputType": "insertText", + "data": [116], + "correct": true, + "wordIndex": 1, + "charIndex": 0, + "inputValue": [116], + "lastWord": true + } + }, + { + "type": "input", + "testMs": 100, + "data": { + "inputType": "insertText", + "data": [119], + "correct": true, + "wordIndex": 1, + "charIndex": 1, + "inputValue": [116, 119], + "lastWord": true + } + }, + { + "type": "input", + "testMs": 100, + "data": { + "inputType": "insertText", + "data": [111], + "correct": true, + "wordIndex": 1, + "charIndex": 2, + "inputValue": [116, 119, 111], + "lastWord": true + } + }, + { + "type": "timer", + "testMs": 100, + "data": { + "event": "end", + "timer": 0, + "date": 0 + } + } + ], + "context": { + "targetWords": [ + [111, 110, 101, 32], + [116, 119, 111] + ], + "mode": "words", + "mode2": "2", + "koreanStatus": false, + "bailedOut": false, + "isFunboxWithNospacePropertyActive": false + } + } + } + ] +} diff --git a/packages/typing-fixtures/oracle/timer-stall.json b/packages/typing-fixtures/oracle/timer-stall.json new file mode 100644 index 000000000..1c9d457d5 --- /dev/null +++ b/packages/typing-fixtures/oracle/timer-stall.json @@ -0,0 +1,339 @@ +{ + "name": "timer-stall", + "frames": [ + { + "state": { + "wordIndex": 0, + "inputValue": [111], + "active": true, + "ended": false + }, + "log": { + "version": 1, + "events": [ + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [111], + "correct": true, + "wordIndex": 0, + "charIndex": 0, + "inputValue": [111] + } + }, + { + "type": "timer", + "testMs": 0, + "data": { + "event": "start", + "timer": 0, + "date": 0 + } + } + ], + "context": { + "targetWords": [ + [111, 110, 101, 32], + [116, 119, 111] + ], + "mode": "words", + "mode2": "2", + "koreanStatus": false, + "bailedOut": false, + "isFunboxWithNospacePropertyActive": false + } + } + }, + { + "state": { + "wordIndex": 0, + "inputValue": [111], + "active": true, + "ended": false + }, + "log": { + "version": 1, + "events": [ + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [111], + "correct": true, + "wordIndex": 0, + "charIndex": 0, + "inputValue": [111] + } + }, + { + "type": "timer", + "testMs": 0, + "data": { + "event": "start", + "timer": 0, + "date": 0 + } + }, + { + "type": "timer", + "testMs": 3300, + "data": { + "event": "step", + "timer": 1, + "catchup": true + } + }, + { + "type": "timer", + "testMs": 3300, + "data": { + "event": "step", + "timer": 2, + "catchup": true + } + }, + { + "type": "timer", + "testMs": 3300, + "data": { + "event": "step", + "timer": 3, + "drift": 2300 + } + } + ], + "context": { + "targetWords": [ + [111, 110, 101, 32], + [116, 119, 111] + ], + "mode": "words", + "mode2": "2", + "koreanStatus": false, + "bailedOut": false, + "isFunboxWithNospacePropertyActive": false + } + } + }, + { + "state": { + "wordIndex": 0, + "inputValue": [111], + "active": true, + "ended": false + }, + "log": { + "version": 1, + "events": [ + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [111], + "correct": true, + "wordIndex": 0, + "charIndex": 0, + "inputValue": [111] + } + }, + { + "type": "timer", + "testMs": 0, + "data": { + "event": "start", + "timer": 0, + "date": 0 + } + }, + { + "type": "timer", + "testMs": 3300, + "data": { + "event": "step", + "timer": 1, + "catchup": true + } + }, + { + "type": "timer", + "testMs": 3300, + "data": { + "event": "step", + "timer": 2, + "catchup": true + } + }, + { + "type": "timer", + "testMs": 3300, + "data": { + "event": "step", + "timer": 3, + "drift": 2300 + } + }, + { + "type": "timer", + "testMs": 4900, + "data": { + "event": "step", + "timer": 4, + "drift": 900 + } + } + ], + "context": { + "targetWords": [ + [111, 110, 101, 32], + [116, 119, 111] + ], + "mode": "words", + "mode2": "2", + "koreanStatus": false, + "bailedOut": false, + "isFunboxWithNospacePropertyActive": false + } + } + }, + { + "state": { + "wordIndex": 0, + "inputValue": [111], + "active": false, + "ended": true + }, + "log": { + "version": 1, + "events": [ + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [111], + "correct": true, + "wordIndex": 0, + "charIndex": 0, + "inputValue": [111] + } + }, + { + "type": "timer", + "testMs": 0, + "data": { + "event": "start", + "timer": 0, + "date": 0 + } + }, + { + "type": "timer", + "testMs": 3300, + "data": { + "event": "step", + "timer": 1, + "catchup": true + } + }, + { + "type": "timer", + "testMs": 3300, + "data": { + "event": "step", + "timer": 2, + "catchup": true + } + }, + { + "type": "timer", + "testMs": 3300, + "data": { + "event": "step", + "timer": 3, + "drift": 2300 + } + }, + { + "type": "timer", + "testMs": 4900, + "data": { + "event": "step", + "timer": 4, + "drift": 900 + } + }, + { + "type": "timer", + "testMs": 4900, + "data": { + "event": "end", + "timer": 4, + "date": 0 + } + } + ], + "context": { + "targetWords": [ + [111, 110, 101, 32], + [116, 119, 111] + ], + "mode": "words", + "mode2": "2", + "koreanStatus": false, + "bailedOut": false, + "isFunboxWithNospacePropertyActive": false + } + } + }, + { + "state": { + "wordIndex": 0, + "inputValue": [], + "active": false, + "ended": false + }, + "log": { + "version": 1, + "events": [], + "context": { + "targetWords": [ + [111, 110, 101, 32], + [116, 119, 111] + ], + "mode": "words", + "mode2": "2", + "koreanStatus": false, + "bailedOut": false, + "isFunboxWithNospacePropertyActive": false + } + } + }, + { + "state": { + "wordIndex": 0, + "inputValue": [], + "active": false, + "ended": false + }, + "log": { + "version": 1, + "events": [], + "context": { + "targetWords": [ + [111, 110, 101, 32], + [116, 119, 111] + ], + "mode": "words", + "mode2": "2", + "koreanStatus": false, + "bailedOut": false, + "isFunboxWithNospacePropertyActive": false + } + } + } + ] +} diff --git a/packages/typing-fixtures/oracle/unicode-halves.json b/packages/typing-fixtures/oracle/unicode-halves.json new file mode 100644 index 000000000..04ffff189 --- /dev/null +++ b/packages/typing-fixtures/oracle/unicode-halves.json @@ -0,0 +1,282 @@ +{ + "name": "unicode-halves", + "frames": [ + { + "state": { + "wordIndex": 0, + "inputValue": [55357], + "active": true, + "ended": false + }, + "log": { + "version": 1, + "events": [ + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [55357], + "correct": true, + "wordIndex": 0, + "charIndex": 0, + "inputValue": [55357] + } + }, + { + "type": "timer", + "testMs": 0, + "data": { + "event": "start", + "timer": 0, + "date": 0 + } + } + ], + "context": { + "targetWords": [ + [55357, 56832, 32], + [101, 769] + ], + "mode": "words", + "mode2": "2", + "koreanStatus": false, + "bailedOut": false, + "isFunboxWithNospacePropertyActive": false + } + } + }, + { + "state": { + "wordIndex": 0, + "inputValue": [55357, 56832], + "active": true, + "ended": false + }, + "log": { + "version": 1, + "events": [ + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [55357], + "correct": true, + "wordIndex": 0, + "charIndex": 0, + "inputValue": [55357] + } + }, + { + "type": "timer", + "testMs": 0, + "data": { + "event": "start", + "timer": 0, + "date": 0 + } + }, + { + "type": "input", + "testMs": 20, + "data": { + "inputType": "insertText", + "data": [56832], + "correct": true, + "wordIndex": 0, + "charIndex": 1, + "inputValue": [55357, 56832] + } + } + ], + "context": { + "targetWords": [ + [55357, 56832, 32], + [101, 769] + ], + "mode": "words", + "mode2": "2", + "koreanStatus": false, + "bailedOut": false, + "isFunboxWithNospacePropertyActive": false + } + } + }, + { + "state": { + "wordIndex": 1, + "inputValue": [], + "active": true, + "ended": false + }, + "log": { + "version": 1, + "events": [ + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [55357], + "correct": true, + "wordIndex": 0, + "charIndex": 0, + "inputValue": [55357] + } + }, + { + "type": "timer", + "testMs": 0, + "data": { + "event": "start", + "timer": 0, + "date": 0 + } + }, + { + "type": "input", + "testMs": 20, + "data": { + "inputType": "insertText", + "data": [56832], + "correct": true, + "wordIndex": 0, + "charIndex": 1, + "inputValue": [55357, 56832] + } + }, + { + "type": "input", + "testMs": 40, + "data": { + "inputType": "insertText", + "data": [32], + "correct": true, + "wordIndex": 0, + "charIndex": 2, + "inputValue": [55357, 56832, 32], + "commitsWord": true + } + } + ], + "context": { + "targetWords": [ + [55357, 56832, 32], + [101, 769] + ], + "mode": "words", + "mode2": "2", + "koreanStatus": false, + "bailedOut": false, + "isFunboxWithNospacePropertyActive": false + } + } + }, + { + "state": { + "wordIndex": 1, + "inputValue": [101, 769], + "active": false, + "ended": true + }, + "log": { + "version": 1, + "events": [ + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [55357], + "correct": true, + "wordIndex": 0, + "charIndex": 0, + "inputValue": [55357] + } + }, + { + "type": "timer", + "testMs": 0, + "data": { + "event": "start", + "timer": 0, + "date": 0 + } + }, + { + "type": "input", + "testMs": 20, + "data": { + "inputType": "insertText", + "data": [56832], + "correct": true, + "wordIndex": 0, + "charIndex": 1, + "inputValue": [55357, 56832] + } + }, + { + "type": "input", + "testMs": 40, + "data": { + "inputType": "insertText", + "data": [32], + "correct": true, + "wordIndex": 0, + "charIndex": 2, + "inputValue": [55357, 56832, 32], + "commitsWord": true + } + }, + { + "type": "input", + "testMs": 100, + "data": { + "inputType": "insertText", + "data": [101], + "correct": true, + "wordIndex": 1, + "charIndex": 0, + "inputValue": [101], + "lastWord": true + } + }, + { + "type": "input", + "testMs": 100, + "data": { + "inputType": "insertText", + "data": [769], + "correct": true, + "wordIndex": 1, + "charIndex": 1, + "inputValue": [101, 769], + "lastWord": true + } + }, + { + "type": "timer", + "testMs": 100, + "data": { + "event": "end", + "timer": 0, + "date": 0 + } + } + ], + "context": { + "targetWords": [ + [55357, 56832, 32], + [101, 769] + ], + "mode": "words", + "mode2": "2", + "koreanStatus": false, + "bailedOut": false, + "isFunboxWithNospacePropertyActive": false + } + } + } + ] +} From 259adfe849c1263f62bf06400bb02ca078e41f25 Mon Sep 17 00:00:00 2001 From: Lakshmi Tanmay Date: Fri, 23 Oct 2026 00:00:43 +0530 Subject: [PATCH 05/93] feat(engine): add pinned Rust workspace and finite UTF-16 session slice --- .gitignore | 4 + Cargo.lock | 363 ++++++++++++ Cargo.toml | 28 + crates/typing-engine-wasm/Cargo.toml | 20 + crates/typing-engine-wasm/src/lib.rs | 62 +++ crates/typing-engine/Cargo.toml | 15 + .../typing-engine/src/bin/export-bindings.rs | 16 + .../typing-engine/src/bin/fixture-runner.rs | 26 + crates/typing-engine/src/lib.rs | 25 + crates/typing-engine/src/session.rs | 521 ++++++++++++++++++ crates/typing-engine/src/text.rs | 79 +++ crates/typing-engine/tests/slice.rs | 96 ++++ package.json | 7 +- rust-toolchain.toml | 5 + scripts/engine.ts | 85 +++ scripts/tsconfig.json | 10 + 16 files changed, 1361 insertions(+), 1 deletion(-) create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 crates/typing-engine-wasm/Cargo.toml create mode 100644 crates/typing-engine-wasm/src/lib.rs create mode 100644 crates/typing-engine/Cargo.toml create mode 100644 crates/typing-engine/src/bin/export-bindings.rs create mode 100644 crates/typing-engine/src/bin/fixture-runner.rs create mode 100644 crates/typing-engine/src/lib.rs create mode 100644 crates/typing-engine/src/session.rs create mode 100644 crates/typing-engine/src/text.rs create mode 100644 crates/typing-engine/tests/slice.rs create mode 100644 rust-toolchain.toml create mode 100644 scripts/engine.ts create mode 100644 scripts/tsconfig.json diff --git a/.gitignore b/.gitignore index 3b04072dd..2f6a763df 100644 --- a/.gitignore +++ b/.gitignore @@ -138,3 +138,7 @@ frontend/playwright-report/ # TanStack Router generator scratch files .tanstack/ + +# Rust / generated Wasm and type bindings +/target/ +/packages/typing-engine/generated/ diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 000000000..c1c7beee1 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,363 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "bumpalo" +version = "3.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" + +[[package]] +name = "cfg-if" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7648175b45a9a48536d676f68d918270699102aa8dab5496df06904c914600" + +[[package]] +name = "equivalent" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00d174d5400e5e8fd687ad1049e2f578285fa914201b1af7e8b112a4546bd826" + +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + +[[package]] +name = "futures-core" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92d699e522242e69e3003b94ecc1f960f3a5e015aa7c5d7486e65ad01dd94f5e" + +[[package]] +name = "futures-task" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd417de3d1d015fc3bfd2b1ea46dfc7bab72ef86f1cc7cc9c78e728b34a6d1fd" + +[[package]] +name = "futures-util" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d50a92467f8ba5dd6e3ee5d4bd04d73ab2e4e1c44474a0674821dfce14b79bc" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "slab", +] + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + +[[package]] +name = "indexmap" +version = "2.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc4e190f5d26ca7051642629da2c52fc03bde85a03197c99408dcd291734c855" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "js-sys" +version = "0.3.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7883d941dae510fb2d978fc3fe018c71c9e2892fd38854de3e8b92c2e5ad9cc5" +dependencies = [ + "cfg-if", + "futures-util", + "wasm-bindgen", +] + +[[package]] +name = "memchr" +version = "2.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "proc-macro2" +version = "1.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rustversion" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" + +[[package]] +name = "serde" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde-wasm-bindgen" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8302e169f0eddcc139c70f139d19d6467353af16f9fce27e8c30158036a1e16b" +dependencies = [ + "js-sys", + "serde", + "wasm-bindgen", +] + +[[package]] +name = "serde_core" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.9", +] + +[[package]] +name = "serde_json" +version = "1.0.154" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7e9cc8b1b85264074fbcc02a88680c4096b1e47df8f739dceb03bf482f04bd6" +dependencies = [ + "foldhash", + "indexmap", + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "syn" +version = "2.0.119" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "3.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d78c8dee4c7bf0e14673097256fed6142ce9d3b85a408189d07482442145823b" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "thiserror" +version = "2.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09e52cb86a36cede5cb101bf8908837b3e4c6e5e59fe7fd85c23fb56200d189e" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe5197923287db20a58125f0bc85c062f7f2c892de97b18c356f9efb14b28524" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.9", +] + +[[package]] +name = "ts-rs" +version = "12.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "756050066659291d47a554a9f558125db17428b073c5ffce1daf5dcb0f7231d8" +dependencies = [ + "serde_json", + "thiserror", + "ts-rs-macros", +] + +[[package]] +name = "ts-rs-macros" +version = "12.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d90eea51bc7988ef9e674bf80a85ba6804739e535e9cab48e4bb34a8b652aa" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", + "termcolor", +] + +[[package]] +name = "typing-engine" +version = "0.1.0" +dependencies = [ + "serde", + "serde_json", + "ts-rs", +] + +[[package]] +name = "typing-engine-wasm" +version = "0.1.0" +dependencies = [ + "serde", + "serde-wasm-bindgen", + "serde_json", + "typing-engine", + "wasm-bindgen", +] + +[[package]] +name = "unicode-ident" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2c754d6c33795a1c324727428e5a7dedb5b06195f9890bdbcba760d3e246563" + +[[package]] +name = "wasm-bindgen" +version = "0.2.129" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bb54f33acc68fd454578d9820b0bde1a1a3d17aa17bb7b6595806d02886d409" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.129" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e29d0c35b16e224a7eeb5cd2d25e3e1968fbd65604117b44d3b789d00ee8535" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.129" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f501a8bc3719dba86ef8ae4728879c08001bea749eb1333ac5b91e040e2a6b7" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn 3.0.9", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.129" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23f0c9c52aa7cd7d77769a4cfe2a9adb1b331f489a41d912ce14513d5ab995c6" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "zmij" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 000000000..a9ce7534a --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,28 @@ +[workspace] +members = ["crates/typing-engine", "crates/typing-engine-wasm"] +resolver = "3" + +[workspace.package] +version = "0.1.0" +edition = "2024" +rust-version = "1.99" +license = "GPL-3.0-only" +repository = "https://github.com/voltcrash/oxytype" + +[workspace.dependencies] +serde = { version = "1.0.229", features = ["derive"] } +serde_json = { version = "1.0.151", features = ["preserve_order"] } +wasm-bindgen = "=0.2.129" +ts-rs = { version = "=12.0.1", features = ["serde-json-impl"] } + +[workspace.lints.rust] +unsafe_code = "forbid" + +[workspace.lints.clippy] +all = "warn" + +[profile.release] +lto = true +codegen-units = 1 +opt-level = "s" +strip = "debuginfo" diff --git a/crates/typing-engine-wasm/Cargo.toml b/crates/typing-engine-wasm/Cargo.toml new file mode 100644 index 000000000..830578b23 --- /dev/null +++ b/crates/typing-engine-wasm/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "typing-engine-wasm" +version.workspace = true +edition.workspace = true +rust-version.workspace = true +license.workspace = true +repository.workspace = true + +[lib] +crate-type = ["cdylib", "rlib"] + +[dependencies] +typing-engine = { path = "../typing-engine" } +serde_json.workspace = true +wasm-bindgen.workspace = true +serde.workspace = true +serde-wasm-bindgen = "0.6.5" + +[lints] +workspace = true diff --git a/crates/typing-engine-wasm/src/lib.rs b/crates/typing-engine-wasm/src/lib.rs new file mode 100644 index 000000000..e018cc752 --- /dev/null +++ b/crates/typing-engine-wasm/src/lib.rs @@ -0,0 +1,62 @@ +use serde::Serialize; +use typing_engine::{BINDING_VERSION, Config, Session, Text}; +use wasm_bindgen::prelude::*; + +#[wasm_bindgen] +pub fn binding_version() -> u32 { + BINDING_VERSION +} + +/// Thin serialization/ownership boundary. All decisions live in typing-engine. +#[wasm_bindgen] +pub struct WasmSession { + session: Option, +} + +#[wasm_bindgen] +impl WasmSession { + #[wasm_bindgen(constructor)] + pub fn new(config: &str, words: &str, version: u32) -> Result { + if version != BINDING_VERSION { + return Err(JsError::new("Binding version mismatch")); + } + let config: Config = serde_json::from_str(config)?; + let words: Vec = serde_json::from_str(words)?; + Ok(Self { + session: Some(Session::new(config, words).map_err(|message| JsError::new(&message))?), + }) + } + pub fn command(&mut self, command: &str) -> Result { + let command = serde_json::from_str(command)?; + let session = self + .session + .as_mut() + .ok_or_else(|| JsError::new("Session disposed"))?; + let update = session + .apply(command) + .map_err(|message| JsError::new(&message))?; + Ok(serde_json::to_string(&update)?) + } + pub fn log(&self) -> Result { + let session = self + .session + .as_ref() + .ok_or_else(|| JsError::new("Session disposed"))?; + Ok(serde_json::to_string(&session.log())?) + } + pub fn command_value(&mut self, command: JsValue) -> Result { + let command = serde_wasm_bindgen::from_value(command)?; + let session = self + .session + .as_mut() + .ok_or_else(|| JsError::new("Session disposed"))?; + let update = session + .apply(command) + .map_err(|message| JsError::new(&message))?; + Ok(update + .serialize(&serde_wasm_bindgen::Serializer::new().serialize_maps_as_objects(true))?) + } + pub fn dispose(&mut self) { + self.session = None; + } +} diff --git a/crates/typing-engine/Cargo.toml b/crates/typing-engine/Cargo.toml new file mode 100644 index 000000000..363ba8801 --- /dev/null +++ b/crates/typing-engine/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "typing-engine" +version.workspace = true +edition.workspace = true +rust-version.workspace = true +license.workspace = true +repository.workspace = true + +[dependencies] +serde.workspace = true +serde_json.workspace = true +ts-rs.workspace = true + +[lints] +workspace = true diff --git a/crates/typing-engine/src/bin/export-bindings.rs b/crates/typing-engine/src/bin/export-bindings.rs new file mode 100644 index 000000000..4c00e38d1 --- /dev/null +++ b/crates/typing-engine/src/bin/export-bindings.rs @@ -0,0 +1,16 @@ +use ts_rs::TS; +use typing_engine::{ + Command, Config, + session::{State, Update}, +}; + +fn main() -> Result<(), Box> { + let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR")) + .join("../../packages/typing-engine/generated/types"); + let config = ts_rs::Config::new().with_out_dir(root); + Command::export_all(&config)?; + Config::export_all(&config)?; + State::export_all(&config)?; + Update::export_all(&config)?; + Ok(()) +} diff --git a/crates/typing-engine/src/bin/fixture-runner.rs b/crates/typing-engine/src/bin/fixture-runner.rs new file mode 100644 index 000000000..23ab2b890 --- /dev/null +++ b/crates/typing-engine/src/bin/fixture-runner.rs @@ -0,0 +1,26 @@ +use serde::Deserialize; +use serde_json::json; +use std::io::{self, Read}; +use typing_engine::{Command, Config, Session, Text}; + +#[derive(Deserialize)] +struct Fixture { + name: String, + config: Config, + words: Vec, + commands: Vec, +} + +fn main() -> Result<(), Box> { + let mut input = String::new(); + io::stdin().read_to_string(&mut input)?; + let fixture: Fixture = serde_json::from_str(&input)?; + let mut session = Session::new(fixture.config, fixture.words)?; + let mut frames = vec![]; + for command in fixture.commands { + let update = session.apply(command)?; + frames.push(json!({"state":update.state,"log":session.log()})); + } + println!("{}", json!({"name":fixture.name,"frames":frames})); + Ok(()) +} diff --git a/crates/typing-engine/src/lib.rs b/crates/typing-engine/src/lib.rs new file mode 100644 index 000000000..e9ff18836 --- /dev/null +++ b/crates/typing-engine/src/lib.rs @@ -0,0 +1,25 @@ +//! Platform-independent typing rules. Hosts provide content, observations and time. +pub mod session; +pub mod text; + +pub use session::{Command, Config, Session}; +pub use text::Text; + +pub const BINDING_VERSION: u32 = 1; +pub const BEHAVIOR_VERSION: u32 = 1; +pub const EVENT_LOG_VERSION: u32 = 1; + +/// Match JavaScript Math.round: halfway values round toward positive infinity. +pub fn js_round(value: f64) -> f64 { + let floor = value.floor(); + if value - floor < 0.5 { + floor + } else { + floor + 1.0 + } +} + +/// Existing roundTo2 uses Number.EPSILON before rounding. +pub fn round2(value: f64) -> f64 { + js_round((value + f64::EPSILON) * 100.0) / 100.0 +} diff --git a/crates/typing-engine/src/session.rs b/crates/typing-engine/src/session.rs new file mode 100644 index 000000000..7dc2ac9c6 --- /dev/null +++ b/crates/typing-engine/src/session.rs @@ -0,0 +1,521 @@ +use crate::{ + EVENT_LOG_VERSION, Text, round2, + text::{delete_word, is_space, normalize}, +}; +use serde::{Deserialize, Serialize}; +use serde_json::{Value, json}; +use ts_rs::TS; + +#[derive(Clone, Debug, Deserialize, Serialize, TS)] +#[serde(default, rename_all = "camelCase")] +pub struct Config { + pub mode: String, + pub words: u32, + pub time: f64, + pub language: String, + pub difficulty: String, + pub strict_space: bool, + pub stop_on_error: String, + pub delete_on_error: String, + pub quick_end: bool, + pub freedom_mode: bool, + pub confidence_mode: String, + pub blind_mode: bool, + pub code_unindent_on_backspace: bool, + pub funbox: Vec, +} + +impl Default for Config { + fn default() -> Self { + Self { + mode: "time".into(), + words: 50, + time: 30.0, + language: "english".into(), + difficulty: "normal".into(), + strict_space: false, + stop_on_error: "off".into(), + delete_on_error: "off".into(), + quick_end: false, + freedom_mode: false, + confidence_mode: "off".into(), + blind_mode: false, + code_unindent_on_backspace: false, + funbox: vec![], + } + } +} + +#[derive(Clone, Debug, Deserialize, Serialize, TS)] +#[serde(tag = "op", rename_all = "camelCase")] +pub enum Command { + #[serde(rename_all = "camelCase")] + Insert { + data: Text, + now: f64, + #[serde(default)] + date: f64, + #[serde(default)] + correct_shift_used: Option, + #[serde(default)] + automatic: Option, + #[serde(default)] + is_composition_ending: Option, + }, + Delete { + kind: String, + now: f64, + #[serde(default)] + date: f64, + }, + Start { + now: f64, + #[serde(default)] + date: f64, + }, + Advance { + now: f64, + #[serde(default)] + date: f64, + }, + Finish { + now: f64, + #[serde(default)] + date: f64, + #[serde(default)] + reason: Option, + }, + End { + now: f64, + #[serde(default)] + date: f64, + }, + Abandon { + now: f64, + #[serde(default)] + date: f64, + }, + Reset { + #[serde(default)] + now: f64, + }, + #[serde(rename_all = "camelCase")] + Observe { + now: f64, + event_type: String, + event_data: Value, + }, +} + +#[derive(Clone, Debug, Serialize, Deserialize, TS)] +#[serde(rename_all = "camelCase")] +pub struct State { + pub word_index: usize, + pub input_value: Text, + pub active: bool, + pub ended: bool, + #[serde(skip_serializing_if = "Option::is_none")] + #[ts(optional)] + pub reason: Option, +} + +#[derive(Clone, Debug, Serialize, Deserialize, TS)] +#[serde(rename_all = "camelCase")] +pub struct Event { + #[serde(rename = "type")] + pub kind: String, + pub test_ms: f64, + pub data: Value, +} + +#[derive(Serialize, TS)] +pub struct Update { + pub state: State, + pub events: Vec, +} + +#[derive(Clone)] +struct Recorded { + kind: String, + now: f64, + data: Value, +} + +pub struct Session { + config: Config, + words: Vec, + input: Vec, + index: usize, + active: bool, + ended: bool, + reason: Option, + start: Option, + ticks: u32, + events: Vec, +} + +impl Session { + pub fn new(config: Config, words: Vec) -> Result { + if !["time", "words", "quote", "custom", "zen"].contains(&config.mode.as_str()) { + return Err("Invalid mode".into()); + } + if !config.time.is_finite() || config.time < 0.0 { + return Err("Invalid time limit".into()); + } + Ok(Self { + config, + input: vec![Text::default(); words.len().max(1)], + words, + index: 0, + active: false, + ended: false, + reason: None, + start: None, + ticks: 0, + events: vec![], + }) + } + + pub fn state(&self) -> State { + State { + word_index: self.index, + input_value: self.current(), + active: self.active, + ended: self.ended, + reason: self.reason.clone(), + } + } + fn current(&self) -> Text { + self.input.get(self.index).cloned().unwrap_or_default() + } + fn target(&self) -> Text { + self.words.get(self.index).cloned().unwrap_or_default() + } + fn nospace(&self) -> bool { + self.config.funbox.iter().any(|name| { + ["nospace", "arrows", "morse", "underscore_spaces"].contains(&name.as_str()) + }) + } + fn record(&mut self, kind: &str, now: f64, data: Value) { + self.events.push(Recorded { + kind: kind.into(), + now: round2(now), + data, + }); + } + fn start(&mut self, now: f64, date: f64) { + if self.active || self.ended { + return; + } + self.active = true; + self.start = Some(now); + self.record( + "timer", + now, + json!({"event":"start", "timer":0, "date":date}), + ); + } + fn end(&mut self, now: f64, date: f64, reason: Option) { + if !self.active || self.ended { + return; + } + self.active = false; + self.ended = true; + self.reason = reason; + self.record("timer", now, json!({"event":"end", "timer":((now-self.start.unwrap_or(now))/1000.0).floor(), "date":date})); + } + + pub fn apply(&mut self, command: Command) -> Result { + let now = match &command { + Command::Insert { now, .. } + | Command::Delete { now, .. } + | Command::Start { now, .. } + | Command::Advance { now, .. } + | Command::Finish { now, .. } + | Command::End { now, .. } + | Command::Abandon { now, .. } + | Command::Reset { now } + | Command::Observe { now, .. } => *now, + }; + if !now.is_finite() || now.abs() > 9_007_199_254_740_991.0 { + return Err("Invalid timestamp".into()); + } + let before = self.events.len(); + match command { + Command::Insert { + data, + now, + date, + correct_shift_used, + automatic, + is_composition_ending, + } => { + for unit in data.0 { + if self.ended { + break; + } + self.insert( + unit, + now, + date, + correct_shift_used, + automatic == Some(true), + is_composition_ending == Some(true), + ); + } + } + Command::Delete { kind, now, .. } => { + if !["deleteContentBackward", "deleteWordBackward"].contains(&kind.as_str()) { + return Err("Invalid deletion kind".into()); + } + self.delete(&kind, now); + } + Command::Start { now, date } => self.start(now, date), + Command::Finish { now, date, reason } => self.end(now, date, reason), + Command::End { now, date } | Command::Abandon { now, date } => { + self.end(now, date, None) + } + Command::Advance { now, date } => self.advance(now, date), + Command::Reset { .. } => { + self.index = 0; + self.active = false; + self.ended = false; + self.reason = None; + self.start = None; + self.ticks = 0; + self.events.clear(); + self.input = vec![Text::default(); self.words.len().max(1)]; + } + Command::Observe { .. } => { + return Err("Observations not yet implemented in proof slice".into()); + } + } + let origin = self.origin(); + let events = self + .events + .get(before..) + .unwrap_or_default() + .iter() + .map(|event| Self::event(event, origin)) + .collect(); + Ok(Update { + state: self.state(), + events, + }) + } + + fn insert( + &mut self, + unit: u16, + now: f64, + date: f64, + shift: Option, + automatic: bool, + composition: bool, + ) { + let input = self.current(); + let target = self.target(); + let nospace = self.nospace(); + if unit == 10 + && self.config.mode != "zen" + && !self.words.iter().any(|word| word.0.contains(&10)) + { + return; + } + if is_space(unit) && nospace { + return; + } + let unit = normalize( + unit, + target.0.get(input.len()).copied(), + &self.config.language, + ); + let commit = is_space(unit) || unit == 10 || (nospace && input.len() + 1 == target.len()); + if is_space(unit) + && input.is_empty() + && !self.config.strict_space + && self.config.difficulty == "normal" + && !self.config.delete_on_error.contains("hard") + { + return; + } + let mut next = input.clone(); + next.0.push(unit); + let word_correct = next == target; + let advance = commit + && (self.config.mode == "zen" + || (!(input.is_empty() + && !nospace + && (self.config.strict_space || self.config.difficulty != "normal")) + && (word_correct + || (self.config.stop_on_error == "off" + && self.config.delete_on_error == "off")))); + let limit = if self.config.mode == "zen" { + 30 + } else { + target.len() + 20 + }; + if input.len() >= limit && !advance { + return; + } + self.start(now, date); + let correct = self.config.mode == "zen" + || (shift != Some(false) && target.0.get(input.len()) == Some(&unit)); + let stopped = (self.config.stop_on_error == "letter" && !correct) || shift == Some(false); + if stopped { + next = input.clone(); + } + let advance = advance && !stopped; + let mut data = json!({"inputType":"insertText", "data":Text(vec![unit]), "correct":correct, "wordIndex":self.index, + "charIndex":input.len(), "inputValue":next}); + for (key, yes) in [ + ("inputStopped", stopped), + ("commitsWord", advance), + ("lastWord", self.index + 1 == self.words.len()), + ("automatic", automatic), + ("isCompositionEnding", composition), + ] { + if yes { + data[key] = json!(true); + } + } + self.input + .resize(self.input.len().max(self.index + 1), Text::default()); + self.input[self.index] = next; + self.record("input", now, data); + // Full input/lifecycle port adds difficulty/deletion penalties after browser proof. + let finite = self.config.mode != "time" + && self.config.mode != "zen" + && !(self.config.mode == "words" && self.config.words == 0); + let quick = self.config.quick_end + && target.len() == input.len() + 1 + && self.config.stop_on_error == "off" + && self.config.delete_on_error == "off"; + if self.index + 1 >= self.words.len() && finite && (word_correct || quick || advance) { + self.end(now, date, None); + } else if advance && (self.index + 1 < self.words.len() || self.config.mode == "zen") { + self.index += 1; + } + } + + fn delete(&mut self, kind: &str, now: f64) { + if !self.active || self.ended { + return; + } + let input = self.current(); + if input.is_empty() && self.index == 0 { + return; + } + let prev = self + .index + .checked_sub(1) + .and_then(|index| self.input.get(index)) + .cloned() + .unwrap_or_default(); + let previous_correct = self.index > 0 && self.words.get(self.index - 1) == Some(&prev); + if !self.config.freedom_mode + && (self.config.confidence_mode == "max" + || (input.is_empty() && (self.config.confidence_mode == "on" || previous_correct))) + { + return; + } + let next = if input.is_empty() { + self.index -= 1; + let mut previous = prev; + if kind == "deleteWordBackward" { + previous.0.clear(); + } else if self.nospace() || matches!(previous.0.last(), Some(32 | 10)) { + previous.0.pop(); + } + previous + } else if kind == "deleteWordBackward" { + delete_word(&input) + } else { + let mut next = input.clone(); + next.0.pop(); + next + }; + let char_index = if input.is_empty() { + next.len() + } else { + input.len() + }; + self.record("input",now,json!({"inputType":kind,"wordIndex":self.index,"charIndex":char_index,"inputValue":next})); + self.input[self.index] = next; + } + + fn advance(&mut self, now: f64, date: f64) { + if !self.active { + return; + } + let start = self.start.unwrap_or(now); + let drift = round2(now - (start + f64::from(self.ticks + 1) * 1000.0)); + if drift < 0.0 { + return; + } + let due = ((now - start) / 1000.0).floor() as u32; + while self.active && self.ticks < due { + self.ticks += 1; + let mut data = json!({"event":"step","timer":self.ticks}); + if self.ticks < due { + data["catchup"] = json!(true); + } else { + data["drift"] = json!(drift); + } + self.record("timer", now, data); + if self.config.mode == "time" + && self.config.time > 0.0 + && f64::from(self.ticks) >= self.config.time + { + self.end(now, date, None); + } + } + } + + fn rank(kind: &str) -> u8 { + match kind { + "keyup" => 0, + "keydown" => 1, + "input" => 2, + "composition" => 3, + _ => 4, + } + } + fn origin(&self) -> f64 { + self.events + .iter() + .find(|e| e.kind == "timer" && e.data["event"] == "start") + .map(|e| e.now) + .unwrap_or_else(|| { + self.events + .iter() + .map(|e| e.now) + .reduce(f64::min) + .unwrap_or(0.0) + }) + } + fn event(event: &Recorded, origin: f64) -> Event { + Event { + kind: event.kind.clone(), + test_ms: round2(event.now - origin), + data: event.data.clone(), + } + } + pub fn log(&self) -> Value { + let mut events = self.events.clone(); + events.sort_by(|a, b| { + a.now + .total_cmp(&b.now) + .then(Self::rank(&a.kind).cmp(&Self::rank(&b.kind))) + }); + let mode2 = match self.config.mode.as_str() { + "words" => self.config.words.to_string(), + "time" => self.config.time.to_string(), + "quote" => "-1".into(), + other => other.into(), + }; + let korean=self.words.iter().flat_map(|word| &word.0).any(|c| matches!(*c,0xac00..=0xd7af | 0x1100..=0x11ff | 0x3130..=0x318f | 0xa960..=0xa97f | 0xd7b0..=0xd7ff)); + json!({"version":EVENT_LOG_VERSION,"events":events.iter().map(|event|Self::event(event,self.origin())).collect::>(), + "context":{"targetWords":self.words,"mode":self.config.mode,"mode2":mode2,"koreanStatus":korean,"bailedOut":false,"isFunboxWithNospacePropertyActive":self.nospace()}}) + } +} diff --git a/crates/typing-engine/src/text.rs b/crates/typing-engine/src/text.rs new file mode 100644 index 000000000..f1daa7f2d --- /dev/null +++ b/crates/typing-engine/src/text.rs @@ -0,0 +1,79 @@ +use serde::{Deserialize, Serialize}; +use ts_rs::TS; + +/// UTF-16 units, not Unicode scalars, UTF-8 bytes, graphemes or terminal columns. +#[derive(Clone, Debug, Default, Deserialize, Serialize, PartialEq, Eq, TS)] +pub struct Text(pub Vec); + +impl From<&str> for Text { + fn from(value: &str) -> Self { + Self(value.encode_utf16().collect()) + } +} + +impl Text { + pub fn len(&self) -> usize { + self.0.len() + } + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } + /// Only for native display; never use this lossy conversion for engine rules. + pub fn display_lossy(&self) -> String { + String::from_utf16_lossy(&self.0) + } +} + +pub fn is_space(unit: u16) -> bool { + matches!( + unit, + 0x0020 + | 0x2002 + | 0x2003 + | 0x2009 + | 0x3000 + | 0x00a0 + | 0x1680 + | 0x202f + | 0xfeff + | 0x2007 + | 0x2008 + | 0x2004 + | 0x200a + | 0x200b + ) +} + +pub fn normalize(unit: u16, target: Option, language: &str) -> u16 { + if let Some(target) = target { + let equivalent = unit == target + || ((unit == 32 || target == 32) && is_space(unit) && is_space(target)) + || ["’‘'ʼ׳ʻ᾽", "\"”“„", "–—-‐‑", ",‚"].iter().any(|set| { + set.encode_utf16().any(|c| c == unit) && set.encode_utf16().any(|c| c == target) + }) + || (language.split('_').next() == Some("russian") + && "ёеe".encode_utf16().any(|c| c == unit) + && "ёеe".encode_utf16().any(|c| c == target)); + if equivalent { + return target; + } + } + if is_space(unit) { 32 } else { unit } +} + +/// JavaScript \s, distinct from the app's directly typable space set. +pub fn js_whitespace(unit: u16) -> bool { + matches!(unit, 9..=13 | 32 | 160 | 0x1680 | 0x2000..=0x200a | 0x2028 | + 0x2029 | 0x202f | 0x205f | 0x3000 | 0xfeff) +} + +pub fn delete_word(input: &Text) -> Text { + let mut end = input.len(); + while end > 0 && js_whitespace(input.0[end - 1]) { + end -= 1; + } + while end > 0 && !js_whitespace(input.0[end - 1]) { + end -= 1; + } + Text(input.0[..end].to_vec()) +} diff --git a/crates/typing-engine/tests/slice.rs b/crates/typing-engine/tests/slice.rs new file mode 100644 index 000000000..ac586c88f --- /dev/null +++ b/crates/typing-engine/tests/slice.rs @@ -0,0 +1,96 @@ +use serde::Deserialize; +use serde_json::{Value, json}; +use typing_engine::{Command, Config, Session, Text}; + +#[derive(Deserialize)] +struct Fixture { + name: String, + config: Config, + words: Vec, + commands: Vec, +} + +fn equivalent(a: &Value, b: &Value) -> bool { + match (a, b) { + (Value::Number(a), Value::Number(b)) => a.as_f64() == b.as_f64(), + (Value::Array(a), Value::Array(b)) => { + a.len() == b.len() && a.iter().zip(b).all(|(a, b)| equivalent(a, b)) + } + (Value::Object(a), Value::Object(b)) => { + a.len() == b.len() + && a.iter() + .all(|(key, a)| b.get(key).is_some_and(|b| equivalent(a, b))) + } + _ => a == b, + } +} + +#[test] +fn finite_slice_matches_frozen_intermediate_oracle() { + let root = + std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("../../packages/typing-fixtures"); + for name in [ + "finite-edit", + "unicode-halves", + "freedom-regression", + "ime-typography", + "timer-stall", + "stop-letter", + "infinite-words", + ] { + let fixture: Fixture = serde_json::from_slice( + &std::fs::read(root.join(format!("commands/{name}.json"))).unwrap(), + ) + .unwrap(); + let expected: Value = serde_json::from_slice( + &std::fs::read(root.join(format!("oracle/{name}.json"))).unwrap(), + ) + .unwrap(); + let mut session = Session::new(fixture.config, fixture.words).unwrap(); + let mut frames = vec![]; + for command in fixture.commands { + let update = session.apply(command).unwrap(); + frames.push(json!({"state":update.state,"log":session.log()})); + } + let actual = json!({"name":fixture.name,"frames":frames}); + assert!( + equivalent(&actual, &expected), + "{name}\nactual={actual}\nexpected={expected}" + ); + } +} + +#[test] +fn independent_sessions_reset_without_cross_contamination() { + let config = Config { + mode: "words".into(), + words: 1, + ..Config::default() + }; + let mut a = Session::new(config.clone(), vec![Text::from("a")]).unwrap(); + let b = Session::new(config, vec![Text::from("b")]).unwrap(); + a.apply(serde_json::from_value(json!({"op":"insert","data":[97],"now":0})).unwrap()) + .unwrap(); + assert!(a.state().ended); + assert!(!b.state().active); + assert!(b.state().input_value.is_empty()); + a.apply(Command::Reset { now: 1.0 }).unwrap(); + assert!(!a.state().ended); + assert!(a.state().input_value.is_empty()); +} + +#[test] +fn invalid_command_leaves_state_unchanged() { + let mut session = Session::new(Config::default(), vec![]).unwrap(); + let before = session.log(); + assert!( + session + .apply(Command::Start { + now: f64::NAN, + date: 0.0 + }) + .is_err() + ); + assert_eq!(session.log(), before); + assert_eq!(Text(vec![0xd83d]).0, vec![0xd83d]); +} diff --git a/package.json b/package.json index e604ac111..70edf2047 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,12 @@ "check-assets-others": "vp run --filter @oxytype/frontend check-assets others", "check-assets-json": "vp run --filter @oxytype/frontend check-assets json", "knip": "knip", - "oxlint": "vp lint" + "oxlint": "vp lint", + "engine:build": "node scripts/engine.ts build", + "engine:test": "node scripts/engine.ts test", + "engine:lint": "node scripts/engine.ts lint", + "engine:fmt": "node scripts/engine.ts fmt", + "engine:fmt-fix": "node scripts/engine.ts fmt-fix" }, "devDependencies": { "@commitlint/cli": "21.2.3", diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 000000000..4a36f816b --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,5 @@ +[toolchain] +channel = "1.99.1" +profile = "minimal" +components = ["clippy", "rustfmt"] +targets = ["wasm32-unknown-unknown"] diff --git a/scripts/engine.ts b/scripts/engine.ts new file mode 100644 index 000000000..9de06d2a2 --- /dev/null +++ b/scripts/engine.ts @@ -0,0 +1,85 @@ +import { spawnSync } from "node:child_process"; +import { mkdirSync } from "node:fs"; +import { resolve } from "node:path"; + +const root = resolve(import.meta.dirname, ".."); +const output = resolve(root, "packages/typing-engine/generated"); +function run(command: string, args: string[]): void { + const result = spawnSync(command, args, { cwd: root, stdio: "inherit" }); + if (result.error !== undefined) throw result.error; + if (result.status !== 0) process.exit(result.status ?? 1); +} + +switch (process.argv[2]) { + case "build": { + const bindingVersion = "0.2.129"; + const installed = spawnSync("wasm-bindgen", ["--version"], { + encoding: "utf8", + }); + if (installed.stdout?.trim() !== `wasm-bindgen ${bindingVersion}`) { + run("cargo", [ + "install", + "wasm-bindgen-cli", + "--version", + bindingVersion, + "--locked", + ]); + } + run("cargo", [ + "build", + "--locked", + "--release", + "--target", + "wasm32-unknown-unknown", + "-p", + "typing-engine-wasm", + ]); + mkdirSync(output, { recursive: true }); + run("wasm-bindgen", [ + "--target", + "web", + "--out-dir", + output, + "--out-name", + "engine", + resolve( + root, + "target/wasm32-unknown-unknown/release/typing_engine_wasm.wasm", + ), + ]); + run("cargo", [ + "run", + "--locked", + "--quiet", + "-p", + "typing-engine", + "--bin", + "export-bindings", + ]); + break; + } + case "test": + run("cargo", ["test", "--locked", "--workspace"]); + break; + case "lint": + run("cargo", [ + "clippy", + "--locked", + "--workspace", + "--all-targets", + "--", + "-D", + "warnings", + ]); + break; + case "fmt": + run("cargo", ["fmt", "--all", "--check"]); + break; + case "fmt-fix": + run("cargo", ["fmt", "--all"]); + break; + default: + throw new Error( + "Usage: vp run engine:build|engine:test|engine:lint|engine:fmt|engine:fmt-fix", + ); +} diff --git a/scripts/tsconfig.json b/scripts/tsconfig.json new file mode 100644 index 000000000..16537f27f --- /dev/null +++ b/scripts/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../packages/typescript-config/base.json", + "compilerOptions": { + "noEmit": true, + "moduleResolution": "Bundler", + "module": "ESNext", + "target": "ES2022" + }, + "include": ["*.ts"] +} From 0ddcb84954abcbfe229eeea45c3db34e6fc71903 Mon Sep 17 00:00:00 2001 From: Lakshmi Tanmay Date: Fri, 23 Oct 2026 00:01:21 +0530 Subject: [PATCH 06/93] feat(engine): prove browser Wasm through Start Cloudflare assets and CSP --- frontend/__tests__/start/document.spec.ts | 4 +- frontend/e2e/engine-proof.migration.ts | 93 +++++++++++ frontend/package.json | 1 + frontend/src/app/env.d.ts | 1 + frontend/src/routeTree.gen.ts | 21 +++ frontend/src/routes/engine-proof.tsx | 148 ++++++++++++++++++ frontend/src/server/response-policy.server.ts | 2 +- frontend/vite-plugins/typing-engine.ts | 15 ++ frontend/vite.config.ts | 5 + packages/typing-engine/package.json | 13 ++ packages/typing-engine/src/browser.client.ts | 117 ++++++++++++++ packages/typing-engine/tsconfig.json | 11 ++ pnpm-lock.yaml | 14 ++ 13 files changed, 443 insertions(+), 2 deletions(-) create mode 100644 frontend/e2e/engine-proof.migration.ts create mode 100644 frontend/src/routes/engine-proof.tsx create mode 100644 frontend/vite-plugins/typing-engine.ts create mode 100644 packages/typing-engine/package.json create mode 100644 packages/typing-engine/src/browser.client.ts create mode 100644 packages/typing-engine/tsconfig.json diff --git a/frontend/__tests__/start/document.spec.ts b/frontend/__tests__/start/document.spec.ts index 8f08f15b6..187a8621c 100644 --- a/frontend/__tests__/start/document.spec.ts +++ b/frontend/__tests__/start/document.spec.ts @@ -44,12 +44,14 @@ describe("Start document and response policy in workerd", () => { expect(directives.get("frame-src")).toEqual([ "https://challenges.cloudflare.com", ]); - expect(directives.get("script-src")).toHaveLength(3); + expect(directives.get("script-src")).toHaveLength(4); expect(directives.get("script-src")).toEqual([ "'self'", + "'wasm-unsafe-eval'", "https://challenges.cloudflare.com", expect.stringMatching(/^'nonce-[^']+'$/), ]); + expect(directives.get("script-src")).not.toContain("'unsafe-eval'"); }); it("renders static fallbacks and gives every executable script a fresh CSP nonce", async () => { diff --git a/frontend/e2e/engine-proof.migration.ts b/frontend/e2e/engine-proof.migration.ts new file mode 100644 index 000000000..086561677 --- /dev/null +++ b/frontend/e2e/engine-proof.migration.ts @@ -0,0 +1,93 @@ +import { readFile, writeFile } from "node:fs/promises"; +import { resolve } from "node:path"; +import { expect, test } from "playwright/test"; + +const root = resolve(import.meta.dirname, "../../packages/typing-fixtures"); +test("native fixture matches browser Wasm through built Cloudflare assets", async ({ + page, +}) => { + const errors: string[] = []; + page.on("pageerror", (error) => errors.push(error.message)); + await page.goto("/engine-proof"); + const response = page.waitForResponse((response) => + response.url().endsWith(".wasm"), + ); + await page + .getByRole("textbox", { name: "Command fixture" }) + .fill(await readFile(resolve(root, "commands/finite-edit.json"), "utf8")); + await page.getByRole("button", { name: "Run fixture" }).click(); + const wasm = await response; + expect(wasm.status()).toBe(200); + expect(wasm.headers()["content-type"]).toContain("application/wasm"); + await expect(page.getByTestId("engine-proof-result")).not.toBeEmpty(); + const actual: unknown = JSON.parse( + await page.getByTestId("engine-proof-result").innerText(), + ); + const expected: unknown = JSON.parse( + await readFile(resolve(root, "oracle/finite-edit.json"), "utf8"), + ); + expect(actual).toEqual(expected); + expect(errors).toEqual([]); + await page.getByRole("textbox", { name: "Type finite slice" }).fill(""); + const inputToPaint: number[] = []; + for (const character of "one two") { + await page.evaluate(() => { + const input = document.querySelector( + 'input[aria-label="Type finite slice"]', + ); + if (input === null) throw new Error("Missing slice input"); + input.addEventListener( + "beforeinput", + () => performance.mark("slice-input"), + { once: true }, + ); + }); + await page + .getByRole("textbox", { name: "Type finite slice" }) + .pressSequentially(character); + inputToPaint.push( + await page.evaluate(async () => { + await new Promise((resolve) => + requestAnimationFrame(() => requestAnimationFrame(() => resolve())), + ); + return ( + performance.now() - + (performance.getEntriesByName("slice-input").at(-1)?.startTime ?? + performance.now()) + ); + }), + ); + } + await expect(page.getByLabel("Finite slice state")).toHaveText("finished"); + const output = process.env["ENGINE_PROOF_OUTPUT"]; + if (output !== undefined) { + const benchmark: unknown = JSON.parse( + await page.getByTestId("engine-proof-benchmark").innerText(), + ); + await writeFile( + output, + `${JSON.stringify({ fixture: "built Cloudflare Start; Desktop Chrome; 50 warmed samples per boundary; 200 UTF-16 units per sample; 7 real keyboard commits with double-rAF paint", browser: page.context().browser()?.version(), wasmBytes: (await wasm.body()).byteLength, wasmHeaders: wasm.headers(), inputToPaint, benchmark }, null, 2)}\n`, + ); + } + // Invalid creation must be recoverable; subsequent valid creation still works. + await page.getByRole("textbox", { name: "Command fixture" }).fill( + JSON.stringify({ + name: "invalid", + config: { mode: "invalid" }, + words: [], + commands: [], + }), + ); + await page.getByRole("button", { name: "Run fixture" }).click(); + await expect(page.getByTestId("engine-proof-error")).toContainText( + "Invalid mode", + ); + await page + .getByRole("textbox", { name: "Command fixture" }) + .fill(await readFile(resolve(root, "commands/finite-edit.json"), "utf8")); + await page.getByRole("button", { name: "Run fixture" }).click(); + await expect(page.getByTestId("engine-proof-result")).not.toBeEmpty(); + expect( + JSON.parse(await page.getByTestId("engine-proof-result").innerText()), + ).toEqual(expected); +}); diff --git a/frontend/package.json b/frontend/package.json index 9b25cbb7e..2257c7703 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -60,6 +60,7 @@ "@oxytype/funbox": "workspace:*", "@oxytype/schemas": "workspace:*", "@oxytype/typing-core": "workspace:*", + "@oxytype/typing-engine": "workspace:*", "@oxytype/util": "workspace:*", "@sentry/browser": "11.1.0", "@sentry/vite-plugin": "5.4.0", diff --git a/frontend/src/app/env.d.ts b/frontend/src/app/env.d.ts index 5a54c8e7c..8ffe023c7 100644 --- a/frontend/src/app/env.d.ts +++ b/frontend/src/app/env.d.ts @@ -1,5 +1,6 @@ // oxlint-disable-next-line typescript/consistent-type-definitions -- Vite augments this interface. interface ImportMetaEnv { + readonly OXYTYPE_ENGINE_PROOF: boolean; readonly OXYTYPE_OFFLINE: boolean; readonly OXYTYPE_BUILD_ID: string; readonly OXYTYPE_SENTRY_ORIGIN: string; diff --git a/frontend/src/routeTree.gen.ts b/frontend/src/routeTree.gen.ts index cc84bcc03..48cb6b5c4 100644 --- a/frontend/src/routeTree.gen.ts +++ b/frontend/src/routeTree.gen.ts @@ -11,6 +11,7 @@ import { Route as rootRouteImport } from './routes/__root' import { Route as R404DothtmlRouteImport } from './routes/404[.]html' import { Route as AppRouteImport } from './routes/_app' +import { Route as EngineProofRouteImport } from './routes/engine-proof' import { Route as OauthCallbackDothtmlRouteImport } from './routes/oauth-callback[.]html' import { Route as PrivacyPolicyDothtmlRouteImport } from './routes/privacy-policy[.]html' import { Route as AppIndexRouteImport } from './routes/_app.index' @@ -99,6 +100,11 @@ const AppRoute = AppRouteImport.update({ id: '/_app', getParentRoute: () => rootRouteImport, } as any) +const EngineProofRoute = EngineProofRouteImport.update({ + id: '/engine-proof', + path: '/engine-proof', + getParentRoute: () => rootRouteImport, +} as any) const OauthCallbackDothtmlRoute = OauthCallbackDothtmlRouteImport.update({ id: '/oauth-callback.html', path: '/oauth-callback.html', @@ -506,6 +512,7 @@ const ApiUsersTagsTagIdPersonalBestRoute = export interface FileRoutesByFullPath { '/404.html': typeof R404DothtmlRoute '/': typeof AppIndexRoute + '/engine-proof': typeof EngineProofRoute '/oauth-callback.html': typeof OauthCallbackDothtmlRoute '/privacy-policy.html': typeof PrivacyPolicyDothtmlRoute '/leaderboards': typeof AppLeaderboardsRoute @@ -585,6 +592,7 @@ export interface FileRoutesByFullPath { } export interface FileRoutesByTo { '/404.html': typeof R404DothtmlRoute + '/engine-proof': typeof EngineProofRoute '/oauth-callback.html': typeof OauthCallbackDothtmlRoute '/privacy-policy.html': typeof PrivacyPolicyDothtmlRoute '/': typeof AppIndexRoute @@ -667,6 +675,7 @@ export interface FileRoutesById { __root__: typeof rootRouteImport '/404.html': typeof R404DothtmlRoute '/_app': typeof AppRouteWithChildren + '/engine-proof': typeof EngineProofRoute '/oauth-callback.html': typeof OauthCallbackDothtmlRoute '/privacy-policy.html': typeof PrivacyPolicyDothtmlRoute '/_app/_authenticated': typeof AppAuthenticatedRouteWithChildren @@ -751,6 +760,7 @@ export interface FileRouteTypes { fullPaths: | '/404.html' | '/' + | '/engine-proof' | '/oauth-callback.html' | '/privacy-policy.html' | '/leaderboards' @@ -830,6 +840,7 @@ export interface FileRouteTypes { fileRoutesByTo: FileRoutesByTo to: | '/404.html' + | '/engine-proof' | '/oauth-callback.html' | '/privacy-policy.html' | '/' @@ -911,6 +922,7 @@ export interface FileRouteTypes { | '__root__' | '/404.html' | '/_app' + | '/engine-proof' | '/oauth-callback.html' | '/privacy-policy.html' | '/_app/_authenticated' @@ -994,6 +1006,7 @@ export interface FileRouteTypes { export interface RootRouteChildren { R404DothtmlRoute: typeof R404DothtmlRoute AppRoute: typeof AppRouteWithChildren + EngineProofRoute: typeof EngineProofRoute OauthCallbackDothtmlRoute: typeof OauthCallbackDothtmlRoute PrivacyPolicyDothtmlRoute: typeof PrivacyPolicyDothtmlRoute ApiSplatRoute: typeof ApiSplatRoute @@ -1032,6 +1045,13 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof AppRouteImport parentRoute: typeof rootRouteImport } + '/engine-proof': { + id: '/engine-proof' + path: '/engine-proof' + fullPath: '/engine-proof' + preLoaderRoute: typeof EngineProofRouteImport + parentRoute: typeof rootRouteImport + } '/oauth-callback.html': { id: '/oauth-callback.html' path: '/oauth-callback.html' @@ -1869,6 +1889,7 @@ const ApiPrefixDocsRouteWithChildren = ApiPrefixDocsRoute._addFileChildren( const rootRouteChildren: RootRouteChildren = { R404DothtmlRoute: R404DothtmlRoute, AppRoute: AppRouteWithChildren, + EngineProofRoute: EngineProofRoute, OauthCallbackDothtmlRoute: OauthCallbackDothtmlRoute, PrivacyPolicyDothtmlRoute: PrivacyPolicyDothtmlRoute, ApiSplatRoute: ApiSplatRoute, diff --git a/frontend/src/routes/engine-proof.tsx b/frontend/src/routes/engine-proof.tsx new file mode 100644 index 000000000..cf3f6d29a --- /dev/null +++ b/frontend/src/routes/engine-proof.tsx @@ -0,0 +1,148 @@ +import type { + BrowserSession, + Command, + Config, + Text, +} from "@oxytype/typing-engine/browser.client"; + +import { createFileRoute } from "@tanstack/solid-router"; +import { createSignal, onCleanup, Show } from "solid-js"; + +export const Route = createFileRoute("/engine-proof")({ + component: EngineProof, +}); + +// Build-time opt-in test harness. Regular deployment exposes no runnable harness. +function EngineProof() { + const [fixture, setFixture] = createSignal(""); + const [result, setResult] = createSignal(""); + const [failure, setFailure] = createSignal(""); + const [benchmark, setBenchmark] = createSignal(""); + const [sliceInput, setSliceInput] = createSignal(""); + const [sliceEnded, setSliceEnded] = createSignal(false); + let typingSession: BrowserSession | undefined; + let disposed = false; + const handles = new Set(); + onCleanup(() => { + disposed = true; + for (const handle of handles) handle.dispose(); + handles.clear(); + }); + const run = async (): Promise => { + setResult(""); + setFailure(""); + try { + const input = JSON.parse(fixture()) as { + name: string; + config: Partial; + words: Text[]; + commands: Command[]; + }; + const started = performance.now(); + const { loadEngine, benchmarkBoundary, encodeText } = + await import("@oxytype/typing-engine/browser.client"); + const engine = await loadEngine(); + if (disposed) return; + const initializationMs = performance.now() - started; + typingSession?.dispose(); + typingSession = engine.createSession({ mode: "words", words: 2 }, [ + encodeText("one "), + encodeText("two"), + ]); + handles.add(typingSession); + setSliceInput(""); + setSliceEnded(false); + const session = engine.createSession(input.config, input.words); + const isolated = engine.createSession(input.config, input.words); + handles.add(session); + handles.add(isolated); + const untouched = JSON.stringify(isolated.log()); + const frames = input.commands.map((command) => ({ + state: session.command(command).state, + log: session.log(), + })); + if (untouched !== JSON.stringify(isolated.log())) { + throw new Error("Session isolation failed"); + } + session.dispose(); + session.dispose(); + let staleRejected = false; + try { + session.log(); + } catch { + staleRejected = true; + } + if (!staleRejected) throw new Error("Stale session accepted"); + isolated.dispose(); + handles.delete(session); + handles.delete(isolated); + const recreated = engine.createSession(input.config, input.words); + recreated.dispose(); + setBenchmark( + JSON.stringify({ + initializationMs, + ...benchmarkBoundary(), + memoryBytes: engine.memoryBytes(), + }), + ); + setResult(JSON.stringify({ name: input.name, frames })); + } catch (error) { + for (const handle of handles) handle.dispose(); + handles.clear(); + setFailure(error instanceof Error ? error.message : String(error)); + } + }; + return ( + Unavailable

} + > +
+ + +
{result()}
+
{failure()}
+
{benchmark()}
+ { + event.preventDefault(); + if (typingSession === undefined) return; + const observation = event as InputEvent; + if (observation.data === null) return; + const data = Array.from( + { length: observation.data.length }, + (_, index) => observation.data?.charCodeAt(index) ?? 0, + ); + const update = typingSession.command({ + op: "insert", + data, + now: performance.now(), + date: Date.now(), + automatic: null, + correctShiftUsed: null, + isCompositionEnding: null, + }); + setSliceInput(String.fromCharCode(...update.state.inputValue)); + setSliceEnded(update.state.ended); + }} + /> + + {sliceEnded() ? "finished" : sliceInput()} + +
+
+ ); +} diff --git a/frontend/src/server/response-policy.server.ts b/frontend/src/server/response-policy.server.ts index 553f358c7..6233470d5 100644 --- a/frontend/src/server/response-policy.server.ts +++ b/frontend/src/server/response-policy.server.ts @@ -36,7 +36,7 @@ export function applyResponsePolicy( "frame-ancestors 'self'", "object-src 'none'", "frame-src https://challenges.cloudflare.com", - `script-src 'self' https://challenges.cloudflare.com 'nonce-${nonce}'${development ? " 'unsafe-eval'" : ""}`, + `script-src 'self' 'wasm-unsafe-eval' https://challenges.cloudflare.com 'nonce-${nonce}'${development ? " 'unsafe-eval'" : ""}`, `script-src-attr 'unsafe-hashes' 'sha256-${recoveryHash}'`, "style-src 'self' 'unsafe-inline'", "font-src 'self' data:", diff --git a/frontend/vite-plugins/typing-engine.ts b/frontend/vite-plugins/typing-engine.ts new file mode 100644 index 000000000..2fef89a25 --- /dev/null +++ b/frontend/vite-plugins/typing-engine.ts @@ -0,0 +1,15 @@ +import { execFileSync } from "node:child_process"; +import { resolve } from "node:path"; + +/** Every Vite entry (dev, Start, staging, production) bootstraps ignored bindings. */ +export function prepareTypingEngine(): void { + const root = resolve(import.meta.dirname, "../.."); + execFileSync( + process.execPath, + [resolve(root, "scripts/engine.ts"), "build"], + { + cwd: root, + stdio: "inherit", + }, + ); +} diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index 90b9b449b..76d900d25 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -7,6 +7,7 @@ import { tanstackStart } from "@tanstack/solid-start/plugin/vite"; import solid from "vite-plugin-solid"; import tailwindcss from "@tailwindcss/vite"; import { frontendLint } from "./lint.config"; +import { prepareTypingEngine } from "./vite-plugins/typing-engine"; import { fontStyles } from "./vite-plugins/font-styles"; import { workerNodeBuiltins } from "./vite-plugins/worker-node-builtins"; import { apiDocs } from "./vite-plugins/api-docs"; @@ -40,6 +41,7 @@ async function devPlugins(env: Record): Promise { } export default defineConfig(async ({ mode, command }) => { + prepareTypingEngine(); const env = loadEnv(mode, process.cwd(), ""); const buildId = resolveBuildId(mode !== "production"); if (mode === "production") { @@ -167,6 +169,9 @@ export default defineConfig(async ({ mode, command }) => { ...(command === "serve" ? await devPlugins(env) : []), ], define: { + "import.meta.env.OXYTYPE_ENGINE_PROOF": JSON.stringify( + process.env["OXYTYPE_ENGINE_PROOF"] === "true", + ), "import.meta.env.OXYTYPE_OFFLINE": JSON.stringify(mode === "offline"), // Client and Worker share it; logs and error reports join on this ID. "import.meta.env.OXYTYPE_BUILD_ID": JSON.stringify(buildId), diff --git a/packages/typing-engine/package.json b/packages/typing-engine/package.json new file mode 100644 index 000000000..8a7ef8160 --- /dev/null +++ b/packages/typing-engine/package.json @@ -0,0 +1,13 @@ +{ + "name": "@oxytype/typing-engine", + "private": true, + "license": "GPL-3.0", + "type": "module", + "exports": { + "./browser.client": "./src/browser.client.ts" + }, + "devDependencies": { + "@oxytype/typescript-config": "workspace:*", + "vite-plus": "catalog:" + } +} diff --git a/packages/typing-engine/src/browser.client.ts b/packages/typing-engine/src/browser.client.ts new file mode 100644 index 000000000..ac8303674 --- /dev/null +++ b/packages/typing-engine/src/browser.client.ts @@ -0,0 +1,117 @@ +import initialize, { + binding_version, + WasmSession, +} from "../generated/engine.js"; +import wasmUrl from "../generated/engine_bg.wasm?url"; +import type { Command } from "../generated/types/Command"; +import type { Config } from "../generated/types/Config"; +import type { Text } from "../generated/types/Text"; +import type { Update } from "../generated/types/Update"; + +export type { Command, Config, Text, Update }; +const BINDING_VERSION = 1; + +export function encodeText(value: string): Text { + return Array.from({ length: value.length }, (_, index) => + value.charCodeAt(index), + ); +} +export function decodeText(units: Text): string { + let value = ""; + for (let i = 0; i < units.length; i += 4096) { + value += String.fromCharCode(...units.slice(i, i + 4096)); + } + return value; +} + +/** Module preparation is async; all prepared session commands are synchronous. */ +export async function loadEngine(): Promise<{ + createSession: (config: Partial, words: Text[]) => BrowserSession; + memoryBytes: () => number; +}> { + const module = await initialize({ module_or_path: wasmUrl }); + if (binding_version() !== BINDING_VERSION) { + throw new Error("Typing engine binding version mismatch"); + } + return { + createSession: (config, words) => new BrowserSession(config, words), + memoryBytes: () => module.memory.buffer.byteLength, + }; +} + +export class BrowserSession { + private handle: WasmSession | undefined; + + constructor(config: Partial, words: Text[]) { + this.handle = new WasmSession( + JSON.stringify(config), + JSON.stringify(words), + BINDING_VERSION, + ); + } + command(command: Command): Update { + if (this.handle === undefined) { + throw new Error("Typing engine session disposed"); + } + return JSON.parse(this.handle.command(JSON.stringify(command))) as Update; + } + log(): unknown { + if (this.handle === undefined) { + throw new Error("Typing engine session disposed"); + } + return JSON.parse(this.handle.log()) as unknown; + } + commandObject(command: Command): Update { + if (this.handle === undefined) { + throw new Error("Typing engine session disposed"); + } + return this.handle.command_value(command) as Update; + } + dispose(): void { + const handle = this.handle; + if (handle === undefined) return; + this.handle = undefined; + handle.dispose(); + handle.free(); + } +} + +/** Compare incremental object conversion with a JSON boundary, same core. */ +export function benchmarkBoundary(): { jsonMs: number[]; objectMs: number[] } { + const jsonMs: number[] = []; + const objectMs: number[] = []; + const config: Partial = { mode: "words", words: 0 }; + const words = Array.from({ length: 101 }, () => encodeText("a ")); + for (let sample = 0; sample < 60; sample++) { + for (const [object, measurements] of [ + [false, jsonMs], + [true, objectMs], + ] as const) { + const session = new BrowserSession(config, words); + try { + const start = performance.now(); + for (let index = 0; index < 100; index++) { + const command: Command = { + op: "insert", + data: [97, 32], + now: index * 100, + date: 0, + automatic: null, + isCompositionEnding: null, + correctShiftUsed: null, + }; + const update = object + ? session.commandObject(command) + : session.command(command); + if (update.state.wordIndex !== index + 1) { + throw new Error("Boundary benchmark lost progress"); + } + } + if (sample >= 10) measurements.push((performance.now() - start) / 200); + } finally { + session.dispose(); + } + } + } + return { jsonMs, objectMs }; +} diff --git a/packages/typing-engine/tsconfig.json b/packages/typing-engine/tsconfig.json new file mode 100644 index 000000000..5b2c1ab1b --- /dev/null +++ b/packages/typing-engine/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "@oxytype/typescript-config/base.json", + "compilerOptions": { + "moduleResolution": "Bundler", + "module": "ESNext", + "target": "ES2022", + "lib": ["es2022", "dom"], + "types": ["vite-plus/client"] + }, + "include": ["src", "generated"] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index aa60498c1..351686784 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -415,6 +415,9 @@ importers: '@oxytype/typing-core': specifier: workspace:* version: link:../packages/typing-core + '@oxytype/typing-engine': + specifier: workspace:* + version: link:../packages/typing-engine '@oxytype/util': specifier: workspace:* version: link:../packages/util @@ -886,6 +889,17 @@ importers: specifier: 'catalog:' version: 1.0.0(@opentelemetry/api@1.9.1)(@types/node@24.19.0)(@vitest/coverage-v8@5.0.3)(@vitest/ui@5.0.3)(esbuild@0.28.1)(happy-dom@20.14.5)(jiti@2.7.0)(jsdom@30.1.1(@noble/hashes@1.8.0))(sass@1.105.1)(terser@5.49.1)(tsx@4.23.15)(typescript@7.0.2)(yaml@2.9.1) + packages/typing-engine: + devDependencies: + '@oxytype/typescript-config': + specifier: workspace:* + version: link:../typescript-config + vite-plus: + specifier: 'catalog:' + version: 1.0.0(@opentelemetry/api@1.9.1)(@types/node@24.19.0)(@vitest/coverage-v8@5.0.3)(@vitest/ui@5.0.3)(esbuild@0.28.1)(happy-dom@20.14.5)(jiti@2.7.0)(jsdom@30.1.1(@noble/hashes@1.8.0))(sass@1.105.1)(terser@5.49.1)(tsx@4.23.15)(typescript@7.0.2)(yaml@2.9.1) + + packages/typing-fixtures: {} + packages/util: devDependencies: '@oxytype/typescript-config': From b5bc5267237f9e11c292d2beed463866275c4391 Mon Sep 17 00:00:00 2001 From: Lakshmi Tanmay Date: Fri, 23 Oct 2026 00:01:42 +0530 Subject: [PATCH 07/93] ci(engine): bootstrap pinned Rust and test native browser Wasm paths --- .github/actions/setup-engine/action.yml | 16 +++++++++++ .github/actions/setup/action.yml | 2 ++ .github/workflows/oxytype-ci.yml | 35 +++++++++++++++++++++++- .github/workflows/production-release.yml | 3 ++ 4 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 .github/actions/setup-engine/action.yml diff --git a/.github/actions/setup-engine/action.yml b/.github/actions/setup-engine/action.yml new file mode 100644 index 000000000..def9a7e43 --- /dev/null +++ b/.github/actions/setup-engine/action.yml @@ -0,0 +1,16 @@ +name: Set up Rust typing engine +description: Bootstrap pinned Rust, Wasm target/tooling and ignored browser bindings. +runs: + using: composite + steps: + - name: Restore Rust build cache + uses: actions/cache@v5.0.3 + with: + path: | + ~/.cargo/registry + ~/.cargo/bin/wasm-bindgen* + target + key: engine-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('Cargo.lock', 'rust-toolchain.toml') }} + - name: Prepare pinned Rust and Wasm bindings + shell: bash + run: vp run engine:build diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml index 59f865de2..fe2d7597b 100644 --- a/.github/actions/setup/action.yml +++ b/.github/actions/setup/action.yml @@ -15,3 +15,5 @@ runs: node-version-file: .node-version cache: true cache-save: ${{ inputs.save-cache }} + - name: Prepare generated engine bindings + uses: ./.github/actions/setup-engine diff --git a/.github/workflows/oxytype-ci.yml b/.github/workflows/oxytype-ci.yml index 175d90915..f4db0d57d 100644 --- a/.github/workflows/oxytype-ci.yml +++ b/.github/workflows/oxytype-ci.yml @@ -41,6 +41,7 @@ jobs: packages: ${{ steps.select.outputs.packages }} assets: ${{ steps.select.outputs.assets }} code: ${{ steps.select.outputs.code }} + engine: ${{ steps.select.outputs.engine }} steps: # PRs read changed files from the API, so no checkout is needed. @@ -72,12 +73,22 @@ jobs: - 'packages/oxlint-config/**' # Workspace packages bundled into the backend and frontend. runtime-packages: - - 'packages/{challenges,contracts,funbox,schemas,typing-core,util}/**' + - 'packages/{challenges,contracts,funbox,schemas,typing-core,typing-engine,typing-fixtures,util}/**' + - 'crates/**' + - 'Cargo.{toml,lock}' + - 'rust-toolchain.toml' + - 'scripts/engine.ts' - '!packages/*/{__test__,__tests__,__fixtures__}/**' - '!**/*.md' packages: - 'packages/**' - '!**/*.md' + engine: + - 'crates/**' + - 'Cargo.{toml,lock}' + - 'rust-toolchain.toml' + - 'scripts/engine.ts' + - 'packages/typing-{engine,fixtures}/**' backend: - 'backend/**' - '!backend/__tests__/**' @@ -176,6 +187,27 @@ jobs: enable packages packages lint-config static-layouts static-challenges enable assets assets runtime-packages enable code code + enable engine engine runtime-packages browser-tests + + engine: + name: engine + needs: [changes] + if: needs.changes.outputs.engine == 'true' + runs-on: ubuntu-latest + timeout-minutes: 20 + steps: + - uses: actions/checkout@v7.0.1 + - uses: ./.github/actions/setup + - name: Native formatting, lint and parity + run: vp run engine:fmt && vp run engine:lint && vp run engine:test + - name: Build Cloudflare browser proof + env: + OXYTYPE_ENGINE_PROOF: "true" + run: vp run --filter @oxytype/frontend build:start + - name: Install Chromium + run: vp -C frontend exec playwright install --with-deps --only-shell chromium + - name: Browser Wasm parity + run: vp run --filter @oxytype/frontend benchmark-migration engine-proof.migration.ts quality: name: quality @@ -475,6 +507,7 @@ jobs: start-delivery, ci-assets, ci-pkg, + engine, ] runs-on: ubuntu-latest timeout-minutes: 2 diff --git a/.github/workflows/production-release.yml b/.github/workflows/production-release.yml index eb9c5916a..96ac04151 100644 --- a/.github/workflows/production-release.yml +++ b/.github/workflows/production-release.yml @@ -85,6 +85,9 @@ jobs: - name: Install dependencies run: vp install --frozen-lockfile + - name: Prepare pinned Rust and browser Wasm + uses: ./.github/actions/setup-engine + - name: Build production frontend and Worker env: OXYTYPE_BUILD_ID: ${{ env.DEPLOYMENT_SHA }}-${{ github.run_id }}-${{ github.run_attempt }} From 9013d4dab518a218aa43f6b031cf4a12fac3a09f Mon Sep 17 00:00:00 2001 From: Lakshmi Tanmay Date: Fri, 23 Oct 2026 00:02:41 +0530 Subject: [PATCH 08/93] docs(engine): record Cloudflare proof and freeze performance budgets --- docs/rust-engine/performance-budgets.json | 18 ++++++++ docs/rust-engine/performance-budgets.md | 53 +++++++++++++++++++++++ docs/rust-engine/progress.md | 13 ++++-- docs/rust-engine/proof-performance.json | 51 ++++++++++++++++++++++ 4 files changed, 132 insertions(+), 3 deletions(-) create mode 100644 docs/rust-engine/performance-budgets.json create mode 100644 docs/rust-engine/performance-budgets.md create mode 100644 docs/rust-engine/proof-performance.json diff --git a/docs/rust-engine/performance-budgets.json b/docs/rust-engine/performance-budgets.json new file mode 100644 index 000000000..2166010a6 --- /dev/null +++ b/docs/rust-engine/performance-budgets.json @@ -0,0 +1,18 @@ +{ + "sourceCommit": "b5bc52672", + "wasmRawBytes": 1048576, + "wasmGzipBytes": 307200, + "glueRawBytes": 32768, + "bindingP95Ms": 0.1, + "initializationP95Ms": 100, + "inputToPaintP95Ms": 112.5, + "restartMedianMs": 448.49853500000063, + "restartP95Ms": 501.58758399999897, + "coldMedianMs": 820.9000000059605, + "coldP95Ms": 1500.6000000089407, + "warmMedianMs": 719.7000000029802, + "warmP95Ms": 844.4000000059605, + "activeHeapBytes": 22084844, + "resetHeapBytes": 15232636, + "wasmHeapBytesAfterReset": 16777216 +} diff --git a/docs/rust-engine/performance-budgets.md b/docs/rust-engine/performance-budgets.md new file mode 100644 index 000000000..9934de3e8 --- /dev/null +++ b/docs/rust-engine/performance-budgets.md @@ -0,0 +1,53 @@ +# Performance budgets — frozen before full port + +Budgets: `performance-budgets.json`. Do not raise them to hide a failed migration. +Conditions: baseline.md; same Linux ARM64 machine, Desktop Chromium 151.0.7896.0, +no CPU/network throttle, local built Cloudflare Start/workerd. Shared host; allow +only the fixed additive margins below. CI timing is diagnostic until equivalently +controlled; structural/size/parity assertions remain mandatory everywhere. + +## Early proof measured + +- Actual browser through production Vite/Start/Cloudflare build: pass. +- Real keyboard finite test, deletion fixture, two isolated sessions, double disposal, + stale access rejection, recreation, invalid-config recovery: pass. +- Wasm 484,654 raw bytes; gzip 110,209 bytes. MIME application/wasm; immutable asset headers. +- Module/glue fetch + initialization: 22.1 ms (one cold sample; not a p95 claim). +- JSON incremental boundary: median 0.005 ms, p95 0.006 ms per UTF-16 unit. +- Incremental object boundary: median 0.008 ms, p95 0.0095 ms. 50 measured batches, + 10 discarded warmup batches, 200 UTF-16 units/batch, identical shared core. +- Seven real input-to-double-rAF samples: maximum 36.1 ms. Full website/input soak + regression is still Stage 2 work; this proof uses a minimal owned Solid component. +- Server JS bundle scan found no engine_bg, wasm_bindgen, WebAssembly, browser.client. +- CSP requires wasm-unsafe-eval. General JavaScript eval remains denied in production; + nonce/static fallbacks unchanged. Workerd document policy: six tests pass. + +## Chosen boundary and limits + +Use synchronous JSON commands + bounded deltas after async preparation; retain +object path as measurement control until Phase 1.7. JSON won this benchmark. +No full history is returned per key; explicit log reads only. +Raw Wasm <=1 MiB, gzip <=300 KiB, glue <=32 KiB. Binding p95 <=0.1 ms/unit. +Initialization p95 <=100 ms (collect repeated cold samples at final gate). +Startup cold/warm median <=baseline+75 ms, p95 <=baseline+100 ms. +Input paint p95 <=baseline+5 ms. Restart median <=baseline+25 ms, p95 <=baseline+50 ms. +2,000-character active JS heap <=baseline+8 MiB; reset JS heap <=baseline+2 MiB; +retained Wasm linear memory after reset <=16 MiB. Keep JS and Wasm memory separate. +Longer soaks must demonstrate bounded growth per recorded event and no per-restart leak. + +## Verification commands + +```sh +vp run engine:test +vp run engine:fmt +vp run engine:lint +OXYTYPE_ENGINE_PROOF=true vp run --filter @oxytype/frontend build:start +ENGINE_PROOF_OUTPUT="$PWD/docs/rust-engine/candidate-proof.json" vp run --filter @oxytype/frontend benchmark-migration engine-proof.migration.ts +vp -C frontend test run --config vitest.start.config.ts __tests__/start/document.spec.ts +``` + +Generated declarations/Wasm/glue ignored; Vite bootstrap invokes pinned Cargo and +wasm-bindgen automatically. CI and production rebuilds use the same setup action. +Pinned candidate versions proven here: Rust 1.99.1, wasm-bindgen/CLI 0.2.129. +Rust MSRV declared 1.99; only pinned 1.99.1 tested so far. + diff --git a/docs/rust-engine/progress.md b/docs/rust-engine/progress.md index 058151e82..e712bc775 100644 --- a/docs/rust-engine/progress.md +++ b/docs/rust-engine/progress.md @@ -12,7 +12,7 @@ empty commits to inflate count. Do not publish production or releases implicitly ## Current position -Stage 1, Phases 1.1–1.2 complete; Phase 1.3 in progress. Read root AGENTS.md and the complete migration +Stage 1, Phases 1.1–1.3 complete; Phase 1.4 in progress. Read root AGENTS.md and the complete migration plan before edits. Installed workspace dependencies with frozen lockfile. Phase 1.1 and 1.2 gates passed: inventory/baselines, lossless contract, frozen oracle and mismatch detection. Existing website engine remains authoritative. @@ -20,8 +20,8 @@ Phase 1.1 and 1.2 gates passed: inventory/baselines, lossless contract, frozen o | --- | --- | --- | | 1.1 Inventory / baselines | Complete | See inventory.md, features.md, baseline.md and raw measurements. | | 1.2 Contracts / fixtures | Complete | 13 command traces, 15 oracle/integrity tests; contract and classified gaps. | -| 1.3 Native / browser proof | Active | Implement finite slice; native/browser/Cloudflare parity and measured budgets pending. | -| 1.4 Input / lifecycle / timing | Pending | Requires production browser proof. | +| 1.3 Native / browser proof | Complete | Native 7 traces; Cloudflare real-browser finite slice; MIME/CSP, lifecycle, boundary benchmark. Budgets frozen. | +| 1.4 Input / lifecycle / timing | Active | Remaining input penalties, code, physical observations, browser differences and timing. | | 1.5 Generation / configuration | Pending | Requires contract and proof. | | 1.6 Events / results / replay | Pending | Requires input and generation. | | 1.7 Binding / engine gate | Pending | Full three-path parity and budgets. | @@ -39,6 +39,13 @@ Phase 1.1 and 1.2 gates passed: inventory/baselines, lossless contract, frozen o - Phase 1.2 type-aware lint: pass. Capture created new traces once, refuses overwrite. - `vp exec rustup target add wasm32-unknown-unknown`: pass. +- Phase 1.3: `vp run engine:test`: three native tests pass (seven frozen traces). +- Phase 1.3: `vp run engine:lint` and `vp run engine:fmt`: pass. +- Phase 1.3: opted-in Cloudflare production build and browser proof: pass. +- Phase 1.3: workerd document suite: six pass; production CSP now permits Wasm compilation specifically. +- Phase 1.3: boundary benchmark/type-aware lint passed; proof-performance.json and performance-budgets.md record limits and evidence. +- Early failed browser proof correctly exposed CSP; fixed and retested. Initial build raced workspace dependency installation; reran after install completed. + ## Decisions - User's three-stage PR structure overrides the plan's suggested per-phase PRs. diff --git a/docs/rust-engine/proof-performance.json b/docs/rust-engine/proof-performance.json new file mode 100644 index 000000000..075bd7507 --- /dev/null +++ b/docs/rust-engine/proof-performance.json @@ -0,0 +1,51 @@ +{ + "fixture": "built Cloudflare Start; Desktop Chrome; 50 warmed samples per boundary; 200 UTF-16 units per sample; 7 real keyboard commits with double-rAF paint", + "browser": "153.0.8010.12", + "wasmBytes": 484654, + "wasmHeaders": { + "transfer-encoding": "chunked", + "cache-control": "public, max-age=31536000, immutable", + "content-encoding": "gzip", + "cf-cache-status": "HIT", + "etag": "\"8f627cfb164e1e2bdd63d31ce0abc8f8\"", + "content-type": "application/wasm", + "x-content-type-options": "nosniff" + }, + "inputToPaint": [ + 36.099999994039536, 32.3999999910593, 32.79999999701977, 22.69999998807907, + 29.299999997019768, 25.100000008940697, 24.400000005960464 + ], + "benchmark": { + "initializationMs": 22.099999994039536, + "jsonMs": [ + 0.005499999970197678, 0.006000000014901161, 0.005999999940395355, + 0.006000000014901161, 0.005, 0.005999999940395355, 0.005, 0.005, + 0.005499999970197678, 0.005999999940395355, 0.005499999970197678, + 0.005500000044703484, 0.005, 0.005499999970197678, 0.005, 0.005, + 0.0044999999552965166, 0.005499999970197678, 0.005, 0.0044999999552965166, + 0.005, 0.005, 0.005, 0.0044999999552965166, 0.005, 0.005, 0.005, + 0.004500000029802323, 0.003999999985098839, 0.005, 0.004500000029802323, + 0.005, 0.005, 0.003999999985098839, 0.005, 0.0044999999552965166, + 0.0044999999552965166, 0.0044999999552965166, 0.0044999999552965166, + 0.0044999999552965166, 0.0044999999552965166, 0.004500000029802323, + 0.004500000029802323, 0.004500000029802323, 0.005, 0.004000000059604645, + 0.005499999970197678, 0.004500000029802323, 0.005, 0.0044999999552965166 + ], + "objectMs": [ + 0.009499999955296516, 0.009499999955296516, 0.009000000059604645, + 0.009500000029802322, 0.0125, 0.00850000001490116, 0.009499999955296516, + 0.00899999998509884, 0.00850000001490116, 0.007999999970197678, + 0.00850000001490116, 0.008499999940395355, 0.00850000001490116, + 0.00850000001490116, 0.007999999970197678, 0.00850000001490116, + 0.007999999970197678, 0.00850000001490116, 0.008499999940395355, + 0.007999999970197678, 0.0075, 0.008000000044703484, 0.0075, + 0.007999999970197678, 0.007999999970197678, 0.0075, 0.0075, 0.0075, + 0.008000000044703484, 0.0075, 0.0075, 0.0075, 0.0075, + 0.008000000044703484, 0.0075, 0.006999999955296516, 0.0075, 0.0075, + 0.006999999955296516, 0.006999999955296516, 0.0075, 0.007999999970197678, + 0.007000000029802322, 0.0075, 0.0075, 0.0075, 0.0075, 0.0075, 0.0075, + 0.008000000044703484 + ], + "memoryBytes": 1310720 + } +} From d27c21c54a37a86f5a733b4c47a99477b1a9a50e Mon Sep 17 00:00:00 2001 From: Lakshmi Tanmay Date: Fri, 23 Oct 2026 00:04:24 +0530 Subject: [PATCH 09/93] feat(engine): port error penalties difficulty and code indentation rules --- crates/typing-engine/src/session.rs | 153 ++++++++++++++++++++++-- crates/typing-engine/tests/slice.rs | 6 + docs/rust-engine/performance-budgets.md | 2 +- 3 files changed, 147 insertions(+), 14 deletions(-) diff --git a/crates/typing-engine/src/session.rs b/crates/typing-engine/src/session.rs index 7dc2ac9c6..ac84dd000 100644 --- a/crates/typing-engine/src/session.rs +++ b/crates/typing-engine/src/session.rs @@ -152,6 +152,7 @@ pub struct Session { start: Option, ticks: u32, events: Vec, + indent_pending: bool, } impl Session { @@ -173,6 +174,7 @@ impl Session { start: None, ticks: 0, events: vec![], + indent_pending: false, }) } @@ -283,6 +285,7 @@ impl Session { self.reason = None; self.start = None; self.ticks = 0; + self.indent_pending = false; self.events.clear(); self.input = vec![Text::default(); self.words.len().max(1)]; } @@ -312,6 +315,22 @@ impl Session { shift: Option, automatic: bool, composition: bool, + ) { + self.insert_one(unit, now, date, shift, automatic, composition); + while self.indent_pending && !self.ended { + self.indent_pending = false; + self.insert_one(9, now, date, None, true, false); + } + } + + fn insert_one( + &mut self, + unit: u16, + now: f64, + date: f64, + shift: Option, + automatic: bool, + composition: bool, ) { let input = self.current(); let target = self.target(); @@ -330,6 +349,24 @@ impl Session { target.0.get(input.len()).copied(), &self.config.language, ); + let replacement = if unit == 0x2026 && target.0.get(input.len()) != Some(&unit) { + Some("...") + } else if unit == 0x0133 + && self.config.language.starts_with("dutch") + && target.0.get(input.len()) != Some(&unit) + { + Some("ij") + } else { + None + }; + if let Some(replacement) = replacement { + for unit in replacement.encode_utf16() { + if !self.ended { + self.insert(unit, now, date, shift, automatic, composition); + } + } + return; + } let commit = is_space(unit) || unit == 10 || (nospace && input.len() + 1 == target.len()); if is_space(unit) && input.is_empty() @@ -383,7 +420,19 @@ impl Session { .resize(self.input.len().max(self.index + 1), Text::default()); self.input[self.index] = next; self.record("input", now, data); - // Full input/lifecycle port adds difficulty/deletion penalties after browser proof. + if !correct && !stopped && self.config.delete_on_error != "off" { + self.delete_error(now); + } + if self.config.mode != "zen" + && ((self.config.difficulty == "master" && !correct) + || (self.config.difficulty == "expert" + && commit + && !(input.is_empty() && (is_space(unit) || unit == 10)) + && !word_correct)) + { + self.end(now, date, Some("difficulty".into())); + return; + } let finite = self.config.mode != "time" && self.config.mode != "zen" && !(self.config.mode == "words" && self.config.words == 0); @@ -396,6 +445,67 @@ impl Session { } else if advance && (self.index + 1 < self.words.len() || self.config.mode == "zen") { self.index += 1; } + if !self.ended + && correct + && self.config.language.starts_with("code") + && self.target().0.first() == Some(&9) + && self.target().0.get(self.current().len()) == Some(&9) + { + self.indent_pending = true; + } + } + + fn log_delete(&mut self, kind: &str, now: f64, char_index: usize, next: Text, automatic: bool) { + let mut data = json!({"inputType":kind,"wordIndex":self.index,"charIndex":char_index,"inputValue":next}); + if automatic { + data["automatic"] = json!(true); + } + self.record("input", now, data); + self.input + .resize(self.input.len().max(self.index + 1), Text::default()); + self.input[self.index] = next; + } + + fn previous_input(&self, kind: &str) -> Text { + let mut input = self.input.get(self.index).cloned().unwrap_or_default(); + if kind == "deleteWordBackward" { + input.0.clear(); + } else if self.nospace() || matches!(input.0.last(), Some(32 | 10)) { + input.0.pop(); + } + input + } + + fn delete_error(&mut self, now: f64) { + let input = self.current(); + let whole = self.config.delete_on_error.starts_with("word"); + let kind = if whole { + "deleteWordBackward" + } else { + "deleteContentBackward" + }; + if !input.is_empty() { + let next = if whole { + Text::default() + } else { + Text(input.0[..input.len() - 1].to_vec()) + }; + self.log_delete(kind, now, input.len(), next, true); + if !whole && input.len() > 1 { + self.log_delete( + kind, + now, + input.len() - 1, + Text(input.0[..input.len() - 2].to_vec()), + true, + ); + } + } + if self.config.delete_on_error.contains("hard") && input.len() <= 1 && self.index > 0 { + self.index -= 1; + let previous = self.previous_input(kind); + self.log_delete(kind, now, previous.len(), previous, true); + } } fn delete(&mut self, kind: &str, now: f64) { @@ -419,15 +529,23 @@ impl Session { { return; } - let next = if input.is_empty() { - self.index -= 1; - let mut previous = prev; - if kind == "deleteWordBackward" { - previous.0.clear(); - } else if self.nospace() || matches!(previous.0.last(), Some(32 | 10)) { - previous.0.pop(); - } - previous + let unindent = self.config.language.starts_with("code") + && self.config.code_unindent_on_backspace + && !input.is_empty() + && input.0.iter().all(|c| *c == 9) + && self.target().0.starts_with(&input.0); + if unindent { + self.log_delete( + "deleteWordBackward", + now, + input.len(), + Text::default(), + false, + ); + } + let next = if input.is_empty() || unindent { + self.index = self.index.saturating_sub(1); + self.previous_input(kind) } else if kind == "deleteWordBackward" { delete_word(&input) } else { @@ -435,13 +553,22 @@ impl Session { next.0.pop(); next }; - let char_index = if input.is_empty() { + let char_index = if input.is_empty() || unindent { next.len() } else { input.len() }; - self.record("input",now,json!({"inputType":kind,"wordIndex":self.index,"charIndex":char_index,"inputValue":next})); - self.input[self.index] = next; + self.log_delete( + if unindent { + "deleteContentBackward" + } else { + kind + }, + now, + char_index, + next, + false, + ); } fn advance(&mut self, now: f64, date: f64) { diff --git a/crates/typing-engine/tests/slice.rs b/crates/typing-engine/tests/slice.rs index ac586c88f..a08a26cc3 100644 --- a/crates/typing-engine/tests/slice.rs +++ b/crates/typing-engine/tests/slice.rs @@ -37,6 +37,12 @@ fn finite_slice_matches_frozen_intermediate_oracle() { "timer-stall", "stop-letter", "infinite-words", + "code-indent", + "master-finish-once", + "delete-error-letter", + "delete-error-letter_hard", + "delete-error-word", + "delete-error-word_hard", ] { let fixture: Fixture = serde_json::from_slice( &std::fs::read(root.join(format!("commands/{name}.json"))).unwrap(), diff --git a/docs/rust-engine/performance-budgets.md b/docs/rust-engine/performance-budgets.md index 9934de3e8..4750369b3 100644 --- a/docs/rust-engine/performance-budgets.md +++ b/docs/rust-engine/performance-budgets.md @@ -1,7 +1,7 @@ # Performance budgets — frozen before full port Budgets: `performance-budgets.json`. Do not raise them to hide a failed migration. -Conditions: baseline.md; same Linux ARM64 machine, Desktop Chromium 151.0.7896.0, +Conditions: baseline.md; same Linux ARM64 machine, Desktop Chromium 153.0.8010.12, no CPU/network throttle, local built Cloudflare Start/workerd. Shared host; allow only the fixed additive margins below. CI timing is diagnostic until equivalently controlled; structural/size/parity assertions remain mandatory everywhere. From 780749787c725a9dbe9adf7636fb48532896cae9 Mon Sep 17 00:00:00 2001 From: Lakshmi Tanmay Date: Fri, 23 Oct 2026 00:06:39 +0530 Subject: [PATCH 10/93] feat(engine): record physical keys and preserve legacy cleanup ordering --- crates/typing-engine/src/session.rs | 187 ++- crates/typing-engine/tests/recordings.rs | 129 ++ crates/typing-engine/tests/slice.rs | 1 + .../scripts/capture-rust-oracle.ts | 8 +- packages/typing-core/scripts/rust-oracle.ts | 7 +- packages/typing-fixtures/commands.schema.json | 1 + .../commands/physical-observations.json | 113 ++ .../oracle/physical-observations.json | 1139 +++++++++++++++++ 8 files changed, 1577 insertions(+), 8 deletions(-) create mode 100644 crates/typing-engine/tests/recordings.rs create mode 100644 packages/typing-fixtures/commands/physical-observations.json create mode 100644 packages/typing-fixtures/oracle/physical-observations.json diff --git a/crates/typing-engine/src/session.rs b/crates/typing-engine/src/session.rs index ac84dd000..2a82eaf9d 100644 --- a/crates/typing-engine/src/session.rs +++ b/crates/typing-engine/src/session.rs @@ -4,6 +4,7 @@ use crate::{ }; use serde::{Deserialize, Serialize}; use serde_json::{Value, json}; +use std::collections::BTreeMap; use ts_rs::TS; #[derive(Clone, Debug, Deserialize, Serialize, TS)] @@ -105,6 +106,9 @@ pub enum Command { event_type: String, event_data: Value, }, + Finalize { + now: f64, + }, } #[derive(Clone, Debug, Serialize, Deserialize, TS)] @@ -153,6 +157,8 @@ pub struct Session { ticks: u32, events: Vec, indent_pending: bool, + pressed: Vec<(String, f64)>, + no_code_index: usize, } impl Session { @@ -175,6 +181,8 @@ impl Session { ticks: 0, events: vec![], indent_pending: false, + pressed: vec![], + no_code_index: 0, }) } @@ -237,7 +245,8 @@ impl Session { | Command::End { now, .. } | Command::Abandon { now, .. } | Command::Reset { now } - | Command::Observe { now, .. } => *now, + | Command::Observe { now, .. } + | Command::Finalize { now } => *now, }; if !now.is_finite() || now.abs() > 9_007_199_254_740_991.0 { return Err("Invalid timestamp".into()); @@ -288,10 +297,15 @@ impl Session { self.indent_pending = false; self.events.clear(); self.input = vec![Text::default(); self.words.len().max(1)]; + self.pressed.clear(); + self.no_code_index = 0; } - Command::Observe { .. } => { - return Err("Observations not yet implemented in proof slice".into()); - } + Command::Observe { + now, + event_type, + event_data, + } => self.observe(&event_type, now, event_data)?, + Command::Finalize { .. } => self.finalize(), } let origin = self.origin(); let events = self @@ -608,6 +622,171 @@ impl Session { _ => 4, } } + + fn observe(&mut self, kind: &str, now: f64, mut data: Value) -> Result<(), String> { + if !data.is_object() { + return Err("Observation data must be an object".into()); + } + if kind == "keydown" || kind == "keyup" { + let code = data["code"].as_str().ok_or("Missing key code")?.to_owned(); + let tracked = (code.len() == 4 + && code.starts_with("Key") + && code.as_bytes()[3].is_ascii_uppercase()) + || (code.len() == 6 + && code.starts_with("Digit") + && code.as_bytes()[5].is_ascii_digit()) + || [ + "NumpadMultiply", + "NumpadSubtract", + "NumpadAdd", + "NumpadDecimal", + "NumpadEqual", + "NumpadDivide", + "Numpad0", + "Numpad1", + "Numpad2", + "Numpad3", + "Numpad4", + "Numpad5", + "Numpad6", + "Numpad7", + "Numpad8", + "Numpad9", + "Backquote", + "Minus", + "Equal", + "BracketLeft", + "BracketRight", + "Backslash", + "Semicolon", + "Quote", + "IntlBackslash", + "Comma", + "Period", + "Slash", + "Space", + "Enter", + "Tab", + "NoCode", + ] + .contains(&code.as_str()); + let indexed_no_code = code.strip_prefix("NoCode").is_some_and(|suffix| { + !suffix.is_empty() && suffix.bytes().all(|c| c.is_ascii_digit()) + }); + if !tracked && !(kind == "keyup" && indexed_no_code) { + return Ok(()); + } + let mut key = code.clone(); + if kind == "keydown" { + if let Some(index) = self.pressed.iter().position(|(key, _)| key == &code) { + self.pressed.remove(index); + self.record("keyup", now, data.clone()); + } + if code == "NoCode" { + key = format!("NoCode{}", self.no_code_index); + self.no_code_index += 1; + } + self.pressed.push((key.clone(), round2(now))); + } else { + if code == "NoCode" { + if self.no_code_index == 0 { + return Ok(()); + } + key = format!("NoCode{}", self.no_code_index - 1); + if !self.pressed.iter().any(|(code, _)| code == &key) { + return Ok(()); + } + self.no_code_index -= 1; + } + let Some(index) = self.pressed.iter().position(|(code, _)| code == &key) else { + return Ok(()); + }; + self.pressed.remove(index); + } + data["code"] = json!(key); + self.record(kind, now, data); + } else if kind == "composition" || kind == "timer" { + self.record(kind, now, data); + } else { + return Err("Unsupported observation type".into()); + } + Ok(()) + } + + fn finalize(&mut self) { + let mut down = BTreeMap::new(); + for event in &self.events { + if event.kind == "keydown" { + down.insert( + event.data["code"].as_str().unwrap_or_default().to_owned(), + event.now, + ); + } + } + let mut durations = vec![]; + for event in &self.events { + if event.kind != "keyup" { + continue; + } + if let Some(start) = down.remove(event.data["code"].as_str().unwrap_or_default()) { + let duration = event.now - start; + if duration > 0.0 { + durations.push(duration); + } + } + } + let average = if durations.is_empty() { + 80.0 + } else { + round2(durations.iter().sum::() / durations.len() as f64) + }; + for (key, now) in self.pressed.clone() { + let _ = self.observe("keyup", now + average, json!({"code":key,"estimated":true})); + } + let start = self + .events + .iter() + .find(|e| e.kind == "timer" && e.data["event"] == "start") + .map(|e| e.now); + let end = self + .events + .iter() + .find(|e| e.kind == "timer" && e.data["event"] == "end") + .map(|e| e.now); + let last_pre = self + .events + .iter() + .enumerate() + .filter(|(_, e)| e.kind == "keydown" && start.is_some_and(|start| e.now < start)) + .map(|(i, _)| i) + .next_back(); + let post_codes: Vec<_> = self + .events + .iter() + .filter(|e| e.kind == "keydown" && end.is_some_and(|end| e.now > end)) + .map(|e| e.data["code"].clone()) + .collect(); + let mut index = 0; + self.events.retain(|e| { + let current = index; + index += 1; + if let Some(start) = start + && e.now < start + && (e.kind == "keyup" || (e.kind == "keydown" && Some(current) != last_pre)) + { + return false; + } + if let Some(end) = end + && e.now > end + && (e.kind == "input" + || e.kind == "keydown" + || (e.kind == "keyup" && post_codes.contains(&e.data["code"]))) + { + return false; + } + true + }); + } fn origin(&self) -> f64 { self.events .iter() diff --git a/crates/typing-engine/tests/recordings.rs b/crates/typing-engine/tests/recordings.rs new file mode 100644 index 000000000..5d83761cc --- /dev/null +++ b/crates/typing-engine/tests/recordings.rs @@ -0,0 +1,129 @@ +use serde_json::{Value, json}; +use typing_engine::{Command, Session, Text}; + +fn input_neutral(mut event: Value) -> Value { + for key in ["inputValue", "data"] { + if let Some(text) = event["data"][key].as_str() { + event["data"][key] = json!(Text::from(text)); + } + } + event +} + +#[test] +fn all_original_browser_recordings_match_input_events() { + let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR")) + .join("../../packages/typing-fixtures/reference/keystrokes"); + for file in std::fs::read_dir(root).unwrap() { + let path = file.unwrap().path(); + if path.extension().is_none_or(|ext| ext != "json") { + continue; + } + let fixture: Value = serde_json::from_slice(&std::fs::read(&path).unwrap()).unwrap(); + let words = fixture["eventLog"]["context"]["targetWords"] + .as_array() + .unwrap() + .iter() + .map(|word| Text::from(word.as_str().unwrap())) + .collect(); + let mut session = Session::new( + serde_json::from_value(fixture["config"].clone()).unwrap(), + words, + ) + .unwrap(); + for event in fixture["eventLog"]["events"].as_array().unwrap() { + let now = event["testMs"].as_f64().unwrap(); + let data = &event["data"]; + match event["type"].as_str().unwrap() { + "input" => { + let command = if data["inputType"] == "insertText" { + json!({"op":"insert","data":Text::from(data["data"].as_str().unwrap()),"now":now, + "automatic":data.get("automatic"),"isCompositionEnding":data.get("isCompositionEnding")}) + } else { + json!({"op":"delete","kind":data["inputType"],"now":now}) + }; + session + .apply(serde_json::from_value(command).unwrap()) + .unwrap(); + } + "timer" if data["event"] == "start" => { + session.apply(Command::Start { now, date: 0.0 }).unwrap(); + } + "timer" if data["event"] == "end" => { + session + .apply(Command::Finish { + now, + date: 0.0, + reason: None, + }) + .unwrap(); + } + kind => { + session + .apply(Command::Observe { + now, + event_type: kind.into(), + event_data: data.clone(), + }) + .unwrap(); + } + } + } + let actual = session.log()["events"] + .as_array() + .unwrap() + .iter() + .filter(|event| event["type"] == "input") + .cloned() + .collect::>(); + let expected = fixture["eventLog"]["events"] + .as_array() + .unwrap() + .iter() + .filter(|event| event["type"] == "input") + .cloned() + .map(input_neutral) + .collect::>(); + assert_eq!(actual.len(), expected.len(), "{}", path.display()); + for (index, (actual, expected)) in actual.iter().zip(&expected).enumerate() { + assert_eq!( + actual["type"], + expected["type"], + "{} event {index}", + path.display() + ); + assert_eq!( + actual["testMs"].as_f64(), + expected["testMs"].as_f64(), + "{} event {index}", + path.display() + ); + assert_eq!( + actual["data"], + expected["data"], + "{} event {index}", + path.display() + ); + } + } +} + +#[test] +fn absent_physical_observations_do_not_create_key_durations() { + let mut session = Session::new( + serde_json::from_value(json!({"mode":"words","words":1})).unwrap(), + vec![Text::from("a")], + ) + .unwrap(); + session + .apply(serde_json::from_value(json!({"op":"insert","data":[97],"now":0})).unwrap()) + .unwrap(); + session.apply(Command::Finalize { now: 10.0 }).unwrap(); + assert!( + session.log()["events"] + .as_array() + .unwrap() + .iter() + .all(|event| event["type"] != "keyup" && event["type"] != "keydown") + ); +} diff --git a/crates/typing-engine/tests/slice.rs b/crates/typing-engine/tests/slice.rs index a08a26cc3..350b0f25b 100644 --- a/crates/typing-engine/tests/slice.rs +++ b/crates/typing-engine/tests/slice.rs @@ -43,6 +43,7 @@ fn finite_slice_matches_frozen_intermediate_oracle() { "delete-error-letter_hard", "delete-error-word", "delete-error-word_hard", + "physical-observations", ] { let fixture: Fixture = serde_json::from_slice( &std::fs::read(root.join(format!("commands/{name}.json"))).unwrap(), diff --git a/packages/typing-core/scripts/capture-rust-oracle.ts b/packages/typing-core/scripts/capture-rust-oracle.ts index 67a62d533..169ea2d8c 100644 --- a/packages/typing-core/scripts/capture-rust-oracle.ts +++ b/packages/typing-core/scripts/capture-rust-oracle.ts @@ -4,9 +4,11 @@ import { readCommandFixture, runOracle } from "./rust-oracle"; const root = resolve(import.meta.dirname, "../../typing-fixtures"); // Capture only NEW traces. Deliberately no overwrite/update flag. -for (const name of readdirSync(resolve(root, "commands")).filter((entry) => - entry.endsWith(".json"), -)) { +const requested = process.argv.slice(2); +for (const name of (requested.length > 0 + ? requested + : readdirSync(resolve(root, "commands")) +).filter((entry) => entry.endsWith(".json"))) { const fixture = readCommandFixture(resolve(root, "commands", name)); const output = await runOracle(fixture); writeFileSync( diff --git a/packages/typing-core/scripts/rust-oracle.ts b/packages/typing-core/scripts/rust-oracle.ts index 1d3b330f4..23c6b6ddc 100644 --- a/packages/typing-core/scripts/rust-oracle.ts +++ b/packages/typing-core/scripts/rust-oracle.ts @@ -19,7 +19,8 @@ export type OracleCommand = { | "end" | "abandon" | "reset" - | "observe"; + | "observe" + | "finalize"; now: number; date?: number; data?: Text; @@ -112,6 +113,10 @@ export async function runOracle(fixture: CommandFixture): Promise { case "reset": session.reset(); break; + case "finalize": + session.recorder.forceReleaseAllKeys(); + session.recorder.cleanupData(); + break; case "observe": if ( command.eventType === undefined || diff --git a/packages/typing-fixtures/commands.schema.json b/packages/typing-fixtures/commands.schema.json index b8adf517e..b0939974c 100644 --- a/packages/typing-fixtures/commands.schema.json +++ b/packages/typing-fixtures/commands.schema.json @@ -35,6 +35,7 @@ "end", "abandon", "reset", + "finalize", "observe" ] }, diff --git a/packages/typing-fixtures/commands/physical-observations.json b/packages/typing-fixtures/commands/physical-observations.json new file mode 100644 index 000000000..c06bbbef3 --- /dev/null +++ b/packages/typing-fixtures/commands/physical-observations.json @@ -0,0 +1,113 @@ +{ + "name": "physical-observations", + "config": { + "mode": "words", + "words": 1 + }, + "words": [[97, 98, 99]], + "commands": [ + { + "op": "observe", + "now": 900, + "eventType": "keydown", + "eventData": { + "code": "KeyX" + } + }, + { + "op": "observe", + "now": 910, + "eventType": "keyup", + "eventData": { + "code": "KeyX" + } + }, + { + "op": "observe", + "now": 950, + "eventType": "keydown", + "eventData": { + "code": "KeyA" + } + }, + { + "op": "start", + "now": 1000, + "date": 5000 + }, + { + "op": "observe", + "now": 1020, + "eventType": "keyup", + "eventData": { + "code": "KeyA" + } + }, + { + "op": "observe", + "now": 1100, + "eventType": "keydown", + "eventData": { + "code": "KeyB" + } + }, + { + "op": "observe", + "now": 1120, + "eventType": "keydown", + "eventData": { + "code": "KeyB" + } + }, + { + "op": "observe", + "now": 1180, + "eventType": "keyup", + "eventData": { + "code": "KeyB" + } + }, + { + "op": "observe", + "now": 1200, + "eventType": "keydown", + "eventData": { + "code": "ShiftLeft" + } + }, + { + "op": "observe", + "now": 1250, + "eventType": "keydown", + "eventData": { + "code": "NoCode" + } + }, + { + "op": "observe", + "now": 1260, + "eventType": "keydown", + "eventData": { + "code": "NoCode" + } + }, + { + "op": "observe", + "now": 1270, + "eventType": "keyup", + "eventData": { + "code": "NoCode" + } + }, + { + "op": "insert", + "data": [97, 98, 99], + "now": 1300, + "date": 5300 + }, + { + "op": "finalize", + "now": 1400 + } + ] +} diff --git a/packages/typing-fixtures/oracle/physical-observations.json b/packages/typing-fixtures/oracle/physical-observations.json new file mode 100644 index 000000000..d49f79c78 --- /dev/null +++ b/packages/typing-fixtures/oracle/physical-observations.json @@ -0,0 +1,1139 @@ +{ + "name": "physical-observations", + "frames": [ + { + "state": { + "wordIndex": 0, + "inputValue": [], + "active": false, + "ended": false + }, + "log": { + "version": 1, + "events": [ + { + "type": "keydown", + "testMs": 0, + "data": { + "code": "KeyX" + } + } + ], + "context": { + "targetWords": [[97, 98, 99]], + "mode": "words", + "mode2": "1", + "koreanStatus": false, + "bailedOut": false, + "isFunboxWithNospacePropertyActive": false + } + } + }, + { + "state": { + "wordIndex": 0, + "inputValue": [], + "active": false, + "ended": false + }, + "log": { + "version": 1, + "events": [ + { + "type": "keydown", + "testMs": 0, + "data": { + "code": "KeyX" + } + }, + { + "type": "keyup", + "testMs": 10, + "data": { + "code": "KeyX" + } + } + ], + "context": { + "targetWords": [[97, 98, 99]], + "mode": "words", + "mode2": "1", + "koreanStatus": false, + "bailedOut": false, + "isFunboxWithNospacePropertyActive": false + } + } + }, + { + "state": { + "wordIndex": 0, + "inputValue": [], + "active": false, + "ended": false + }, + "log": { + "version": 1, + "events": [ + { + "type": "keydown", + "testMs": 0, + "data": { + "code": "KeyX" + } + }, + { + "type": "keyup", + "testMs": 10, + "data": { + "code": "KeyX" + } + }, + { + "type": "keydown", + "testMs": 50, + "data": { + "code": "KeyA" + } + } + ], + "context": { + "targetWords": [[97, 98, 99]], + "mode": "words", + "mode2": "1", + "koreanStatus": false, + "bailedOut": false, + "isFunboxWithNospacePropertyActive": false + } + } + }, + { + "state": { + "wordIndex": 0, + "inputValue": [], + "active": true, + "ended": false + }, + "log": { + "version": 1, + "events": [ + { + "type": "keydown", + "testMs": -100, + "data": { + "code": "KeyX" + } + }, + { + "type": "keyup", + "testMs": -90, + "data": { + "code": "KeyX" + } + }, + { + "type": "keydown", + "testMs": -50, + "data": { + "code": "KeyA" + } + }, + { + "type": "timer", + "testMs": 0, + "data": { + "event": "start", + "timer": 0, + "date": 5000 + } + } + ], + "context": { + "targetWords": [[97, 98, 99]], + "mode": "words", + "mode2": "1", + "koreanStatus": false, + "bailedOut": false, + "isFunboxWithNospacePropertyActive": false + } + } + }, + { + "state": { + "wordIndex": 0, + "inputValue": [], + "active": true, + "ended": false + }, + "log": { + "version": 1, + "events": [ + { + "type": "keydown", + "testMs": -100, + "data": { + "code": "KeyX" + } + }, + { + "type": "keyup", + "testMs": -90, + "data": { + "code": "KeyX" + } + }, + { + "type": "keydown", + "testMs": -50, + "data": { + "code": "KeyA" + } + }, + { + "type": "timer", + "testMs": 0, + "data": { + "event": "start", + "timer": 0, + "date": 5000 + } + }, + { + "type": "keyup", + "testMs": 20, + "data": { + "code": "KeyA" + } + } + ], + "context": { + "targetWords": [[97, 98, 99]], + "mode": "words", + "mode2": "1", + "koreanStatus": false, + "bailedOut": false, + "isFunboxWithNospacePropertyActive": false + } + } + }, + { + "state": { + "wordIndex": 0, + "inputValue": [], + "active": true, + "ended": false + }, + "log": { + "version": 1, + "events": [ + { + "type": "keydown", + "testMs": -100, + "data": { + "code": "KeyX" + } + }, + { + "type": "keyup", + "testMs": -90, + "data": { + "code": "KeyX" + } + }, + { + "type": "keydown", + "testMs": -50, + "data": { + "code": "KeyA" + } + }, + { + "type": "timer", + "testMs": 0, + "data": { + "event": "start", + "timer": 0, + "date": 5000 + } + }, + { + "type": "keyup", + "testMs": 20, + "data": { + "code": "KeyA" + } + }, + { + "type": "keydown", + "testMs": 100, + "data": { + "code": "KeyB" + } + } + ], + "context": { + "targetWords": [[97, 98, 99]], + "mode": "words", + "mode2": "1", + "koreanStatus": false, + "bailedOut": false, + "isFunboxWithNospacePropertyActive": false + } + } + }, + { + "state": { + "wordIndex": 0, + "inputValue": [], + "active": true, + "ended": false + }, + "log": { + "version": 1, + "events": [ + { + "type": "keydown", + "testMs": -100, + "data": { + "code": "KeyX" + } + }, + { + "type": "keyup", + "testMs": -90, + "data": { + "code": "KeyX" + } + }, + { + "type": "keydown", + "testMs": -50, + "data": { + "code": "KeyA" + } + }, + { + "type": "timer", + "testMs": 0, + "data": { + "event": "start", + "timer": 0, + "date": 5000 + } + }, + { + "type": "keyup", + "testMs": 20, + "data": { + "code": "KeyA" + } + }, + { + "type": "keydown", + "testMs": 100, + "data": { + "code": "KeyB" + } + }, + { + "type": "keyup", + "testMs": 120, + "data": { + "code": "KeyB" + } + }, + { + "type": "keydown", + "testMs": 120, + "data": { + "code": "KeyB" + } + } + ], + "context": { + "targetWords": [[97, 98, 99]], + "mode": "words", + "mode2": "1", + "koreanStatus": false, + "bailedOut": false, + "isFunboxWithNospacePropertyActive": false + } + } + }, + { + "state": { + "wordIndex": 0, + "inputValue": [], + "active": true, + "ended": false + }, + "log": { + "version": 1, + "events": [ + { + "type": "keydown", + "testMs": -100, + "data": { + "code": "KeyX" + } + }, + { + "type": "keyup", + "testMs": -90, + "data": { + "code": "KeyX" + } + }, + { + "type": "keydown", + "testMs": -50, + "data": { + "code": "KeyA" + } + }, + { + "type": "timer", + "testMs": 0, + "data": { + "event": "start", + "timer": 0, + "date": 5000 + } + }, + { + "type": "keyup", + "testMs": 20, + "data": { + "code": "KeyA" + } + }, + { + "type": "keydown", + "testMs": 100, + "data": { + "code": "KeyB" + } + }, + { + "type": "keyup", + "testMs": 120, + "data": { + "code": "KeyB" + } + }, + { + "type": "keydown", + "testMs": 120, + "data": { + "code": "KeyB" + } + }, + { + "type": "keyup", + "testMs": 180, + "data": { + "code": "KeyB" + } + } + ], + "context": { + "targetWords": [[97, 98, 99]], + "mode": "words", + "mode2": "1", + "koreanStatus": false, + "bailedOut": false, + "isFunboxWithNospacePropertyActive": false + } + } + }, + { + "state": { + "wordIndex": 0, + "inputValue": [], + "active": true, + "ended": false + }, + "log": { + "version": 1, + "events": [ + { + "type": "keydown", + "testMs": -100, + "data": { + "code": "KeyX" + } + }, + { + "type": "keyup", + "testMs": -90, + "data": { + "code": "KeyX" + } + }, + { + "type": "keydown", + "testMs": -50, + "data": { + "code": "KeyA" + } + }, + { + "type": "timer", + "testMs": 0, + "data": { + "event": "start", + "timer": 0, + "date": 5000 + } + }, + { + "type": "keyup", + "testMs": 20, + "data": { + "code": "KeyA" + } + }, + { + "type": "keydown", + "testMs": 100, + "data": { + "code": "KeyB" + } + }, + { + "type": "keyup", + "testMs": 120, + "data": { + "code": "KeyB" + } + }, + { + "type": "keydown", + "testMs": 120, + "data": { + "code": "KeyB" + } + }, + { + "type": "keyup", + "testMs": 180, + "data": { + "code": "KeyB" + } + } + ], + "context": { + "targetWords": [[97, 98, 99]], + "mode": "words", + "mode2": "1", + "koreanStatus": false, + "bailedOut": false, + "isFunboxWithNospacePropertyActive": false + } + } + }, + { + "state": { + "wordIndex": 0, + "inputValue": [], + "active": true, + "ended": false + }, + "log": { + "version": 1, + "events": [ + { + "type": "keydown", + "testMs": -100, + "data": { + "code": "KeyX" + } + }, + { + "type": "keyup", + "testMs": -90, + "data": { + "code": "KeyX" + } + }, + { + "type": "keydown", + "testMs": -50, + "data": { + "code": "KeyA" + } + }, + { + "type": "timer", + "testMs": 0, + "data": { + "event": "start", + "timer": 0, + "date": 5000 + } + }, + { + "type": "keyup", + "testMs": 20, + "data": { + "code": "KeyA" + } + }, + { + "type": "keydown", + "testMs": 100, + "data": { + "code": "KeyB" + } + }, + { + "type": "keyup", + "testMs": 120, + "data": { + "code": "KeyB" + } + }, + { + "type": "keydown", + "testMs": 120, + "data": { + "code": "KeyB" + } + }, + { + "type": "keyup", + "testMs": 180, + "data": { + "code": "KeyB" + } + }, + { + "type": "keydown", + "testMs": 250, + "data": { + "code": "NoCode0" + } + } + ], + "context": { + "targetWords": [[97, 98, 99]], + "mode": "words", + "mode2": "1", + "koreanStatus": false, + "bailedOut": false, + "isFunboxWithNospacePropertyActive": false + } + } + }, + { + "state": { + "wordIndex": 0, + "inputValue": [], + "active": true, + "ended": false + }, + "log": { + "version": 1, + "events": [ + { + "type": "keydown", + "testMs": -100, + "data": { + "code": "KeyX" + } + }, + { + "type": "keyup", + "testMs": -90, + "data": { + "code": "KeyX" + } + }, + { + "type": "keydown", + "testMs": -50, + "data": { + "code": "KeyA" + } + }, + { + "type": "timer", + "testMs": 0, + "data": { + "event": "start", + "timer": 0, + "date": 5000 + } + }, + { + "type": "keyup", + "testMs": 20, + "data": { + "code": "KeyA" + } + }, + { + "type": "keydown", + "testMs": 100, + "data": { + "code": "KeyB" + } + }, + { + "type": "keyup", + "testMs": 120, + "data": { + "code": "KeyB" + } + }, + { + "type": "keydown", + "testMs": 120, + "data": { + "code": "KeyB" + } + }, + { + "type": "keyup", + "testMs": 180, + "data": { + "code": "KeyB" + } + }, + { + "type": "keydown", + "testMs": 250, + "data": { + "code": "NoCode0" + } + }, + { + "type": "keydown", + "testMs": 260, + "data": { + "code": "NoCode1" + } + } + ], + "context": { + "targetWords": [[97, 98, 99]], + "mode": "words", + "mode2": "1", + "koreanStatus": false, + "bailedOut": false, + "isFunboxWithNospacePropertyActive": false + } + } + }, + { + "state": { + "wordIndex": 0, + "inputValue": [], + "active": true, + "ended": false + }, + "log": { + "version": 1, + "events": [ + { + "type": "keydown", + "testMs": -100, + "data": { + "code": "KeyX" + } + }, + { + "type": "keyup", + "testMs": -90, + "data": { + "code": "KeyX" + } + }, + { + "type": "keydown", + "testMs": -50, + "data": { + "code": "KeyA" + } + }, + { + "type": "timer", + "testMs": 0, + "data": { + "event": "start", + "timer": 0, + "date": 5000 + } + }, + { + "type": "keyup", + "testMs": 20, + "data": { + "code": "KeyA" + } + }, + { + "type": "keydown", + "testMs": 100, + "data": { + "code": "KeyB" + } + }, + { + "type": "keyup", + "testMs": 120, + "data": { + "code": "KeyB" + } + }, + { + "type": "keydown", + "testMs": 120, + "data": { + "code": "KeyB" + } + }, + { + "type": "keyup", + "testMs": 180, + "data": { + "code": "KeyB" + } + }, + { + "type": "keydown", + "testMs": 250, + "data": { + "code": "NoCode0" + } + }, + { + "type": "keydown", + "testMs": 260, + "data": { + "code": "NoCode1" + } + }, + { + "type": "keyup", + "testMs": 270, + "data": { + "code": "NoCode1" + } + } + ], + "context": { + "targetWords": [[97, 98, 99]], + "mode": "words", + "mode2": "1", + "koreanStatus": false, + "bailedOut": false, + "isFunboxWithNospacePropertyActive": false + } + } + }, + { + "state": { + "wordIndex": 0, + "inputValue": [97, 98, 99], + "active": false, + "ended": true + }, + "log": { + "version": 1, + "events": [ + { + "type": "keydown", + "testMs": -100, + "data": { + "code": "KeyX" + } + }, + { + "type": "keyup", + "testMs": -90, + "data": { + "code": "KeyX" + } + }, + { + "type": "keydown", + "testMs": -50, + "data": { + "code": "KeyA" + } + }, + { + "type": "timer", + "testMs": 0, + "data": { + "event": "start", + "timer": 0, + "date": 5000 + } + }, + { + "type": "keyup", + "testMs": 20, + "data": { + "code": "KeyA" + } + }, + { + "type": "keydown", + "testMs": 100, + "data": { + "code": "KeyB" + } + }, + { + "type": "keyup", + "testMs": 120, + "data": { + "code": "KeyB" + } + }, + { + "type": "keydown", + "testMs": 120, + "data": { + "code": "KeyB" + } + }, + { + "type": "keyup", + "testMs": 180, + "data": { + "code": "KeyB" + } + }, + { + "type": "keydown", + "testMs": 250, + "data": { + "code": "NoCode0" + } + }, + { + "type": "keydown", + "testMs": 260, + "data": { + "code": "NoCode1" + } + }, + { + "type": "keyup", + "testMs": 270, + "data": { + "code": "NoCode1" + } + }, + { + "type": "input", + "testMs": 300, + "data": { + "inputType": "insertText", + "data": [97], + "correct": true, + "wordIndex": 0, + "charIndex": 0, + "inputValue": [97], + "lastWord": true + } + }, + { + "type": "input", + "testMs": 300, + "data": { + "inputType": "insertText", + "data": [98], + "correct": true, + "wordIndex": 0, + "charIndex": 1, + "inputValue": [97, 98], + "lastWord": true + } + }, + { + "type": "input", + "testMs": 300, + "data": { + "inputType": "insertText", + "data": [99], + "correct": true, + "wordIndex": 0, + "charIndex": 2, + "inputValue": [97, 98, 99], + "lastWord": true + } + }, + { + "type": "timer", + "testMs": 300, + "data": { + "event": "end", + "timer": 0, + "date": 5300 + } + } + ], + "context": { + "targetWords": [[97, 98, 99]], + "mode": "words", + "mode2": "1", + "koreanStatus": false, + "bailedOut": false, + "isFunboxWithNospacePropertyActive": false + } + } + }, + { + "state": { + "wordIndex": 0, + "inputValue": [97, 98, 99], + "active": false, + "ended": true + }, + "log": { + "version": 1, + "events": [ + { + "type": "keydown", + "testMs": -50, + "data": { + "code": "KeyA" + } + }, + { + "type": "timer", + "testMs": 0, + "data": { + "event": "start", + "timer": 0, + "date": 5000 + } + }, + { + "type": "keyup", + "testMs": 20, + "data": { + "code": "KeyA" + } + }, + { + "type": "keydown", + "testMs": 100, + "data": { + "code": "KeyB" + } + }, + { + "type": "keyup", + "testMs": 120, + "data": { + "code": "KeyB" + } + }, + { + "type": "keydown", + "testMs": 120, + "data": { + "code": "KeyB" + } + }, + { + "type": "keyup", + "testMs": 180, + "data": { + "code": "KeyB" + } + }, + { + "type": "keydown", + "testMs": 250, + "data": { + "code": "NoCode0" + } + }, + { + "type": "keydown", + "testMs": 260, + "data": { + "code": "NoCode1" + } + }, + { + "type": "keyup", + "testMs": 270, + "data": { + "code": "NoCode1" + } + }, + { + "type": "keyup", + "testMs": 280, + "data": { + "code": "NoCode0", + "estimated": true + } + }, + { + "type": "input", + "testMs": 300, + "data": { + "inputType": "insertText", + "data": [97], + "correct": true, + "wordIndex": 0, + "charIndex": 0, + "inputValue": [97], + "lastWord": true + } + }, + { + "type": "input", + "testMs": 300, + "data": { + "inputType": "insertText", + "data": [98], + "correct": true, + "wordIndex": 0, + "charIndex": 1, + "inputValue": [97, 98], + "lastWord": true + } + }, + { + "type": "input", + "testMs": 300, + "data": { + "inputType": "insertText", + "data": [99], + "correct": true, + "wordIndex": 0, + "charIndex": 2, + "inputValue": [97, 98, 99], + "lastWord": true + } + }, + { + "type": "timer", + "testMs": 300, + "data": { + "event": "end", + "timer": 0, + "date": 5300 + } + } + ], + "context": { + "targetWords": [[97, 98, 99]], + "mode": "words", + "mode2": "1", + "koreanStatus": false, + "bailedOut": false, + "isFunboxWithNospacePropertyActive": false + } + } + } + ] +} From 72ff8da2ce04a3961ffb2b26ac2ef7cd72b3a683 Mon Sep 17 00:00:00 2001 From: Lakshmi Tanmay Date: Fri, 23 Oct 2026 00:09:55 +0530 Subject: [PATCH 11/93] feat(engine): derive legacy score and timing fields from event logs --- Cargo.toml | 5 +- crates/typing-engine/src/lib.rs | 1 + crates/typing-engine/src/stats.rs | 412 +++++++++++++++++++++++ crates/typing-engine/tests/statistics.rs | 55 +++ 4 files changed, 472 insertions(+), 1 deletion(-) create mode 100644 crates/typing-engine/src/stats.rs create mode 100644 crates/typing-engine/tests/statistics.rs diff --git a/Cargo.toml b/Cargo.toml index a9ce7534a..657027a0b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,10 @@ repository = "https://github.com/voltcrash/oxytype" [workspace.dependencies] serde = { version = "1.0.229", features = ["derive"] } -serde_json = { version = "1.0.151", features = ["preserve_order"] } +serde_json = { version = "1.0.151", features = [ + "preserve_order", + "float_roundtrip", +] } wasm-bindgen = "=0.2.129" ts-rs = { version = "=12.0.1", features = ["serde-json-impl"] } diff --git a/crates/typing-engine/src/lib.rs b/crates/typing-engine/src/lib.rs index e9ff18836..185ab9fc1 100644 --- a/crates/typing-engine/src/lib.rs +++ b/crates/typing-engine/src/lib.rs @@ -1,5 +1,6 @@ //! Platform-independent typing rules. Hosts provide content, observations and time. pub mod session; +pub mod stats; pub mod text; pub use session::{Command, Config, Session}; diff --git a/crates/typing-engine/src/stats.rs b/crates/typing-engine/src/stats.rs new file mode 100644 index 000000000..eb13aa787 --- /dev/null +++ b/crates/typing-engine/src/stats.rs @@ -0,0 +1,412 @@ +//! Legacy event-log derivation. Text offsets/counts remain UTF-16 units. +use crate::{ + Text, js_round, round2, + session::Event, + text::{is_space, js_whitespace}, +}; +use serde::{Deserialize, Serialize}; +use serde_json::{Value, json}; + +#[derive(Clone, Debug, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct Context { + pub target_words: Vec, + pub mode: String, + pub mode2: String, + #[serde(default)] + pub bailed_out: bool, + #[serde(default)] + pub korean_status: bool, + pub custom_text_limit_mode: Option, + pub custom_text_limit_value: Option, +} +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct Log { + pub version: u32, + pub events: Vec, + pub context: Context, +} + +pub fn text(value: &Value) -> Text { + if let Some(value) = value.as_str() { + Text::from(value) + } else { + serde_json::from_value(value.clone()).unwrap_or_default() + } +} +pub fn word_index(event: &Event) -> Option { + event.data["wordIndex"].as_u64().map(|v| v as usize) +} +fn is_insert(event: &Event) -> bool { + event.kind == "input" + && (event.data["inputType"] == "insertText" + || event.data["inputType"] == "insertCompositionText") +} +pub fn input(events: &[&Event]) -> Text { + let Some(event) = events.iter().rev().find(|event| event.kind == "input") else { + return Text::default(); + }; + let mut value = text(&event.data["inputValue"]); + if event.data["inputType"] == "insertText" + && text(&event.data["data"]).0 == [32] + && event.data["lastWord"] == true + && event.data["commitsWord"] == true + && event.data["correct"] == false + { + while value.0.last().is_some_and(|c| js_whitespace(*c)) { + value.0.pop(); + } + } + value +} +fn per_word(log: &Log, limit: Option) -> Vec<(usize, Vec<&Event>)> { + let mut words: Vec<(usize, Vec<&Event>)> = vec![]; + for event in &log.events { + if limit.is_some_and(|limit| event.test_ms > limit) { + break; + } + if let Some(index) = word_index(event) { + if let Some((_, events)) = words.iter_mut().find(|(i, _)| *i == index) { + events.push(event); + } else { + words.push((index, vec![event])); + } + } + } + words +} +fn active_index(words: &[(usize, Vec<&Event>)]) -> usize { + let last = words + .iter() + .filter(|(_, events)| !input(events).is_empty()) + .max_by_key(|(i, _)| *i); + let Some((index, events)) = last else { + return 0; + }; + let space = events + .last() + .is_some_and(|e| e.data["inputType"] == "insertText" && text(&e.data["data"]).0 == [32]); + *index + usize::from(space) +} +pub fn timed(log: &Log) -> bool { + log.context.mode == "time" + || (log.context.mode == "words" && log.context.mode2 == "0") + || (log.context.mode == "custom" + && (log.context.custom_text_limit_mode.as_deref() == Some("time") + || log.context.custom_text_limit_value == Some(0.0))) +} +#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct Chars { + pub all_correct: usize, + pub correct_word: usize, + pub incorrect: usize, + pub extra: usize, + pub missed: usize, +} +pub fn count_chars(input: &Text, target: &Text, partial: bool) -> Chars { + let correct = input == target; + let prefix = target.0.starts_with(&input.0); + let mut chars = Chars::default(); + for index in 0..input.len().max(target.len()) { + let a = input.0.get(index); + let b = target.0.get(index); + if a == b { + if b == Some(&32) && !correct { + chars.extra += 1; + } else { + chars.all_correct += 1; + } + if correct || (partial && prefix) { + chars.correct_word += 1; + } + } else if a.is_none() { + if !partial { + chars.missed += 1; + } + } else if b.is_none() || (b == Some(&32) && a != Some(&32) && !input.0.contains(&32)) { + chars.extra += 1; + } else { + chars.incorrect += 1; + } + } + chars +} +pub fn chars(log: &Log, partial: bool, limit: Option) -> Chars { + let words = per_word(log, limit); + let active = active_index(&words); + let mut total = Chars::default(); + for (index, events) in words { + let mut typed = input(&events); + for unit in &mut typed.0 { + if is_space(*unit) { + *unit = 32; + } + } + let target = log.context.target_words.get(index).unwrap_or(&typed); + let counts = count_chars( + &typed, + target, + index == active && (partial || timed(log) || log.context.bailed_out), + ); + total.all_correct += counts.all_correct; + total.correct_word += counts.correct_word; + total.incorrect += counts.incorrect; + total.extra += counts.extra; + total.missed += counts.missed; + if index == active { + break; + } + } + total +} +pub fn wpm(chars: usize, seconds: f64) -> f64 { + if seconds <= 0.0 { + 0.0 + } else { + chars as f64 / 5.0 / (seconds / 60.0) + } +} +pub fn accuracy(log: &Log, limit: Option) -> f64 { + let mut correct = 0; + let mut total = 0; + for event in &log.events { + if limit.is_some_and(|limit| event.test_ms > limit) { + break; + } + if event.kind == "input" && event.data.get("correct").is_some() { + total += 1; + if event.data["correct"] == true { + correct += 1; + } + } + } + if total == 0 { + 0.0 + } else { + correct as f64 / total as f64 * 100.0 + } +} +fn end(log: &Log) -> f64 { + log.events + .iter() + .rev() + .find(|event| event.kind == "timer" && event.data["event"] == "end") + .map(|event| event.test_ms) + .unwrap_or(0.0) +} +fn raw_last_key_to_end(log: &Log) -> f64 { + let last = log + .events + .iter() + .rev() + .find(|event| event.kind == "keydown"); + last.map(|event| round2((end(log) - event.test_ms).max(0.0))) + .unwrap_or(0.0) +} +fn adjusted_end(log: &Log) -> f64 { + let mut end = end(log); + if log.context.mode == "zen" || log.context.bailed_out { + let last = raw_last_key_to_end(log); + if last < 7000.0 { + end -= last; + } + } + end +} +pub fn duration(log: &Log) -> f64 { + let end = adjusted_end(log); + if log.context.mode == "custom" { + end + } else { + round2(end / 1000.0) * 1000.0 + } +} +pub fn boundaries(log: &Log) -> Vec { + if !log + .events + .iter() + .any(|event| event.kind == "timer" && event.data["event"] == "end") + { + return vec![]; + } + let end = adjusted_end(log); + let ticks = (end / 1000.0).floor() as usize; + let mut boundaries: Vec<_> = (1..=ticks).map(|i| i as f64 * 1000.0).collect(); + if !timed(log) && js_round(round2(end / 1000.0) % 1.0) >= 0.5 { + boundaries.push(end); + } + boundaries +} +fn count_intervals(log: &Log, predicate: impl Fn(&Event) -> bool) -> Vec { + let mut index = 0; + let mut counts = vec![]; + for boundary in boundaries(log) { + let mut count = 0; + while let Some(event) = log.events.get(index) { + if event.test_ms > boundary { + break; + } + if predicate(event) { + count += 1; + } + index += 1; + } + counts.push(count); + } + counts +} +pub fn keypresses_per_second(log: &Log) -> Vec { + count_intervals(log, |event| { + event.kind == "input" && event.data["inputType"] == "insertText" + }) +} +pub fn burst_history(log: &Log) -> Vec { + let counts = keypresses_per_second(log); + let mut previous = 0.0; + boundaries(log) + .iter() + .zip(counts) + .map(|(boundary, count)| { + let seconds = (boundary - previous) / 1000.0; + previous = *boundary; + js_round(wpm(count, seconds)) + }) + .collect() +} +pub fn speed_history(log: &Log, raw: bool) -> Vec { + boundaries(log) + .iter() + .map(|boundary| { + let chars = chars(log, true, Some(*boundary)); + js_round(wpm( + if raw { + chars.all_correct + chars.incorrect + chars.extra + } else { + chars.correct_word + }, + boundary / 1000.0, + )) + }) + .collect() +} +pub fn afk(log: &Log) -> usize { + count_intervals(log, |e| e.kind == "keydown" || e.kind == "input") + .iter() + .filter(|count| **count == 0) + .count() +} +pub fn incomplete_seconds(log: &Log) -> f64 { + round2(duration(log) / 1000.0 - afk(log) as f64).max(0.0) +} +pub fn consistency(values: &[f64]) -> f64 { + if values.is_empty() { + return 0.0; + } + let mean = values.iter().sum::() / values.len() as f64; + let deviation = (values + .iter() + .map(|value| (value - mean).powi(2)) + .sum::() + / values.len() as f64) + .sqrt(); + let cov = deviation / mean; + let result = round2(100.0 * (1.0 - (cov + cov.powi(3) / 3.0 + cov.powi(5) / 5.0).tanh())); + if result.is_nan() { 0.0 } else { result } +} +pub fn word_burst(log: &Log, index: usize, now: f64) -> f64 { + let events: Vec<_> = log + .events + .iter() + .filter(|e| word_index(e) == Some(index)) + .collect(); + let input = input(&events); + let mut length = input.len(); + if !matches!(input.0.last(), Some(32 | 10)) { + length += 1; + } + let mut first = None; + let mut last = None; + for event in events { + if event.kind == "composition" && event.data["event"] == "start" && first.is_none() { + first = Some(event.test_ms); + } + if is_insert(event) { + if event.data["charIndex"] == 0 && first.is_none() { + first = Some(event.test_ms); + } + if first.is_some() { + last = Some(event.test_ms); + } + } + } + let Some(first) = first else { + return 0.0; + }; + if input.is_empty() { + return 0.0; + } + let seconds = (last.filter(|last| *last >= first).unwrap_or(now) - first) / 1000.0; + if seconds <= 0.0 { + f64::INFINITY + } else { + js_round(wpm(length, seconds)) + } +} +pub fn summary(log: &Log) -> Value { + let counts = chars(log, false, None); + let seconds = duration(log) / 1000.0; + let burst = burst_history(log); + let speed = speed_history(log, false); + let errors = count_intervals(log, |event| { + event.kind == "input" + && event.data["inputType"] == "insertText" + && event.data["correct"] == false + }); + let mut spacing = vec![]; + let mut last = None; + let mut durations: Vec = vec![]; + let mut held: Vec<(String, f64, usize)> = vec![]; + let mut overlap = 0.0; + let mut overlap_start = None; + let first = log + .events + .iter() + .find(|event| event.kind == "keydown") + .map(|event| event.test_ms.max(0.0)) + .unwrap_or(0.0); + for event in &log.events { + let code = event.data["code"].as_str().unwrap_or_default().to_owned(); + if event.kind == "keydown" { + if let Some(previous) = last { + spacing.push(event.test_ms - previous); + } + last = Some(event.test_ms.max(0.0)); + if let Some(index) = held.iter().position(|(key, _, _)| key == &code) { + held.remove(index); + } + held.push((code, event.test_ms, durations.len())); + durations.push(0.0); + if overlap_start.is_none() && held.len() > 1 { + overlap_start = Some(event.test_ms); + } + } else if event.kind == "keyup" { + if let Some(index) = held.iter().position(|(key, _, _)| key == &code) { + let (_, time, index) = held.remove(index); + durations[index] = event.test_ms - time; + } + if let Some(start) = overlap_start + && held.len() == 1 + { + overlap += event.test_ms - start; + overlap_start = None; + } + } + } + json!({"wpm":round2(wpm(counts.correct_word,seconds)),"rawWpm":round2(wpm(counts.all_correct+counts.incorrect+counts.extra,seconds)), + "charStats":[counts.correct_word,counts.incorrect,counts.extra,counts.missed],"charTotal":counts.all_correct+counts.incorrect+counts.extra, + "acc":round2(accuracy(log,None)),"testDuration":seconds,"lastKeyToEnd":if log.context.mode=="zen" {0.0} else {raw_last_key_to_end(log)}, + "startToFirstKey":if log.context.mode=="zen" {0.0} else {round2(first)},"afkDuration":afk(log), + "consistency":consistency(&burst),"wpmConsistency":consistency(&speed),"keyConsistency":consistency(&spacing[..spacing.len().saturating_sub(1)]), + "chartData":{"wpm":speed,"burst":burst,"err":errors},"keySpacing":spacing,"keyDuration":durations,"keyOverlap":round2(overlap)}) +} diff --git a/crates/typing-engine/tests/statistics.rs b/crates/typing-engine/tests/statistics.rs new file mode 100644 index 000000000..b53ae420e --- /dev/null +++ b/crates/typing-engine/tests/statistics.rs @@ -0,0 +1,55 @@ +use serde_json::{Value, json}; +use typing_engine::{ + Text, + stats::{Log, summary}, +}; + +fn equal(a: &Value, b: &Value, path: &str) { + match (a, b) { + (Value::Number(a), Value::Number(b)) => assert_eq!(a.as_f64(), b.as_f64(), "{path}"), + (Value::Array(a), Value::Array(b)) => { + assert_eq!(a.len(), b.len(), "{path}"); + for (index, (a, b)) in a.iter().zip(b).enumerate() { + equal(a, b, &format!("{path}[{index}]")); + } + } + (Value::Object(a), Value::Object(b)) => { + assert_eq!(a.len(), b.len(), "{path}"); + for (key, a) in a { + equal(a, &b[key], &format!("{path}.{key}")); + } + } + _ => assert_eq!(a, b, "{path}"), + } +} + +#[test] +fn summary_fields_match_all_unchanged_browser_results_exactly() { + let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR")) + .join("../../packages/typing-fixtures/reference/keystrokes"); + for file in std::fs::read_dir(root).unwrap() { + let path = file.unwrap().path(); + if path.extension().is_none_or(|ext| ext != "json") { + continue; + } + let fixture: Value = serde_json::from_slice(&std::fs::read(&path).unwrap()).unwrap(); + let mut log = fixture["eventLog"].clone(); + log["context"]["targetWords"] = json!( + log["context"]["targetWords"] + .as_array() + .unwrap() + .iter() + .map(|word| Text::from(word.as_str().unwrap())) + .collect::>() + ); + let log: Log = serde_json::from_value(log).unwrap(); + let actual = summary(&log); + for (key, actual) in actual.as_object().unwrap() { + equal( + actual, + &fixture["completedEvent"][key], + &format!("{}.{key}", path.display()), + ); + } + } +} From 936cddaca6c520af43450d6a968c9b67c60ec0bd Mon Sep 17 00:00:00 2001 From: Lakshmi Tanmay Date: Fri, 23 Oct 2026 00:09:56 +0530 Subject: [PATCH 12/93] feat(engine): preserve timer stalls minimum accuracy speed and burst failures --- crates/typing-engine/src/session.rs | 69 +++++ crates/typing-engine/tests/slice.rs | 5 + docs/rust-engine/progress.md | 5 +- .../typing-fixtures/commands/min-burst.json | 25 ++ .../commands/time-stall-finish.json | 26 ++ .../typing-fixtures/commands/time-zero.json | 26 ++ .../commands/timer-min-accuracy.json | 24 ++ .../commands/timer-min-speed.json | 21 ++ .../typing-fixtures/oracle/min-burst.json | 142 +++++++++ .../oracle/time-stall-finish.json | 212 +++++++++++++ .../typing-fixtures/oracle/time-zero.json | 239 ++++++++++++++ .../oracle/timer-min-accuracy.json | 114 +++++++ .../oracle/timer-min-speed.json | 293 ++++++++++++++++++ 13 files changed, 1200 insertions(+), 1 deletion(-) create mode 100644 packages/typing-fixtures/commands/min-burst.json create mode 100644 packages/typing-fixtures/commands/time-stall-finish.json create mode 100644 packages/typing-fixtures/commands/time-zero.json create mode 100644 packages/typing-fixtures/commands/timer-min-accuracy.json create mode 100644 packages/typing-fixtures/commands/timer-min-speed.json create mode 100644 packages/typing-fixtures/oracle/min-burst.json create mode 100644 packages/typing-fixtures/oracle/time-stall-finish.json create mode 100644 packages/typing-fixtures/oracle/time-zero.json create mode 100644 packages/typing-fixtures/oracle/timer-min-accuracy.json create mode 100644 packages/typing-fixtures/oracle/timer-min-speed.json diff --git a/crates/typing-engine/src/session.rs b/crates/typing-engine/src/session.rs index 2a82eaf9d..7449ba42a 100644 --- a/crates/typing-engine/src/session.rs +++ b/crates/typing-engine/src/session.rs @@ -24,6 +24,12 @@ pub struct Config { pub blind_mode: bool, pub code_unindent_on_backspace: bool, pub funbox: Vec, + pub min_burst: String, + pub min_burst_custom_speed: f64, + pub min_wpm: String, + pub min_wpm_custom_speed: f64, + pub min_acc: String, + pub min_acc_custom: f64, } impl Default for Config { @@ -43,6 +49,12 @@ impl Default for Config { blind_mode: false, code_unindent_on_backspace: false, funbox: vec![], + min_burst: "off".into(), + min_burst_custom_speed: 100.0, + min_wpm: "off".into(), + min_wpm_custom_speed: 100.0, + min_acc: "off".into(), + min_acc_custom: 90.0, } } } @@ -447,6 +459,30 @@ impl Session { self.end(now, date, Some("difficulty".into())); return; } + if advance && self.config.min_burst != "off" { + let log: crate::stats::Log = + serde_json::from_value(self.log()).expect("Engine log is valid"); + let burst = crate::stats::word_burst(&log, self.index, now - self.start.unwrap_or(now)); + let length = if self.config.mode == "zen" { + input.len() + 1 + } else { + target.len() + }; + let flex = self.config.min_burst_custom_speed.min( + (self.config.min_burst_custom_speed * 1.03_f64.powf(-2.0 * (length as f64 - 3.0))) + .floor(), + ); + if burst + < (if self.config.min_burst == "flex" { + flex + } else { + self.config.min_burst_custom_speed + }) + { + self.end(now, date, Some("min burst".into())); + return; + } + } let finite = self.config.mode != "time" && self.config.mode != "zen" && !(self.config.mode == "words" && self.config.words == 0); @@ -604,6 +640,39 @@ impl Session { data["drift"] = json!(drift); } self.record("timer", now, data); + if self.ticks == due + && (self.config.min_wpm == "custom" || self.config.min_acc == "custom") + { + let log: crate::stats::Log = + serde_json::from_value(self.log()).expect("Engine log is valid"); + let chars = crate::stats::chars(&log, true, None); + let wpm = crate::js_round(crate::stats::wpm( + chars.correct_word, + (now - start) / 1000.0, + )); + let has_input = self + .events + .iter() + .any(|e| e.kind == "input" && e.data.get("correct").is_some()); + let accuracy = if has_input { + crate::stats::accuracy(&log, None) + } else { + 100.0 + }; + let reason = if self.config.min_wpm == "custom" + && wpm < self.config.min_wpm_custom_speed + && self.index > 3 + { + Some("min speed") + } else if self.config.min_acc == "custom" && accuracy < self.config.min_acc_custom { + Some("min accuracy") + } else { + None + }; + if let Some(reason) = reason { + self.end(now, date, Some(reason.into())); + } + } if self.config.mode == "time" && self.config.time > 0.0 && f64::from(self.ticks) >= self.config.time diff --git a/crates/typing-engine/tests/slice.rs b/crates/typing-engine/tests/slice.rs index 350b0f25b..814c3aa30 100644 --- a/crates/typing-engine/tests/slice.rs +++ b/crates/typing-engine/tests/slice.rs @@ -44,6 +44,11 @@ fn finite_slice_matches_frozen_intermediate_oracle() { "delete-error-word", "delete-error-word_hard", "physical-observations", + "timer-min-accuracy", + "timer-min-speed", + "min-burst", + "time-stall-finish", + "time-zero", ] { let fixture: Fixture = serde_json::from_slice( &std::fs::read(root.join(format!("commands/{name}.json"))).unwrap(), diff --git a/docs/rust-engine/progress.md b/docs/rust-engine/progress.md index e712bc775..f52d8b4b2 100644 --- a/docs/rust-engine/progress.md +++ b/docs/rust-engine/progress.md @@ -21,7 +21,7 @@ Phase 1.1 and 1.2 gates passed: inventory/baselines, lossless contract, frozen o | 1.1 Inventory / baselines | Complete | See inventory.md, features.md, baseline.md and raw measurements. | | 1.2 Contracts / fixtures | Complete | 13 command traces, 15 oracle/integrity tests; contract and classified gaps. | | 1.3 Native / browser proof | Complete | Native 7 traces; Cloudflare real-browser finite slice; MIME/CSP, lifecycle, boundary benchmark. Budgets frozen. | -| 1.4 Input / lifecycle / timing | Active | Remaining input penalties, code, physical observations, browser differences and timing. | +| 1.4 Input / lifecycle / timing | Active | Input penalties/code and physical recording now match 14 traces and 8 browser recordings. Browser differences/timing failures still open. | | 1.5 Generation / configuration | Pending | Requires contract and proof. | | 1.6 Events / results / replay | Pending | Requires input and generation. | | 1.7 Binding / engine gate | Pending | Full three-path parity and budgets. | @@ -46,6 +46,9 @@ Phase 1.1 and 1.2 gates passed: inventory/baselines, lossless contract, frozen o - Phase 1.3: boundary benchmark/type-aware lint passed; proof-performance.json and performance-budgets.md record limits and evidence. - Early failed browser proof correctly exposed CSP; fixed and retested. Initial build raced workspace dependency installation; reran after install completed. +- Phase 1.4 partial: native input logs match all eight original browser recordings. All 14 command traces (including physical repeat/NoCode/estimated cleanup) match intermediate frames. +- Native Clippy passes. Pure event statistics are being ported as prerequisites for min-speed/min-burst timing rules; full result gate remains pending. + ## Decisions - User's three-stage PR structure overrides the plan's suggested per-phase PRs. diff --git a/packages/typing-fixtures/commands/min-burst.json b/packages/typing-fixtures/commands/min-burst.json new file mode 100644 index 000000000..2e74a8d46 --- /dev/null +++ b/packages/typing-fixtures/commands/min-burst.json @@ -0,0 +1,25 @@ +{ + "name": "min-burst", + "config": { + "mode": "words", + "words": 2, + "minBurst": "fixed", + "minBurstCustomSpeed": 100 + }, + "words": [ + [111, 110, 101, 32], + [116, 119, 111] + ], + "commands": [ + { + "op": "insert", + "now": 0, + "data": [111] + }, + { + "op": "insert", + "now": 10000, + "data": [110, 101, 32] + } + ] +} diff --git a/packages/typing-fixtures/commands/time-stall-finish.json b/packages/typing-fixtures/commands/time-stall-finish.json new file mode 100644 index 000000000..91d4cb710 --- /dev/null +++ b/packages/typing-fixtures/commands/time-stall-finish.json @@ -0,0 +1,26 @@ +{ + "name": "time-stall-finish", + "config": { + "mode": "time", + "time": 3 + }, + "words": [ + [111, 110, 101, 32], + [116, 119, 111, 32] + ], + "commands": [ + { + "op": "insert", + "now": 0, + "data": [111] + }, + { + "op": "advance", + "now": 5000 + }, + { + "op": "advance", + "now": 6000 + } + ] +} diff --git a/packages/typing-fixtures/commands/time-zero.json b/packages/typing-fixtures/commands/time-zero.json new file mode 100644 index 000000000..1795b0de7 --- /dev/null +++ b/packages/typing-fixtures/commands/time-zero.json @@ -0,0 +1,26 @@ +{ + "name": "time-zero", + "config": { + "mode": "time", + "time": 0 + }, + "words": [ + [111, 110, 101, 32], + [116, 119, 111, 32] + ], + "commands": [ + { + "op": "insert", + "now": 0, + "data": [111] + }, + { + "op": "advance", + "now": 5000 + }, + { + "op": "end", + "now": 6000 + } + ] +} diff --git a/packages/typing-fixtures/commands/timer-min-accuracy.json b/packages/typing-fixtures/commands/timer-min-accuracy.json new file mode 100644 index 000000000..b80d235a8 --- /dev/null +++ b/packages/typing-fixtures/commands/timer-min-accuracy.json @@ -0,0 +1,24 @@ +{ + "name": "timer-min-accuracy", + "config": { + "mode": "words", + "words": 2, + "minAcc": "custom", + "minAccCustom": 90 + }, + "words": [ + [111, 110, 101, 32], + [116, 119, 111] + ], + "commands": [ + { + "op": "insert", + "now": 0, + "data": [120] + }, + { + "op": "advance", + "now": 1000 + } + ] +} diff --git a/packages/typing-fixtures/commands/timer-min-speed.json b/packages/typing-fixtures/commands/timer-min-speed.json new file mode 100644 index 000000000..22dfa8225 --- /dev/null +++ b/packages/typing-fixtures/commands/timer-min-speed.json @@ -0,0 +1,21 @@ +{ + "name": "timer-min-speed", + "config": { + "mode": "words", + "words": 5, + "minWpm": "custom", + "minWpmCustomSpeed": 100 + }, + "words": [[97, 32], [98, 32], [99, 32], [100, 32], [101]], + "commands": [ + { + "op": "insert", + "now": 0, + "data": [97, 32, 98, 32, 99, 32, 100, 32] + }, + { + "op": "advance", + "now": 2000 + } + ] +} diff --git a/packages/typing-fixtures/oracle/min-burst.json b/packages/typing-fixtures/oracle/min-burst.json new file mode 100644 index 000000000..8d79fa626 --- /dev/null +++ b/packages/typing-fixtures/oracle/min-burst.json @@ -0,0 +1,142 @@ +{ + "name": "min-burst", + "frames": [ + { + "state": { + "wordIndex": 0, + "inputValue": [111], + "active": true, + "ended": false + }, + "log": { + "version": 1, + "events": [ + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [111], + "correct": true, + "wordIndex": 0, + "charIndex": 0, + "inputValue": [111] + } + }, + { + "type": "timer", + "testMs": 0, + "data": { + "event": "start", + "timer": 0, + "date": 0 + } + } + ], + "context": { + "targetWords": [ + [111, 110, 101, 32], + [116, 119, 111] + ], + "mode": "words", + "mode2": "2", + "koreanStatus": false, + "bailedOut": false, + "isFunboxWithNospacePropertyActive": false + } + } + }, + { + "state": { + "wordIndex": 0, + "inputValue": [111, 110, 101, 32], + "active": false, + "ended": true, + "reason": "min burst" + }, + "log": { + "version": 1, + "events": [ + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [111], + "correct": true, + "wordIndex": 0, + "charIndex": 0, + "inputValue": [111] + } + }, + { + "type": "timer", + "testMs": 0, + "data": { + "event": "start", + "timer": 0, + "date": 0 + } + }, + { + "type": "input", + "testMs": 10000, + "data": { + "inputType": "insertText", + "data": [110], + "correct": true, + "wordIndex": 0, + "charIndex": 1, + "inputValue": [111, 110] + } + }, + { + "type": "input", + "testMs": 10000, + "data": { + "inputType": "insertText", + "data": [101], + "correct": true, + "wordIndex": 0, + "charIndex": 2, + "inputValue": [111, 110, 101] + } + }, + { + "type": "input", + "testMs": 10000, + "data": { + "inputType": "insertText", + "data": [32], + "correct": true, + "wordIndex": 0, + "charIndex": 3, + "inputValue": [111, 110, 101, 32], + "commitsWord": true + } + }, + { + "type": "timer", + "testMs": 10000, + "data": { + "event": "end", + "timer": 10, + "date": 0 + } + } + ], + "context": { + "targetWords": [ + [111, 110, 101, 32], + [116, 119, 111] + ], + "mode": "words", + "mode2": "2", + "koreanStatus": false, + "bailedOut": false, + "isFunboxWithNospacePropertyActive": false + } + } + } + ] +} diff --git a/packages/typing-fixtures/oracle/time-stall-finish.json b/packages/typing-fixtures/oracle/time-stall-finish.json new file mode 100644 index 000000000..8f97907c8 --- /dev/null +++ b/packages/typing-fixtures/oracle/time-stall-finish.json @@ -0,0 +1,212 @@ +{ + "name": "time-stall-finish", + "frames": [ + { + "state": { + "wordIndex": 0, + "inputValue": [111], + "active": true, + "ended": false + }, + "log": { + "version": 1, + "events": [ + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [111], + "correct": true, + "wordIndex": 0, + "charIndex": 0, + "inputValue": [111] + } + }, + { + "type": "timer", + "testMs": 0, + "data": { + "event": "start", + "timer": 0, + "date": 0 + } + } + ], + "context": { + "targetWords": [ + [111, 110, 101, 32], + [116, 119, 111, 32] + ], + "mode": "time", + "mode2": "3", + "koreanStatus": false, + "bailedOut": false, + "isFunboxWithNospacePropertyActive": false + } + } + }, + { + "state": { + "wordIndex": 0, + "inputValue": [111], + "active": false, + "ended": true + }, + "log": { + "version": 1, + "events": [ + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [111], + "correct": true, + "wordIndex": 0, + "charIndex": 0, + "inputValue": [111] + } + }, + { + "type": "timer", + "testMs": 0, + "data": { + "event": "start", + "timer": 0, + "date": 0 + } + }, + { + "type": "timer", + "testMs": 5000, + "data": { + "event": "step", + "timer": 1, + "catchup": true + } + }, + { + "type": "timer", + "testMs": 5000, + "data": { + "event": "step", + "timer": 2, + "catchup": true + } + }, + { + "type": "timer", + "testMs": 5000, + "data": { + "event": "step", + "timer": 3, + "catchup": true + } + }, + { + "type": "timer", + "testMs": 5000, + "data": { + "event": "end", + "timer": 5, + "date": 0 + } + } + ], + "context": { + "targetWords": [ + [111, 110, 101, 32], + [116, 119, 111, 32] + ], + "mode": "time", + "mode2": "3", + "koreanStatus": false, + "bailedOut": false, + "isFunboxWithNospacePropertyActive": false + } + } + }, + { + "state": { + "wordIndex": 0, + "inputValue": [111], + "active": false, + "ended": true + }, + "log": { + "version": 1, + "events": [ + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [111], + "correct": true, + "wordIndex": 0, + "charIndex": 0, + "inputValue": [111] + } + }, + { + "type": "timer", + "testMs": 0, + "data": { + "event": "start", + "timer": 0, + "date": 0 + } + }, + { + "type": "timer", + "testMs": 5000, + "data": { + "event": "step", + "timer": 1, + "catchup": true + } + }, + { + "type": "timer", + "testMs": 5000, + "data": { + "event": "step", + "timer": 2, + "catchup": true + } + }, + { + "type": "timer", + "testMs": 5000, + "data": { + "event": "step", + "timer": 3, + "catchup": true + } + }, + { + "type": "timer", + "testMs": 5000, + "data": { + "event": "end", + "timer": 5, + "date": 0 + } + } + ], + "context": { + "targetWords": [ + [111, 110, 101, 32], + [116, 119, 111, 32] + ], + "mode": "time", + "mode2": "3", + "koreanStatus": false, + "bailedOut": false, + "isFunboxWithNospacePropertyActive": false + } + } + } + ] +} diff --git a/packages/typing-fixtures/oracle/time-zero.json b/packages/typing-fixtures/oracle/time-zero.json new file mode 100644 index 000000000..9141c0474 --- /dev/null +++ b/packages/typing-fixtures/oracle/time-zero.json @@ -0,0 +1,239 @@ +{ + "name": "time-zero", + "frames": [ + { + "state": { + "wordIndex": 0, + "inputValue": [111], + "active": true, + "ended": false + }, + "log": { + "version": 1, + "events": [ + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [111], + "correct": true, + "wordIndex": 0, + "charIndex": 0, + "inputValue": [111] + } + }, + { + "type": "timer", + "testMs": 0, + "data": { + "event": "start", + "timer": 0, + "date": 0 + } + } + ], + "context": { + "targetWords": [ + [111, 110, 101, 32], + [116, 119, 111, 32] + ], + "mode": "time", + "mode2": "0", + "koreanStatus": false, + "bailedOut": false, + "isFunboxWithNospacePropertyActive": false + } + } + }, + { + "state": { + "wordIndex": 0, + "inputValue": [111], + "active": true, + "ended": false + }, + "log": { + "version": 1, + "events": [ + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [111], + "correct": true, + "wordIndex": 0, + "charIndex": 0, + "inputValue": [111] + } + }, + { + "type": "timer", + "testMs": 0, + "data": { + "event": "start", + "timer": 0, + "date": 0 + } + }, + { + "type": "timer", + "testMs": 5000, + "data": { + "event": "step", + "timer": 1, + "catchup": true + } + }, + { + "type": "timer", + "testMs": 5000, + "data": { + "event": "step", + "timer": 2, + "catchup": true + } + }, + { + "type": "timer", + "testMs": 5000, + "data": { + "event": "step", + "timer": 3, + "catchup": true + } + }, + { + "type": "timer", + "testMs": 5000, + "data": { + "event": "step", + "timer": 4, + "catchup": true + } + }, + { + "type": "timer", + "testMs": 5000, + "data": { + "event": "step", + "timer": 5, + "drift": 4000 + } + } + ], + "context": { + "targetWords": [ + [111, 110, 101, 32], + [116, 119, 111, 32] + ], + "mode": "time", + "mode2": "0", + "koreanStatus": false, + "bailedOut": false, + "isFunboxWithNospacePropertyActive": false + } + } + }, + { + "state": { + "wordIndex": 0, + "inputValue": [111], + "active": false, + "ended": true + }, + "log": { + "version": 1, + "events": [ + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [111], + "correct": true, + "wordIndex": 0, + "charIndex": 0, + "inputValue": [111] + } + }, + { + "type": "timer", + "testMs": 0, + "data": { + "event": "start", + "timer": 0, + "date": 0 + } + }, + { + "type": "timer", + "testMs": 5000, + "data": { + "event": "step", + "timer": 1, + "catchup": true + } + }, + { + "type": "timer", + "testMs": 5000, + "data": { + "event": "step", + "timer": 2, + "catchup": true + } + }, + { + "type": "timer", + "testMs": 5000, + "data": { + "event": "step", + "timer": 3, + "catchup": true + } + }, + { + "type": "timer", + "testMs": 5000, + "data": { + "event": "step", + "timer": 4, + "catchup": true + } + }, + { + "type": "timer", + "testMs": 5000, + "data": { + "event": "step", + "timer": 5, + "drift": 4000 + } + }, + { + "type": "timer", + "testMs": 6000, + "data": { + "event": "end", + "timer": 6, + "date": 0 + } + } + ], + "context": { + "targetWords": [ + [111, 110, 101, 32], + [116, 119, 111, 32] + ], + "mode": "time", + "mode2": "0", + "koreanStatus": false, + "bailedOut": false, + "isFunboxWithNospacePropertyActive": false + } + } + } + ] +} diff --git a/packages/typing-fixtures/oracle/timer-min-accuracy.json b/packages/typing-fixtures/oracle/timer-min-accuracy.json new file mode 100644 index 000000000..7441cecc3 --- /dev/null +++ b/packages/typing-fixtures/oracle/timer-min-accuracy.json @@ -0,0 +1,114 @@ +{ + "name": "timer-min-accuracy", + "frames": [ + { + "state": { + "wordIndex": 0, + "inputValue": [120], + "active": true, + "ended": false + }, + "log": { + "version": 1, + "events": [ + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [120], + "correct": false, + "wordIndex": 0, + "charIndex": 0, + "inputValue": [120] + } + }, + { + "type": "timer", + "testMs": 0, + "data": { + "event": "start", + "timer": 0, + "date": 0 + } + } + ], + "context": { + "targetWords": [ + [111, 110, 101, 32], + [116, 119, 111] + ], + "mode": "words", + "mode2": "2", + "koreanStatus": false, + "bailedOut": false, + "isFunboxWithNospacePropertyActive": false + } + } + }, + { + "state": { + "wordIndex": 0, + "inputValue": [120], + "active": false, + "ended": true, + "reason": "min accuracy" + }, + "log": { + "version": 1, + "events": [ + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [120], + "correct": false, + "wordIndex": 0, + "charIndex": 0, + "inputValue": [120] + } + }, + { + "type": "timer", + "testMs": 0, + "data": { + "event": "start", + "timer": 0, + "date": 0 + } + }, + { + "type": "timer", + "testMs": 1000, + "data": { + "event": "step", + "timer": 1, + "drift": 0 + } + }, + { + "type": "timer", + "testMs": 1000, + "data": { + "event": "end", + "timer": 1, + "date": 0 + } + } + ], + "context": { + "targetWords": [ + [111, 110, 101, 32], + [116, 119, 111] + ], + "mode": "words", + "mode2": "2", + "koreanStatus": false, + "bailedOut": false, + "isFunboxWithNospacePropertyActive": false + } + } + } + ] +} diff --git a/packages/typing-fixtures/oracle/timer-min-speed.json b/packages/typing-fixtures/oracle/timer-min-speed.json new file mode 100644 index 000000000..b5ca14927 --- /dev/null +++ b/packages/typing-fixtures/oracle/timer-min-speed.json @@ -0,0 +1,293 @@ +{ + "name": "timer-min-speed", + "frames": [ + { + "state": { + "wordIndex": 4, + "inputValue": [], + "active": true, + "ended": false + }, + "log": { + "version": 1, + "events": [ + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [97], + "correct": true, + "wordIndex": 0, + "charIndex": 0, + "inputValue": [97] + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [32], + "correct": true, + "wordIndex": 0, + "charIndex": 1, + "inputValue": [97, 32], + "commitsWord": true + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [98], + "correct": true, + "wordIndex": 1, + "charIndex": 0, + "inputValue": [98] + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [32], + "correct": true, + "wordIndex": 1, + "charIndex": 1, + "inputValue": [98, 32], + "commitsWord": true + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [99], + "correct": true, + "wordIndex": 2, + "charIndex": 0, + "inputValue": [99] + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [32], + "correct": true, + "wordIndex": 2, + "charIndex": 1, + "inputValue": [99, 32], + "commitsWord": true + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [100], + "correct": true, + "wordIndex": 3, + "charIndex": 0, + "inputValue": [100] + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [32], + "correct": true, + "wordIndex": 3, + "charIndex": 1, + "inputValue": [100, 32], + "commitsWord": true + } + }, + { + "type": "timer", + "testMs": 0, + "data": { + "event": "start", + "timer": 0, + "date": 0 + } + } + ], + "context": { + "targetWords": [[97, 32], [98, 32], [99, 32], [100, 32], [101]], + "mode": "words", + "mode2": "5", + "koreanStatus": false, + "bailedOut": false, + "isFunboxWithNospacePropertyActive": false + } + } + }, + { + "state": { + "wordIndex": 4, + "inputValue": [], + "active": false, + "ended": true, + "reason": "min speed" + }, + "log": { + "version": 1, + "events": [ + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [97], + "correct": true, + "wordIndex": 0, + "charIndex": 0, + "inputValue": [97] + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [32], + "correct": true, + "wordIndex": 0, + "charIndex": 1, + "inputValue": [97, 32], + "commitsWord": true + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [98], + "correct": true, + "wordIndex": 1, + "charIndex": 0, + "inputValue": [98] + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [32], + "correct": true, + "wordIndex": 1, + "charIndex": 1, + "inputValue": [98, 32], + "commitsWord": true + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [99], + "correct": true, + "wordIndex": 2, + "charIndex": 0, + "inputValue": [99] + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [32], + "correct": true, + "wordIndex": 2, + "charIndex": 1, + "inputValue": [99, 32], + "commitsWord": true + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [100], + "correct": true, + "wordIndex": 3, + "charIndex": 0, + "inputValue": [100] + } + }, + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [32], + "correct": true, + "wordIndex": 3, + "charIndex": 1, + "inputValue": [100, 32], + "commitsWord": true + } + }, + { + "type": "timer", + "testMs": 0, + "data": { + "event": "start", + "timer": 0, + "date": 0 + } + }, + { + "type": "timer", + "testMs": 2000, + "data": { + "event": "step", + "timer": 1, + "catchup": true + } + }, + { + "type": "timer", + "testMs": 2000, + "data": { + "event": "step", + "timer": 2, + "drift": 1000 + } + }, + { + "type": "timer", + "testMs": 2000, + "data": { + "event": "end", + "timer": 2, + "date": 0 + } + } + ], + "context": { + "targetWords": [[97, 32], [98, 32], [99, 32], [100, 32], [101]], + "mode": "words", + "mode2": "5", + "koreanStatus": false, + "bailedOut": false, + "isFunboxWithNospacePropertyActive": false + } + } + } + ] +} From 9dce5932dd3e03cca285d499b9c62e3ec1f4f83d Mon Sep 17 00:00:00 2001 From: Lakshmi Tanmay Date: Fri, 23 Oct 2026 00:14:06 +0530 Subject: [PATCH 13/93] fix(engine): preserve Korean scoring for every legacy UTF-16 unit --- crates/typing-engine/src/stats.rs | 17 ++++++- crates/typing-engine/src/text.rs | 60 ++++++++++++++++++++++++ crates/typing-engine/tests/statistics.rs | 17 +++++++ docs/rust-engine/decisions.md | 3 ++ 4 files changed, 95 insertions(+), 2 deletions(-) diff --git a/crates/typing-engine/src/stats.rs b/crates/typing-engine/src/stats.rs index eb13aa787..425ce7f94 100644 --- a/crates/typing-engine/src/stats.rs +++ b/crates/typing-engine/src/stats.rs @@ -143,10 +143,23 @@ pub fn chars(log: &Log, partial: bool, limit: Option) -> Chars { *unit = 32; } } - let target = log.context.target_words.get(index).unwrap_or(&typed); + let target = log + .context + .target_words + .get(index) + .unwrap_or(&typed) + .clone(); + let (typed, target) = if log.context.korean_status { + ( + crate::text::disassemble_hangul(&typed), + crate::text::disassemble_hangul(&target), + ) + } else { + (typed, target) + }; let counts = count_chars( &typed, - target, + &target, index == active && (partial || timed(log) || log.context.bailed_out), ); total.all_correct += counts.all_correct; diff --git a/crates/typing-engine/src/text.rs b/crates/typing-engine/src/text.rs index f1daa7f2d..3f4c2ccd5 100644 --- a/crates/typing-engine/src/text.rs +++ b/crates/typing-engine/src/text.rs @@ -77,3 +77,63 @@ pub fn delete_word(input: &Text) -> Text { } Text(input.0[..end].to_vec()) } + +/// Match hangul-js disassemble: compatibility compound jamo split; modern jamo +/// remain unchanged. Scores count these units while event indices stay UTF-16. +pub fn disassemble_hangul(text: &Text) -> Text { + let leading: Vec<_> = "ㄱㄲㄴㄷㄸㄹㅁㅂㅃㅅㅆㅇㅈㅉㅊㅋㅌㅍㅎ" + .encode_utf16() + .collect(); + let trailing: Vec<_> = "\0ㄱㄲㄳㄴㄵㄶㄷㄹㄺㄻㄼㄽㄾㄿㅀㅁㅂㅄㅅㅆㅇㅈㅊㅋㅌㅍㅎ" + .encode_utf16() + .collect(); + fn push(unit: u16, output: &mut Vec) { + // hangul-js initializes its lookup hashes with 0 -> 0; preserve that + // historical NUL interpretation rather than silently changing scores. + if unit == 0 { + output.push(0x3131); + return; + } + let pair = match unit { + 0x3133 => Some((0x3131, 0x3145)), + 0x3135 => Some((0x3134, 0x3148)), + 0x3136 => Some((0x3134, 0x314e)), + 0x313a => Some((0x3139, 0x3131)), + 0x313b => Some((0x3139, 0x3141)), + 0x313c => Some((0x3139, 0x3142)), + 0x313d => Some((0x3139, 0x3145)), + 0x313e => Some((0x3139, 0x314c)), + 0x313f => Some((0x3139, 0x314d)), + 0x3140 => Some((0x3139, 0x314e)), + 0x3144 => Some((0x3142, 0x3145)), + 0x3158 => Some((0x3157, 0x314f)), + 0x3159 => Some((0x3157, 0x3150)), + 0x315a => Some((0x3157, 0x3163)), + 0x315d => Some((0x315c, 0x3153)), + 0x315e => Some((0x315c, 0x3154)), + 0x315f => Some((0x315c, 0x3163)), + 0x3162 => Some((0x3161, 0x3163)), + _ => None, + }; + if let Some((a, b)) = pair { + output.push(a); + output.push(b); + } else { + output.push(unit); + } + } + let mut result = vec![]; + for unit in &text.0 { + if (0xac00..=0xd7a3).contains(unit) { + let syllable = usize::from(*unit - 0xac00); + push(leading[syllable / 588], &mut result); + push(0x314f + (syllable % 588 / 28) as u16, &mut result); + if syllable % 28 != 0 { + push(trailing[syllable % 28], &mut result); + } + } else { + push(*unit, &mut result); + } + } + Text(result) +} diff --git a/crates/typing-engine/tests/statistics.rs b/crates/typing-engine/tests/statistics.rs index b53ae420e..1782afcf1 100644 --- a/crates/typing-engine/tests/statistics.rs +++ b/crates/typing-engine/tests/statistics.rs @@ -53,3 +53,20 @@ fn summary_fields_match_all_unchanged_browser_results_exactly() { } } } + +#[test] +fn hangul_disassembly_matches_frozen_original_library_for_every_utf16_unit() { + // Frozen from hangul-js 0.2.6, all 65536 individual UTF-16 code units. + // FNV-1a64 over each result's unit length followed by its UTF-16 units. + let mut hash = 14695981039346656037_u64; + let mut count = 0; + for unit in 0..=u16::MAX { + let result = typing_engine::text::disassemble_hangul(&Text(vec![unit])); + for unit in std::iter::once(result.len() as u16).chain(result.0) { + hash = (hash ^ u64::from(unit)).wrapping_mul(1099511628211); + count += 1; + } + } + assert_eq!(count, 161148); + assert_eq!(hash, 0x6b0ab93734a2fb3f); +} diff --git a/docs/rust-engine/decisions.md b/docs/rust-engine/decisions.md index d20322e9e..1550a01be 100644 --- a/docs/rust-engine/decisions.md +++ b/docs/rust-engine/decisions.md @@ -11,3 +11,6 @@ before finalizing Phase 1.3; no lower MSRV support claimed without a build. - Offline terminal initial scope excludes accounts/sync. Publication follows repository release authorization; no production deployment implied by this task. +- Korean scoring preserves hangul-js 0.2.6 disassembly, including its NUL→ㄱ + lookup quirk. An exhaustive frozen digest covers every UTF-16 unit. Correcting + that legacy interpretation requires a separately versioned behavior change. From 4e1832bc9e399174ea30511d45c1865fd25946d5 Mon Sep 17 00:00:00 2001 From: Lakshmi Tanmay Date: Fri, 23 Oct 2026 00:14:08 +0530 Subject: [PATCH 14/93] test(engine): exercise every frozen command trace in real browser Wasm --- frontend/e2e/engine-proof.migration.ts | 19 ++++++++++++++++++- knip.ts | 7 +++++++ packages/typing-engine/package.json | 5 +++++ packages/typing-engine/vite.config.ts | 9 +++++++++ 4 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 packages/typing-engine/vite.config.ts diff --git a/frontend/e2e/engine-proof.migration.ts b/frontend/e2e/engine-proof.migration.ts index 086561677..d9171b97d 100644 --- a/frontend/e2e/engine-proof.migration.ts +++ b/frontend/e2e/engine-proof.migration.ts @@ -1,4 +1,4 @@ -import { readFile, writeFile } from "node:fs/promises"; +import { readFile, readdir, writeFile } from "node:fs/promises"; import { resolve } from "node:path"; import { expect, test } from "playwright/test"; @@ -90,4 +90,21 @@ test("native fixture matches browser Wasm through built Cloudflare assets", asyn expect( JSON.parse(await page.getByTestId("engine-proof-result").innerText()), ).toEqual(expected); + for (const name of (await readdir(resolve(root, "commands"))).filter( + (entry) => entry.endsWith(".json"), + )) { + await page + .getByRole("textbox", { name: "Command fixture" }) + .fill(await readFile(resolve(root, "commands", name), "utf8")); + await page.getByRole("button", { name: "Run fixture" }).click(); + await expect(page.getByTestId("engine-proof-result")).not.toBeEmpty(); + const frozen: unknown = JSON.parse( + await readFile(resolve(root, "oracle", name), "utf8"), + ); + expect( + JSON.parse(await page.getByTestId("engine-proof-result").innerText()), + name, + ).toEqual(frozen); + await expect(page.getByTestId("engine-proof-error")).toBeEmpty(); + } }); diff --git a/knip.ts b/knip.ts index 8a222ac07..bbb7824f9 100644 --- a/knip.ts +++ b/knip.ts @@ -6,6 +6,8 @@ import process from "node:process"; process.env.TURNSTILE_SITE_KEY ??= "1x00000000000000000000AA"; export default { + // Installed through the pinned Cargo bootstrap, not a JavaScript dependency. + ignoreBinaries: ["wasm-bindgen"], workspaces: { ".": { entry: [ @@ -65,6 +67,11 @@ export default { "packages/typing-core": { entry: ["scripts/*.ts"], }, + "packages/typing-engine": { + // This package's browser-only declarations use the workspace TS base; + // Knip's default entry analysis does not inspect this tsconfig reference. + ignoreDependencies: ["@oxytype/typescript-config"], + }, "packages/oxlint-config": { entry: ["plugins/*.js"], project: ["plugins/*.js"], diff --git a/packages/typing-engine/package.json b/packages/typing-engine/package.json index 8a7ef8160..f65f47c42 100644 --- a/packages/typing-engine/package.json +++ b/packages/typing-engine/package.json @@ -6,6 +6,11 @@ "exports": { "./browser.client": "./src/browser.client.ts" }, + "scripts": { + "ts-check": "vp lint . --type-aware --type-check --format agent", + "lint": "vp lint . --type-aware --type-check --format agent", + "lint-fast": "vp lint . --format agent" + }, "devDependencies": { "@oxytype/typescript-config": "workspace:*", "vite-plus": "catalog:" diff --git a/packages/typing-engine/vite.config.ts b/packages/typing-engine/vite.config.ts new file mode 100644 index 000000000..50f4c8d73 --- /dev/null +++ b/packages/typing-engine/vite.config.ts @@ -0,0 +1,9 @@ +import { defineConfig } from "vite-plus"; +import { sharedLint } from "../oxlint-config/config"; + +export default defineConfig({ + lint: { + extends: [sharedLint], + ignorePatterns: ["generated", "node_modules"], + }, +}); From 0f497ab66cf3f1869421e73762d861d62a4d6974 Mon Sep 17 00:00:00 2001 From: Lakshmi Tanmay Date: Fri, 23 Oct 2026 00:24:40 +0530 Subject: [PATCH 15/93] feat(engine): port owned pace weak-spot learning and typing-time state --- crates/typing-engine/src/lib.rs | 2 + crates/typing-engine/src/pace.rs | 151 ++++++++++++++++++++++++ crates/typing-engine/src/weak_spot.rs | 66 +++++++++++ crates/typing-engine/tests/pace.rs | 67 +++++++++++ crates/typing-engine/tests/weak_spot.rs | 16 +++ 5 files changed, 302 insertions(+) create mode 100644 crates/typing-engine/src/pace.rs create mode 100644 crates/typing-engine/src/weak_spot.rs create mode 100644 crates/typing-engine/tests/pace.rs create mode 100644 crates/typing-engine/tests/weak_spot.rs diff --git a/crates/typing-engine/src/lib.rs b/crates/typing-engine/src/lib.rs index 185ab9fc1..a90c28b7c 100644 --- a/crates/typing-engine/src/lib.rs +++ b/crates/typing-engine/src/lib.rs @@ -1,7 +1,9 @@ //! Platform-independent typing rules. Hosts provide content, observations and time. +pub mod pace; pub mod session; pub mod stats; pub mod text; +pub mod weak_spot; pub use session::{Command, Config, Session}; pub use text::Text; diff --git a/crates/typing-engine/src/pace.rs b/crates/typing-engine/src/pace.rs new file mode 100644 index 000000000..be77a8074 --- /dev/null +++ b/crates/typing-engine/src/pace.rs @@ -0,0 +1,151 @@ +use crate::Text; +use serde::Serialize; +use std::collections::BTreeSet; +use ts_rs::TS; + +#[derive(Clone, Debug, Serialize, TS, PartialEq)] +#[serde(rename_all = "camelCase")] +pub struct Position { + pub word_index: usize, + pub letter_index: usize, + pub wpm: f64, +} +#[derive(Default)] +pub struct Pace { + wpm: Option, + word_index: usize, + letter_index: usize, + correction: isize, + mistakes: BTreeSet, + start_ms: f64, + steps: u64, + last_wpm: f64, +} +impl Pace { + pub fn reset(&mut self, wpm: Option) -> bool { + self.wpm = wpm.filter(|value| value.is_finite() && *value >= 1.0); + self.word_index = 0; + self.letter_index = 0; + self.correction = 0; + self.mistakes.clear(); + self.start_ms = 0.0; + self.steps = 0; + self.wpm.is_some() + } + pub fn start(&mut self, now: f64) { + self.start_ms = now; + self.steps = 0; + } + pub fn position(&self) -> Option { + self.wpm.map(|wpm| Position { + word_index: self.word_index, + letter_index: self.letter_index, + wpm, + }) + } + pub fn correct(&mut self, index: usize, correct: bool, word: &Text, blind: bool) { + if self.wpm.is_none() || blind { + return; + } + if correct { + if self.mistakes.remove(&index) { + self.correction -= word.len() as isize; + } + } else if self.mistakes.insert(index) { + self.correction += word.len() as isize; + } + } + pub fn step(&mut self, words: &[Text], blind: bool) -> Option { + self.wpm?; + let Some(word) = words.get(self.word_index) else { + self.wpm = None; + return None; + }; + if self.letter_index >= word.len() { + self.letter_index = 0; + self.word_index += 1; + } else { + self.letter_index += 1; + } + if !blind { + while self.correction < 0 { + if self.letter_index == 0 { + let Some(previous) = self + .word_index + .checked_sub(1) + .and_then(|index| words.get(index)) + else { + self.wpm = None; + return None; + }; + self.word_index -= 1; + self.letter_index = previous.len(); + } else { + self.letter_index -= 1; + } + self.correction += 1; + } + while self.correction > 0 { + let Some(word) = words.get(self.word_index) else { + self.wpm = None; + return None; + }; + self.letter_index += 1; + if self.letter_index > word.len() { + self.letter_index = 0; + self.word_index += 1; + } + self.correction -= 1; + } + } + self.steps += 1; + self.position() + } + pub fn advance(&mut self, now: f64, words: &[Text], blind: bool) -> Option { + let speed = self.wpm?; + let due = ((now - self.start_ms) / (60.0 / (speed * 5.0) * 1000.0)).floor(); + while (self.steps as f64) < due { + self.step(words, blind)?; + } + self.position() + } + pub fn step_end(&self) -> f64 { + self.start_ms + + self.steps as f64 * self.wpm.map(|wpm| 60.0 / (wpm * 5.0)).unwrap_or(0.0) * 1000.0 + } + pub fn remember(&mut self, wpm: f64, repeating: bool) { + if !repeating || wpm > self.last_wpm { + self.last_wpm = wpm; + } + } + pub fn last_wpm(&self) -> f64 { + self.last_wpm + } +} + +#[derive(Default)] +pub struct TypingTime { + seconds: f64, + day: Option, +} +impl TypingTime { + /// The host supplies its local-calendar day token; no hidden system clock. + pub fn add(&mut self, seconds: f64, today: i64) { + if self.day.is_some_and(|day| day != today) { + self.day = Some(today); + self.seconds = seconds; + } else { + self.seconds += seconds; + } + } + pub fn load_today(&mut self, seconds: f64, today: i64) { + self.day = Some(today); + self.seconds = seconds; + } + pub fn seconds(&self) -> f64 { + self.seconds + } + pub fn is_today(&self) -> bool { + self.day.is_some() + } +} diff --git a/crates/typing-engine/src/weak_spot.rs b/crates/typing-engine/src/weak_spot.rs new file mode 100644 index 000000000..bcb2b8a05 --- /dev/null +++ b/crates/typing-engine/src/weak_spot.rs @@ -0,0 +1,66 @@ +use crate::Text; +use std::collections::BTreeMap; + +#[derive(Default)] +pub struct WeakSpot { + scores: BTreeMap, Score>, +} +#[derive(Default)] +struct Score { + average: f64, + count: u32, +} +impl WeakSpot { + pub fn update(&mut self, text: &Text, correct: bool, spacing: Option) { + let Some(spacing) = spacing else { + return; + }; + let score = self.scores.entry(text.0.clone()).or_default(); + score.count = (score.count + 1).min(50); + let rate = 1.0 / f64::from(score.count); + let value = spacing + if correct { 0.0 } else { 5000.0 }; + score.average = value * rate + score.average * (1.0 - rate); + } + fn score(&self, word: &Text) -> f64 { + let mut total = 0.0; + let mut count = 0; + let mut index = 0; + // Match JS for-of code-point iteration when scoring candidate words. + while index < word.len() { + let width = if (0xd800..=0xdbff).contains(&word.0[index]) + && word + .0 + .get(index + 1) + .is_some_and(|c| (0xdc00..=0xdfff).contains(c)) + { + 2 + } else { + 1 + }; + if let Some(score) = self.scores.get(&word.0[index..index + width]) { + total += score.average; + count += 1; + } + index += width; + } + if count == 0 { + 0.0 + } else { + total / f64::from(count) + } + } + /// Generator supplies the same twenty RNG-selected candidates; ties retain + /// the earliest sample, exactly as the browser learner does. + pub fn best<'a>(&self, candidates: &'a [Text]) -> Option<&'a Text> { + let mut best = None; + let mut high = 0.0; + for candidate in candidates { + let score = self.score(candidate); + if best.is_none() || score > high { + best = Some(candidate); + high = score; + } + } + best + } +} diff --git a/crates/typing-engine/tests/pace.rs b/crates/typing-engine/tests/pace.rs new file mode 100644 index 000000000..feeaf4a14 --- /dev/null +++ b/crates/typing-engine/tests/pace.rs @@ -0,0 +1,67 @@ +use typing_engine::{ + Text, + pace::{Pace, Position, TypingTime}, +}; +#[test] +fn pace_corrects_each_error_once_and_remembers_repeated_speed() { + let words = vec![Text::from("abc"), Text::from("def"), Text::from("ghi")]; + let mut pace = Pace::default(); + assert!(pace.reset(Some(120.0))); + pace.correct(0, false, &Text::from("abc "), false); + pace.correct(0, false, &Text::from("abc "), false); + assert_eq!( + pace.step(&words, false), + Some(Position { + word_index: 1, + letter_index: 1, + wpm: 120.0 + }) + ); + pace.correct(0, true, &Text::from("abc "), false); + assert_eq!( + pace.step(&words, false), + Some(Position { + word_index: 0, + letter_index: 2, + wpm: 120.0 + }) + ); + pace.remember(70.0, false); + pace.remember(40.0, true); + assert_eq!(pace.last_wpm(), 70.0); + pace.remember(90.0, true); + assert!(!pace.reset(None)); + assert_eq!(pace.last_wpm(), 90.0); +} +#[test] +fn pace_catches_up_from_start_then_disables_when_out_of_words() { + let words = vec![Text::from("hi"), Text::from("you"), Text::from("all")]; + let mut pace = Pace::default(); + pace.reset(Some(60.0)); + pace.start(0.0); + assert_eq!(pace.advance(199.0, &words, false).unwrap().letter_index, 0); + assert_eq!( + pace.advance(1200.0, &words, false), + Some(Position { + word_index: 1, + letter_index: 3, + wpm: 60.0 + }) + ); + assert!(pace.advance(5000.0, &words, false).is_none()); + assert!(!pace.reset(Some(f64::INFINITY))); +} +#[test] +fn typing_time_uses_explicit_local_day_and_preserves_session_before_load() { + let mut time = TypingTime::default(); + time.add(10.0, 1); + time.add(10.0, 2); + assert_eq!(time.seconds(), 20.0); + assert!(!time.is_today()); + time.load_today(10.0, 2); + time.add(5.0, 2); + assert_eq!(time.seconds(), 15.0); + time.add(10.0, 3); + time.add(5.0, 3); + assert_eq!(time.seconds(), 15.0); +} diff --git a/crates/typing-engine/tests/weak_spot.rs b/crates/typing-engine/tests/weak_spot.rs new file mode 100644 index 000000000..0c5b946a6 --- /dev/null +++ b/crates/typing-engine/tests/weak_spot.rs @@ -0,0 +1,16 @@ +use typing_engine::{Text, weak_spot::WeakSpot}; +#[test] +fn learner_penalizes_errors_and_remains_isolated() { + let candidates = vec![Text::from("a"), Text::from("x")]; + let mut learner = WeakSpot::default(); + learner.update(&Text::from("x"), false, Some(100.0)); + assert_eq!(learner.best(&candidates), Some(&candidates[1])); + assert_eq!(WeakSpot::default().best(&candidates), Some(&candidates[0])); +} +#[test] +fn unknown_spacing_is_not_learned_as_measured_zero() { + let candidates = vec![Text::from("a"), Text::from("x")]; + let mut learner = WeakSpot::default(); + learner.update(&Text::from("x"), false, None); + assert_eq!(learner.best(&candidates), Some(&candidates[0])); +} From e95c2957c5bf8c3fc9bc7d07920fcad4e1727902 Mon Sep 17 00:00:00 2001 From: Lakshmi Tanmay Date: Fri, 23 Oct 2026 00:24:45 +0530 Subject: [PATCH 16/93] feat(engine): match frozen Mulberry32 streams and explicit RNG exhaustion --- crates/typing-engine/src/lib.rs | 1 + crates/typing-engine/src/random.rs | 64 ++++++++++++++++++ crates/typing-engine/tests/random.rs | 28 ++++++++ .../oracle/random-streams.json | 67 +++++++++++++++++++ 4 files changed, 160 insertions(+) create mode 100644 crates/typing-engine/src/random.rs create mode 100644 crates/typing-engine/tests/random.rs create mode 100644 packages/typing-fixtures/oracle/random-streams.json diff --git a/crates/typing-engine/src/lib.rs b/crates/typing-engine/src/lib.rs index a90c28b7c..34375a90b 100644 --- a/crates/typing-engine/src/lib.rs +++ b/crates/typing-engine/src/lib.rs @@ -1,5 +1,6 @@ //! Platform-independent typing rules. Hosts provide content, observations and time. pub mod pace; +pub mod random; pub mod session; pub mod stats; pub mod text; diff --git a/crates/typing-engine/src/random.rs b/crates/typing-engine/src/random.rs new file mode 100644 index 000000000..20d5d90cd --- /dev/null +++ b/crates/typing-engine/src/random.rs @@ -0,0 +1,64 @@ +use serde::{Deserialize, Serialize}; + +/// Stable Mulberry32 stream, matching the original generator fixtures. No host +/// RNG, hidden clock or library-selected algorithm participates in generation. +#[derive(Clone, Debug, Deserialize, Serialize)] +#[serde(tag = "kind", rename_all = "camelCase")] +pub enum Random { + Seeded { state: u32 }, + Stream { values: Vec, cursor: usize }, +} +impl Random { + pub fn seeded(seed: u32) -> Self { + Self::Seeded { state: seed } + } + pub fn stream(values: Vec) -> Result { + if values + .iter() + .any(|value| !value.is_finite() || !(0.0..1.0).contains(value)) + { + return Err("Random stream values must be finite and in [0,1)".into()); + } + Ok(Self::Stream { values, cursor: 0 }) + } + pub fn draw(&mut self) -> Result { + match self { + Self::Seeded { state } => { + *state = state.wrapping_add(0x6d2b79f5); + let mut t = (*state ^ (*state >> 15)).wrapping_mul(1 | *state); + t = t.wrapping_add((t ^ (t >> 7)).wrapping_mul(61 | t)) ^ t; + Ok(f64::from(t ^ (t >> 14)) / 4294967296.0) + } + Self::Stream { values, cursor } => { + let value = *values.get(*cursor).ok_or("Random stream exhausted")?; + *cursor += 1; + Ok(value) + } + } + } + pub fn index(&mut self, length: usize) -> Result { + if length == 0 { + return Err("Cannot choose from empty content".into()); + } + Ok((self.draw()? * length as f64).floor() as usize) + } + pub fn zipf_index(&mut self, length: usize) -> Result { + if length == 0 { + return Err("Cannot choose from empty content".into()); + } + let gamma = std::f64::consts::EULER_GAMMA; + let harmonic = (length as f64 + 0.5).ln() + gamma; + let index = ((self.draw()? * harmonic - gamma).exp() - 0.5).floor(); + if index < 0.0 || index >= length as f64 { + return Err("Zipf draw outside wordset".into()); + } + Ok(index as usize) + } + pub fn shuffle(&mut self, values: &mut [T]) -> Result<(), String> { + for index in (1..values.len()).rev() { + let other = self.index(index + 1)?; + values.swap(index, other); + } + Ok(()) + } +} diff --git a/crates/typing-engine/tests/random.rs b/crates/typing-engine/tests/random.rs new file mode 100644 index 000000000..a9337af4e --- /dev/null +++ b/crates/typing-engine/tests/random.rs @@ -0,0 +1,28 @@ +use typing_engine::random::Random; +#[test] +fn seeded_draw_order_matches_frozen_javascript_mulberry32() { + let fixtures: serde_json::Value = serde_json::from_str(include_str!( + "../../../packages/typing-fixtures/oracle/random-streams.json" + )) + .unwrap(); + for (seed, values) in fixtures.as_object().unwrap() { + let mut rng = Random::seeded(seed.parse().unwrap()); + for expected in values.as_array().unwrap() { + assert_eq!( + rng.draw().unwrap(), + expected.as_f64().unwrap(), + "seed {seed}" + ); + } + } +} +#[test] +fn explicit_streams_reject_invalid_values_and_never_reseed_on_exhaustion() { + assert!(Random::stream(vec![f64::NAN]).is_err()); + assert!(Random::stream(vec![1.0]).is_err()); + let mut rng = Random::stream(vec![0.0, 0.5, 0.999]).unwrap(); + assert_eq!(rng.index(4).unwrap(), 0); + assert_eq!(rng.index(4).unwrap(), 2); + assert_eq!(rng.index(4).unwrap(), 3); + assert!(rng.draw().is_err()); +} diff --git a/packages/typing-fixtures/oracle/random-streams.json b/packages/typing-fixtures/oracle/random-streams.json new file mode 100644 index 000000000..d9c447b87 --- /dev/null +++ b/packages/typing-fixtures/oracle/random-streams.json @@ -0,0 +1,67 @@ +{ + "0": [ + 0.26642920868471265, 0.0003297457005828619, 0.2232720274478197, + 0.1462021479383111, 0.46732782293111086, 0.5450490827206522, + 0.6152513844426721, 0.6489853798411787, 0.45600721263326705, + 0.581218967679888, 0.24625753005966544, 0.09785169479437172, + 0.9034309543203562, 0.40820372267626226, 0.7202955442480743, + 0.6599067240022123, 0.8058585098478943, 0.6261914535425603, + 0.5696913106366992, 0.12123633874580264, 0.24193100933916867, + 0.8153759187553078, 0.323521607555449, 0.08783002896234393, + 0.7895766077563167, 0.9079886567778885, 0.551719788229093, + 0.4687971824314445, 0.3561653404030949, 0.09744705795310438, + 0.752272559562698, 0.52662223437801 + ], + "1": [ + 0.6270739405881613, 0.002735721180215478, 0.5274470399599522, + 0.9810509674716741, 0.9683778982143849, 0.281103502959013, + 0.6128388606011868, 0.7207431411370635, 0.425796952098608, + 0.9948229456786066, 0.45527112716808915, 0.48878831742331386, + 0.13893358339555562, 0.40374476509168744, 0.24752389639616013, + 0.15449313493445516, 0.48914389545097947, 0.0673698834143579, + 0.3946193160954863, 0.7671080259606242, 0.28586167353205383, + 0.19087357842363417, 0.042729709995910525, 0.42738195578567684, + 0.590382493333891, 0.7986842156387866, 0.29486987087875605, + 0.2054697866551578, 0.6581296445801854, 0.5460823616012931, + 0.758442840538919, 0.19281403115019202 + ], + "42": [ + 0.6011037519201636, 0.44829055899754167, 0.8524657934904099, + 0.6697340414393693, 0.17481389874592423, 0.5265925421845168, + 0.2732279943302274, 0.6247446539346129, 0.8654746483080089, + 0.4723170551005751, 0.24992373422719538, 0.8820588334929198, + 0.7457375649828464, 0.3070015134289861, 0.19725383794866502, + 0.5007294877432287, 0.6866120179183781, 0.6106208984274417, + 0.003842951962724328, 0.47078192373737693, 0.8373374259099364, + 0.05120926629751921, 0.5923239905387163, 0.03153795562684536, + 0.2669559868518263, 0.06178139243274927, 0.18568900716491044, + 0.7835472931619734, 0.530335606308654, 0.027123609324917197, + 0.17300523445010185, 0.8426881253253669 + ], + "2147483648": [ + 0.8205775609239936, 0.4481089550536126, 0.7836112855002284, + 0.5120457962621003, 0.8388098266441375, 0.4205148529727012, + 0.8944806265644729, 0.9326926763169467, 0.6747888037934899, + 0.4173516221344471, 0.7520525439176708, 0.5010397543665022, + 0.3403281669598073, 0.9581121851224452, 0.13500668411143124, + 0.6329791664611548, 0.4525297924410552, 0.9778232092503458, + 0.8422936180140823, 0.22736566024832428, 0.2501462288200855, + 0.5457336774561554, 0.13329570461064577, 0.9689932239707559, + 0.7632795765530318, 0.049264932284131646, 0.8356091578025371, + 0.498972142348066, 0.7754034039098769, 0.7437046824488789, + 0.6968864763621241, 0.5806966486852616 + ], + "4294967295": [ + 0.8964226141106337, 0.189478256739676, 0.7156526781618595, + 0.9440599093213677, 0.8452364315744489, 0.5391399988438934, + 0.6804977387655526, 0.4755720964167267, 0.13585773925296962, + 0.9884445976931602, 0.8714674517977983, 0.18934845877811313, + 0.3436866346746683, 0.7386848733294755, 0.4759919897187501, + 0.7567116804420948, 0.9159235202241689, 0.10207696096040308, + 0.1436101261060685, 0.3921705954708159, 0.9202409950084984, + 0.9238722093869001, 0.7916392679326236, 0.8520552832633257, + 0.1153229579795152, 0.09125144220888615, 0.5151041666977108, + 0.2751815707888454, 0.3076603573281318, 0.01972890179604292, + 0.4548310365062207, 0.2538063288666308 + ] +} From 38e98e9487024c6fdee2c7cbe677448fdcd563bf Mon Sep 17 00:00:00 2001 From: Lakshmi Tanmay Date: Fri, 23 Oct 2026 00:24:49 +0530 Subject: [PATCH 17/93] feat(engine): preserve browser commit composition and deferred input lifecycle --- crates/typing-engine/src/session.rs | 126 ++++++++++++++++-- crates/typing-engine/tests/observations.rs | 81 +++++++++++ crates/typing-engine/tests/recordings.rs | 25 ++++ .../e2e/engine-input-contract.migration.ts | 34 +++++ 4 files changed, 258 insertions(+), 8 deletions(-) create mode 100644 crates/typing-engine/tests/observations.rs create mode 100644 frontend/e2e/engine-input-contract.migration.ts diff --git a/crates/typing-engine/src/session.rs b/crates/typing-engine/src/session.rs index 7449ba42a..e12d4059a 100644 --- a/crates/typing-engine/src/session.rs +++ b/crates/typing-engine/src/session.rs @@ -4,7 +4,7 @@ use crate::{ }; use serde::{Deserialize, Serialize}; use serde_json::{Value, json}; -use std::collections::BTreeMap; +use std::collections::{BTreeMap, VecDeque}; use ts_rs::TS; #[derive(Clone, Debug, Deserialize, Serialize, TS)] @@ -121,6 +121,18 @@ pub enum Command { Finalize { now: f64, }, + #[serde(rename_all = "camelCase")] + View { + now: f64, + #[serde(default)] + earliest_visible_word: Option, + #[serde(default)] + defer_automatic: Option, + }, + FlushAutomatic { + now: f64, + generation: u32, + }, } #[derive(Clone, Debug, Serialize, Deserialize, TS)] @@ -148,6 +160,9 @@ pub struct Event { pub struct Update { pub state: State, pub events: Vec, + pub generation: u32, + #[serde(rename = "automaticPending")] + pub automatic_pending: bool, } #[derive(Clone)] @@ -158,6 +173,8 @@ struct Recorded { } pub struct Session { + pub pace: crate::pace::Pace, + pub weak_spot: crate::weak_spot::WeakSpot, config: Config, words: Vec, input: Vec, @@ -171,6 +188,12 @@ pub struct Session { indent_pending: bool, pressed: Vec<(String, f64)>, no_code_index: usize, + completion_checkpoint: bool, + composing: bool, + earliest_visible_word: usize, + defer_automatic: bool, + automatic: VecDeque<(f64, f64)>, + generation: u32, } impl Session { @@ -182,6 +205,8 @@ impl Session { return Err("Invalid time limit".into()); } Ok(Self { + pace: crate::pace::Pace::default(), + weak_spot: crate::weak_spot::WeakSpot::default(), config, input: vec![Text::default(); words.len().max(1)], words, @@ -195,6 +220,12 @@ impl Session { indent_pending: false, pressed: vec![], no_code_index: 0, + completion_checkpoint: true, + composing: false, + earliest_visible_word: 0, + defer_automatic: false, + automatic: VecDeque::new(), + generation: 0, }) } @@ -231,6 +262,7 @@ impl Session { } self.active = true; self.start = Some(now); + self.pace.start(now); self.record( "timer", now, @@ -258,7 +290,9 @@ impl Session { | Command::Abandon { now, .. } | Command::Reset { now } | Command::Observe { now, .. } - | Command::Finalize { now } => *now, + | Command::Finalize { now } + | Command::View { now, .. } + | Command::FlushAutomatic { now, .. } => *now, }; if !now.is_finite() || now.abs() > 9_007_199_254_740_991.0 { return Err("Invalid timestamp".into()); @@ -273,10 +307,17 @@ impl Session { automatic, is_composition_ending, } => { - for unit in data.0 { + if is_composition_ending == Some(true) { + self.composing = false; + } + let length = data.len(); + for (index, unit) in data.0.into_iter().enumerate() { if self.ended { break; } + // The browser checks failure/finish at the final unit of a + // committed input batch, not after each surrogate/character. + self.completion_checkpoint = index + 1 == length && !self.composing; self.insert( unit, now, @@ -286,6 +327,7 @@ impl Session { is_composition_ending == Some(true), ); } + self.completion_checkpoint = true; } Command::Delete { kind, now, .. } => { if !["deleteContentBackward", "deleteWordBackward"].contains(&kind.as_str()) { @@ -311,6 +353,11 @@ impl Session { self.input = vec![Text::default(); self.words.len().max(1)]; self.pressed.clear(); self.no_code_index = 0; + self.pace.reset(None); + self.composing = false; + self.earliest_visible_word = 0; + self.automatic.clear(); + self.generation = self.generation.wrapping_add(1); } Command::Observe { now, @@ -318,6 +365,26 @@ impl Session { event_data, } => self.observe(&event_type, now, event_data)?, Command::Finalize { .. } => self.finalize(), + Command::View { + earliest_visible_word, + defer_automatic, + .. + } => { + self.earliest_visible_word = earliest_visible_word.unwrap_or(0); + if let Some(defer) = defer_automatic { + self.defer_automatic = defer; + } + } + Command::FlushAutomatic { generation, .. } => { + if generation == self.generation + && !self.ended + && let Some((now, date)) = self.automatic.pop_front() + { + self.completion_checkpoint = !self.composing; + self.insert(9, now, date, None, true, false); + self.completion_checkpoint = true; + } + } } let origin = self.origin(); let events = self @@ -330,6 +397,8 @@ impl Session { Ok(Update { state: self.state(), events, + generation: self.generation, + automatic_pending: !self.automatic.is_empty(), }) } @@ -343,9 +412,19 @@ impl Session { composition: bool, ) { self.insert_one(unit, now, date, shift, automatic, composition); + if self.defer_automatic && self.indent_pending { + self.indent_pending = false; + self.automatic.push_back((now, date)); + return; + } while self.indent_pending && !self.ended { self.indent_pending = false; self.insert_one(9, now, date, None, true, false); + if self.defer_automatic && self.indent_pending { + self.indent_pending = false; + self.automatic.push_back((now, date)); + break; + } } } @@ -444,12 +523,28 @@ impl Session { } self.input .resize(self.input.len().max(self.index + 1), Text::default()); + if advance { + self.pace + .correct(self.index, next == target, &target, self.config.blind_mode); + } self.input[self.index] = next; + let previous = self + .events + .iter() + .rev() + .find(|event| event.kind == "input") + .map(|event| event.now); self.record("input", now, data); + self.weak_spot.update( + &Text(vec![unit]), + correct, + previous.map(|previous| round2(round2(now) - previous)), + ); if !correct && !stopped && self.config.delete_on_error != "off" { self.delete_error(now); } - if self.config.mode != "zen" + if self.completion_checkpoint + && self.config.mode != "zen" && ((self.config.difficulty == "master" && !correct) || (self.config.difficulty == "expert" && commit @@ -459,7 +554,7 @@ impl Session { self.end(now, date, Some("difficulty".into())); return; } - if advance && self.config.min_burst != "off" { + if self.completion_checkpoint && advance && self.config.min_burst != "off" { let log: crate::stats::Log = serde_json::from_value(self.log()).expect("Engine log is valid"); let burst = crate::stats::word_burst(&log, self.index, now - self.start.unwrap_or(now)); @@ -490,7 +585,11 @@ impl Session { && target.len() == input.len() + 1 && self.config.stop_on_error == "off" && self.config.delete_on_error == "off"; - if self.index + 1 >= self.words.len() && finite && (word_correct || quick || advance) { + if self.completion_checkpoint + && self.index + 1 >= self.words.len() + && finite + && (word_correct || quick || advance) + { self.end(now, date, None); } else if advance && (self.index + 1 < self.words.len() || self.config.mode == "zen") { self.index += 1; @@ -551,7 +650,10 @@ impl Session { ); } } - if self.config.delete_on_error.contains("hard") && input.len() <= 1 && self.index > 0 { + if self.config.delete_on_error.contains("hard") + && input.len() <= 1 + && self.index > self.earliest_visible_word + { self.index -= 1; let previous = self.previous_input(kind); self.log_delete(kind, now, previous.len(), previous, true); @@ -563,7 +665,7 @@ impl Session { return; } let input = self.current(); - if input.is_empty() && self.index == 0 { + if input.is_empty() && self.index <= self.earliest_visible_word { return; } let prev = self @@ -775,6 +877,14 @@ impl Session { data["code"] = json!(key); self.record(kind, now, data); } else if kind == "composition" || kind == "timer" { + if kind == "composition" { + if data["event"] == "start" { + self.composing = true; + } else if data["event"] == "end" { + self.composing = false; + } + data["wordIndex"] = json!(self.index); + } self.record(kind, now, data); } else { return Err("Unsupported observation type".into()); diff --git a/crates/typing-engine/tests/observations.rs b/crates/typing-engine/tests/observations.rs new file mode 100644 index 000000000..a618abb05 --- /dev/null +++ b/crates/typing-engine/tests/observations.rs @@ -0,0 +1,81 @@ +use serde_json::json; +use typing_engine::{Session, Text}; +fn apply(session: &mut Session, value: serde_json::Value) -> typing_engine::session::Update { + session + .apply(serde_json::from_value(value).unwrap()) + .unwrap() +} +#[test] +fn visible_word_bound_blocks_backtracking_even_with_freedom() { + let mut session = Session::new( + serde_json::from_value(json!({"mode":"words","words":3,"freedomMode":true})).unwrap(), + vec![Text::from("a "), Text::from("b "), Text::from("c")], + ) + .unwrap(); + apply(&mut session, json!({"op":"insert","data":[97,32],"now":0})); + apply( + &mut session, + json!({"op":"view","earliestVisibleWord":1,"now":10}), + ); + apply( + &mut session, + json!({"op":"delete","kind":"deleteContentBackward","now":20}), + ); + assert_eq!(session.state().word_index, 1); + assert!(session.state().input_value.is_empty()); +} +#[test] +fn deferred_indentation_retains_capture_time_and_reset_invalidates_old_tasks() { + let mut session = Session::new( + serde_json::from_value(json!({"mode":"words","words":2,"language":"code_javascript"})) + .unwrap(), + vec![Text::from("a\n"), Text::from("\t\tb")], + ) + .unwrap(); + apply( + &mut session, + json!({"op":"view","deferAutomatic":true,"now":0}), + ); + let update = apply( + &mut session, + json!({"op":"insert","data":[97,10],"now":100}), + ); + assert!(update.automatic_pending); + assert!(session.state().input_value.is_empty()); + let next = apply( + &mut session, + json!({"op":"flushAutomatic","generation":update.generation,"now":200}), + ); + assert_eq!(session.state().input_value, Text::from("\t")); + assert!(next.automatic_pending); + assert!(next.events.iter().all(|event| event.test_ms == 0.0)); + apply(&mut session, json!({"op":"reset","now":250})); + let stale = apply( + &mut session, + json!({"op":"flushAutomatic","generation":update.generation,"now":300}), + ); + assert!(stale.events.is_empty()); + assert!(!stale.automatic_pending); + assert!(!session.state().active); +} +#[test] +fn composition_defers_difficulty_until_the_committed_end() { + let mut session = Session::new( + serde_json::from_value(json!({"mode":"words","words":1,"difficulty":"master"})).unwrap(), + vec![Text::from("abc")], + ) + .unwrap(); + apply( + &mut session, + json!({"op":"observe","eventType":"composition","eventData":{"event":"start","wordIndex":99},"now":0}), + ); + apply(&mut session, json!({"op":"insert","data":[120],"now":10})); + assert!(!session.state().ended); + apply( + &mut session, + json!({"op":"insert","data":[121],"isCompositionEnding":true,"now":20}), + ); + assert!(session.state().ended); + assert_eq!(session.state().reason.as_deref(), Some("difficulty")); + assert_eq!(session.log()["events"][0]["data"]["wordIndex"], 0); +} diff --git a/crates/typing-engine/tests/recordings.rs b/crates/typing-engine/tests/recordings.rs index 5d83761cc..004914ec3 100644 --- a/crates/typing-engine/tests/recordings.rs +++ b/crates/typing-engine/tests/recordings.rs @@ -127,3 +127,28 @@ fn absent_physical_observations_do_not_create_key_durations() { .all(|event| event["type"] != "keyup" && event["type"] != "keydown") ); } + +#[test] +fn master_failure_checks_final_unit_as_recorded_by_actual_browser_handler() { + let mut session = Session::new( + serde_json::from_value(json!({"mode":"words","words":2,"difficulty":"master"})).unwrap(), + vec![Text::from("one "), Text::from("two")], + ) + .unwrap(); + session + .apply( + serde_json::from_value(json!({"op":"insert","data":[120,111,110,101,32],"now":100})) + .unwrap(), + ) + .unwrap(); + let log = session.log(); + let inputs: Vec<_> = log["events"] + .as_array() + .unwrap() + .iter() + .filter(|e| e["type"] == "input") + .collect(); + assert_eq!(inputs.len(), 5); + assert_eq!(session.state().reason.as_deref(), Some("difficulty")); + assert!(session.state().ended); +} diff --git a/frontend/e2e/engine-input-contract.migration.ts b/frontend/e2e/engine-input-contract.migration.ts new file mode 100644 index 000000000..3290007e5 --- /dev/null +++ b/frontend/e2e/engine-input-contract.migration.ts @@ -0,0 +1,34 @@ +import type { EventLog } from "@oxytype/typing-core/events/types"; +import { expect, openTest, test } from "./fixtures"; + +test.use({ + config: { + mode: "words", + words: 15, + difficulty: "master", + smoothCaret: "off", + }, +}); + +test("master failure waits for the final unit of a multi-character browser commit", async ({ + page, +}) => { + await openTest(page); + const first = await page.locator("#words .word").first().textContent(); + if (first === null) throw new Error("Missing target"); + await page.locator("#words").click(); + const batch = `x${first} `; + await page.keyboard.insertText(batch); + await expect(page.locator("#result")).toBeVisible(); + const log = await page.evaluate(() => + (window as unknown as { lastEventLog: () => EventLog }).lastEventLog(), + ); + const inputs = log.events.filter((event) => event.type === "input"); + expect(inputs).toHaveLength(batch.length); + expect( + inputs + .map((event) => ("data" in event.data ? event.data.data : "")) + .join(""), + ).toBe(batch); + expect(inputs.at(-1)?.data.wordIndex).toBe(0); +}); From 4a59d28938e474ede0e5fdabaa0db5590731249a Mon Sep 17 00:00:00 2001 From: Lakshmi Tanmay Date: Fri, 23 Oct 2026 00:31:26 +0530 Subject: [PATCH 18/93] ci(engine): verify Rust path routing and allow the requested PR stack --- .github/workflows/oxytype-ci.yml | 6 +++--- packages/release/__tests__/ci-routing.spec.js | 14 ++++++++++++++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/.github/workflows/oxytype-ci.yml b/.github/workflows/oxytype-ci.yml index f4db0d57d..1834d4e31 100644 --- a/.github/workflows/oxytype-ci.yml +++ b/.github/workflows/oxytype-ci.yml @@ -9,7 +9,7 @@ permissions: on: pull_request: - branches: [main, "stack/**"] + branches: [main, "stack/**", "t3/rust-engine-migration"] types: [opened, reopened, synchronize, ready_for_review] push: branches: [main] @@ -184,10 +184,10 @@ jobs: enable start-browser "${runtime[@]}" frontend start-tests browser-tests static-delivery enable start-updates "${runtime[@]}" frontend start-tests update-tests static-delivery enable start-delivery "${runtime[@]}" frontend static-delivery - enable packages packages lint-config static-layouts static-challenges + enable packages packages lint-config static-layouts static-challenges engine enable assets assets runtime-packages enable code code - enable engine engine runtime-packages browser-tests + enable engine engine runtime-packages engine: name: engine diff --git a/packages/release/__tests__/ci-routing.spec.js b/packages/release/__tests__/ci-routing.spec.js index f34104a69..10f3f10be 100644 --- a/packages/release/__tests__/ci-routing.spec.js +++ b/packages/release/__tests__/ci-routing.spec.js @@ -69,6 +69,7 @@ const allJobs = [ "packages", "assets", "code", + "engine", ]; const appJobs = [ "backend-test", @@ -116,6 +117,19 @@ it("runs every job for force-full-ci and main or manual runs", () => { expect(selected([], { full: true })).toEqual(allJobs); }); +it.each([ + "Cargo.toml", + "Cargo.lock", + "rust-toolchain.toml", + "crates/typing-engine/src/session.rs", + "crates/typing-engine-wasm/src/lib.rs", + "packages/typing-engine/src/browser.client.ts", + "packages/typing-fixtures/commands/finite-edit.json", + "scripts/engine.ts", +])("runs native, Wasm, parity and Cloudflare consumers for %s", (file) => { + expect(selected([file])).toEqual(allJobs); +}); + it.each([ "README.md", "docs/CI.md", From 60e451bb947f72cefa56d54cb8cc713eff2db6bb Mon Sep 17 00:00:00 2001 From: Lakshmi Tanmay Date: Fri, 23 Oct 2026 00:31:28 +0530 Subject: [PATCH 19/93] feat(engine): port base generation custom sections shuffle and repeats --- crates/typing-engine/src/generation.rs | 457 +++++++++++++++++++++++ crates/typing-engine/src/lib.rs | 1 + crates/typing-engine/tests/generation.rs | 109 ++++++ 3 files changed, 567 insertions(+) create mode 100644 crates/typing-engine/src/generation.rs create mode 100644 crates/typing-engine/tests/generation.rs diff --git a/crates/typing-engine/src/generation.rs b/crates/typing-engine/src/generation.rs new file mode 100644 index 000000000..0f8d490a4 --- /dev/null +++ b/crates/typing-engine/src/generation.rs @@ -0,0 +1,457 @@ +use crate::{Text, random::Random}; +use serde::{Deserialize, Serialize}; +use std::collections::VecDeque; + +#[derive(Clone, Debug, Default, Deserialize)] +#[serde(default, rename_all = "camelCase")] +pub struct Language { + pub name: String, + pub words: Vec, + pub right_to_left: Option, + pub joining_script: bool, +} +#[derive(Clone, Debug, Deserialize)] +#[serde(default, rename_all = "camelCase")] +pub struct GenerationConfig { + pub mode: String, + pub words: usize, + pub language: String, + pub show_all_lines: bool, + pub punctuation: bool, + pub numbers: bool, + pub lazy_mode: bool, + pub british_english: bool, + pub funbox: Vec, +} +impl Default for GenerationConfig { + fn default() -> Self { + let config = crate::Config::default(); + Self { + mode: config.mode, + words: config.words as usize, + language: config.language, + show_all_lines: false, + punctuation: false, + numbers: false, + lazy_mode: false, + british_english: false, + funbox: vec![], + } + } +} +#[derive(Clone, Debug, Deserialize)] +pub struct CustomLimit { + pub mode: String, + pub value: usize, +} +#[derive(Clone, Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct CustomText { + pub text: Vec, + pub mode: String, + pub limit: CustomLimit, + pub pipe_delimiter: bool, +} +#[derive(Clone, Debug, Serialize, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +pub struct NextWord { + pub word: Text, + pub word_raw: Text, + pub section_index: usize, +} +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct Generated { + pub words: Vec, + pub section_indexes: Vec, + pub has_tab: bool, + pub has_newline: bool, + #[serde(skip_serializing_if = "Option::is_none")] + pub all_right_to_left: Option, + pub all_joining_script: bool, +} +pub struct Generator { + config: GenerationConfig, + language: Language, + custom: Option, + random: Random, + wordset: Vec, + ordered: usize, + shuffled: Vec, + section: VecDeque, + section_index: usize, + section_history: Vec, + previous: Vec, + repeating: bool, + generated: usize, +} +fn case(text: &Text, upper: bool) -> Text { + let mut output = vec![]; + let mut valid = String::new(); + fn flush(valid: &mut String, output: &mut Vec, upper: bool) { + let transformed = if upper { + valid.to_uppercase() + } else { + valid.to_lowercase() + }; + output.extend(transformed.encode_utf16()); + valid.clear(); + } + for value in char::decode_utf16(text.0.iter().copied()) { + match value { + Ok(value) => valid.push(value), + Err(error) => { + flush(&mut valid, &mut output, upper); + output.push(error.unpaired_surrogate()); + } + } + } + flush(&mut valid, &mut output, upper); + Text(output) +} +fn clean_previous(text: &Text, apostrophe: bool) -> Text { + case( + &Text( + text.0 + .iter() + .copied() + .filter(|unit| { + ![46, 63, 33, 34, 58, 45, 44].contains(unit) && !(apostrophe && *unit == 39) + }) + .collect(), + ), + false, + ) +} +fn collapse_spaces(text: Text) -> Text { + let mut result = vec![]; + for unit in text.0 { + if unit != 32 || result.last() != Some(&32) { + result.push(unit); + } + } + if result.first() == Some(&32) { + result.remove(0); + } + if result.last() == Some(&32) { + result.pop(); + } + Text(result) +} +impl Generator { + pub fn new( + config: GenerationConfig, + language: Language, + custom: Option, + random: Random, + ) -> Result { + if !["time", "words", "custom", "zen", "quote"].contains(&config.mode.as_str()) { + return Err("Invalid mode".into()); + } + if config.mode == "quote" { + return Err("Quote asset selection is not yet ported".into()); + } + if config.punctuation + || config.numbers + || config.lazy_mode + || config.british_english + || !config.funbox.is_empty() + { + return Err("Requested generator transform is not yet ported".into()); + } + if config.mode == "custom" && custom.is_none() { + return Err("Missing custom text".into()); + } + if let Some(custom) = &custom + && (!["repeat", "random", "shuffle"].contains(&custom.mode.as_str()) + || !["word", "time", "section"].contains(&custom.limit.mode.as_str())) + { + return Err("Invalid custom mode or limit".into()); + } + let wordset = if config.mode == "custom" { + custom.as_ref().unwrap().text.clone() + } else { + language.words.clone() + }; + if config.mode != "zen" && (wordset.is_empty() || wordset.iter().all(Text::is_empty)) { + return Err("Content has no words".into()); + } + if wordset.len() > 1_000_000 || wordset.iter().any(|word| word.len() > 1_000_000) { + return Err("Content exceeds generation bound".into()); + } + Ok(Self { + config, + language, + custom, + random, + wordset, + ordered: 0, + shuffled: vec![], + section: VecDeque::new(), + section_index: 0, + section_history: vec![], + previous: vec![], + repeating: false, + generated: 0, + }) + } + fn limit(&self) -> usize { + if self.config.mode == "zen" { + return 0; + } + let mut limit = 100; + if self.config.show_all_lines && self.config.mode == "words" { + limit = self.config.words; + } + if self.config.mode == "words" && self.config.words == 0 { + limit = 100; + } + if let Some(custom) = &self.custom + && self.config.mode == "custom" + { + limit = if custom.limit.value == 0 || custom.limit.mode == "time" { + 100 + } else { + custom.limit.value.min(100) + }; + } + if self.config.mode == "words" && self.config.words > 0 { + limit = limit.min(self.config.words); + } + limit + } + pub fn all_generated(&self) -> bool { + if self.config.mode == "words" { + return self.config.words > 0 && self.generated >= self.config.words; + } + if self.config.mode == "custom" + && let Some(custom) = &self.custom + { + return custom.limit.value > 0 + && match custom.limit.mode.as_str() { + "word" => self.generated >= custom.limit.value, + "section" => { + self.section_index >= custom.limit.value && self.section.is_empty() + } + _ => false, + }; + } + false + } + fn random_word(&mut self) -> Result { + Ok(self.wordset[self.random.index(self.wordset.len())?].clone()) + } + fn next_ordered(&mut self) -> Text { + if self.ordered >= self.wordset.len() { + self.ordered = 0; + } + let word = self.wordset[self.ordered].clone(); + self.ordered += 1; + word + } + fn next_shuffled(&mut self) -> Result { + if self.shuffled.is_empty() { + self.shuffled = (0..self.wordset.len()).collect(); + self.random.shuffle(&mut self.shuffled)?; + } + Ok(self.wordset[self.shuffled.pop().unwrap()].clone()) + } + pub fn generate(&mut self, repeating: bool) -> Result { + self.repeating = repeating; + self.ordered = 0; + self.shuffled.clear(); + self.section.clear(); + self.section_index = 0; + self.section_history.clear(); + self.generated = 0; + if !repeating { + self.previous.clear(); + } + let limit = self.limit(); + if limit > 1_000_000 { + return Err("Generation request exceeds bound".into()); + } + let mut words = vec![]; + let mut indexes = vec![]; + let mut raw: Vec = vec![]; + if limit > 0 { + loop { + let next = self.next( + words.len(), + limit, + raw.last(), + raw.get(raw.len().wrapping_sub(2)), + )?; + indexes.push(next.section_index); + words.push(next.word.clone()); + raw.push(next.word_raw); + let sections = self.config.mode == "custom" + && self + .custom + .as_ref() + .is_some_and(|custom| custom.pipe_delimiter); + if (sections + && ((self.section.is_empty() && self.section_index >= limit) + || words.len() >= 100)) + || (!sections && words.len() >= limit) + { + break; + } + } + } + self.generated = words.len(); + self.section_history.clear(); + let has_tab = words + .iter() + .chain(&self.wordset) + .any(|word| word.0.contains(&9)); + let has_newline = words + .iter() + .chain(&self.wordset) + .any(|word| word.0.contains(&10)); + Ok(Generated { + words, + section_indexes: indexes, + has_tab, + has_newline, + all_right_to_left: self.language.right_to_left, + all_joining_script: self.language.joining_script, + }) + } + pub fn next( + &mut self, + index: usize, + _bound: usize, + previous: Option<&Text>, + previous2: Option<&Text>, + ) -> Result { + if self.repeating + && let Some(next) = self.previous.get(index) + { + self.section_index += 1; + self.generated = index + 1; + return Ok(next.clone()); + } + let mut word = self.random_word()?; + let previous = previous.map(|word| clean_previous(word, false)); + let previous2 = previous2.map(|word| clean_previous(word, true)); + if self.section.is_empty() { + let custom = self.custom.clone(); + if self.config.mode == "custom" + && custom + .as_ref() + .is_some_and(|custom| custom.mode == "repeat") + { + word = self.next_ordered(); + } else if self.config.mode == "custom" + && custom + .as_ref() + .is_some_and(|custom| custom.mode == "random") + && self.wordset.len() < 4 + { + word = self.random_word()?; + } else if self.config.mode == "custom" + && custom + .as_ref() + .is_some_and(|custom| custom.mode == "shuffle") + { + word = self.next_shuffled()?; + } else if self.config.mode == "custom" + && custom + .as_ref() + .is_some_and(|custom| custom.limit.mode == "section") + { + word = self.random_word()?; + let mut retries = 0; + while retries < 100 + && (self.section_history.last() == Some(&word) + || self + .section_history + .get(self.section_history.len().wrapping_sub(2)) + == Some(&word)) + { + retries += 1; + word = self.random_word()?; + } + } else { + let mut retries = 0; + let mut first = case( + &Text( + word.0 + .split(|unit| *unit == 32) + .next() + .unwrap_or_default() + .to_vec(), + ), + false, + ); + while retries < 100 + && (previous.as_ref() == Some(&first) + || previous2.as_ref() == Some(&first) + || (self.config.mode != "custom" + && (word.0 == [73] + || (!self.config.language.starts_with("code") + && word.0.iter().any(|unit| { + b"-=_+[]{};'\\:\"|,./<>?".contains(&(*unit as u8)) + && *unit < 128 + })) + || word.0.iter().any(|unit| (48..=57).contains(unit))))) + { + retries += 1; + word = self.random_word()?; + first = Text( + word.0 + .split(|unit| *unit == 32) + .next() + .unwrap_or_default() + .to_vec(), + ); + } + } + word = collapse_spaces(word); + self.section = word + .0 + .split(|unit| *unit == 32) + .map(|part| Text(part.to_vec())) + .collect(); + self.section_history.push(word); + self.section_index += 1; + } + word = self.section.pop_front().ok_or("Empty generated section")?; + if word.is_empty() { + return Err("Random word is empty".into()); + } + if self.config.mode != "custom" + && word.0.iter().any(|unit| (65..=90).contains(unit)) + && !["german", "swiss_german", "code", "klingon"] + .iter() + .any(|prefix| self.config.language.starts_with(prefix)) + { + word = case(&word, false); + } + word = collapse_spaces(word); + if self.config.language.starts_with("swiss_german") { + let mut out = vec![]; + for unit in word.0 { + if unit == 0xdf { + out.extend([115, 115]); + } else { + out.push(unit); + } + } + word = Text(out); + } + let mut committed = word.clone(); + if committed.0.last() != Some(&10) { + committed.0.push(32); + } + let next = NextWord { + word: committed, + word_raw: word, + section_index: self.section_index, + }; + self.previous.push(next.clone()); + self.generated = index + 1; + Ok(next) + } +} diff --git a/crates/typing-engine/src/lib.rs b/crates/typing-engine/src/lib.rs index 34375a90b..140ae1280 100644 --- a/crates/typing-engine/src/lib.rs +++ b/crates/typing-engine/src/lib.rs @@ -1,4 +1,5 @@ //! Platform-independent typing rules. Hosts provide content, observations and time. +pub mod generation; pub mod pace; pub mod random; pub mod session; diff --git a/crates/typing-engine/tests/generation.rs b/crates/typing-engine/tests/generation.rs new file mode 100644 index 000000000..f9434ee78 --- /dev/null +++ b/crates/typing-engine/tests/generation.rs @@ -0,0 +1,109 @@ +use serde_json::{Value, json}; +use typing_engine::{ + Text, + generation::{CustomText, GenerationConfig, Generator, Language}, + random::Random, +}; + +fn neutral_words(value: &mut Value) { + if let Some(words) = value.get_mut("words").and_then(Value::as_array_mut) { + for word in words { + if let Some(value) = word.as_str() { + *word = json!(Text::from(value)); + } + } + } + if let Some(text) = value.get_mut("text").and_then(Value::as_array_mut) { + for word in text { + if let Some(value) = word.as_str() { + *word = json!(Text::from(value)); + } + } + } +} +#[test] +fn base_generation_matches_immutable_browser_snapshots() { + let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("../.."); + let cases: Vec = serde_json::from_slice( + &std::fs::read(root.join("packages/typing-fixtures/reference/words/cases.json")).unwrap(), + ) + .unwrap(); + let mut checked = 0; + for case in cases { + let config: GenerationConfig = serde_json::from_value(case["config"].clone()).unwrap(); + if config.punctuation + || config.numbers + || config.lazy_mode + || config.british_english + || !config.funbox.is_empty() + || config.mode == "quote" + { + continue; + } + let name = case["name"].as_str().unwrap(); + let mut language: Value = serde_json::from_slice( + &std::fs::read(root.join(format!( + "frontend/static/languages/{}.json", + config.language + ))) + .unwrap(), + ) + .unwrap(); + neutral_words(&mut language); + let language: Language = serde_json::from_value(language).unwrap(); + let custom = case.get("customText").map(|value| { + let mut value = value.clone(); + neutral_words(&mut value); + serde_json::from_value::(value).unwrap() + }); + let mut generator = Generator::new( + config, + language, + custom, + Random::seeded(case["seed"].as_u64().unwrap() as u32), + ) + .unwrap(); + let mut generated = generator.generate(false).unwrap(); + if case["repeat"] == true { + generated = generator.generate(true).unwrap(); + } + let mut words = generated.words.clone(); + let mut extra = vec![]; + for _ in 0..case["extraWords"].as_u64().unwrap_or(0) { + let strip = |text: &Text| { + let mut text = text.clone(); + if matches!(text.0.last(), Some(32 | 10)) { + text.0.pop(); + } + text + }; + let previous = words.last().map(strip); + let previous2 = words.get(words.len().wrapping_sub(2)).map(strip); + let next = generator + .next(words.len(), 100, previous.as_ref(), previous2.as_ref()) + .unwrap(); + words.push(next.word.clone()); + extra.push(json!(next)); + } + let mut actual = json!(generated); + actual["extra"] = json!(extra); + actual["allWordsGenerated"] = json!(generator.all_generated()); + actual["currentQuote"] = Value::Null; + let mut expected: Value = serde_json::from_slice( + &std::fs::read(root.join(format!( + "packages/typing-fixtures/reference/words/__snapshots__/{name}.json" + ))) + .unwrap(), + ) + .unwrap(); + neutral_words(&mut expected); + for extra in expected["extra"].as_array_mut().unwrap() { + for field in ["word", "wordRaw"] { + extra[field] = json!(Text::from(extra[field].as_str().unwrap())); + } + } + assert_eq!(actual, expected, "{name}"); + checked += 1; + } + assert!(checked >= 15, "Only {checked} cases exercised"); +} From 0899e247a98005f00fc30d5906fe2d833b558634 Mon Sep 17 00:00:00 2001 From: Lakshmi Tanmay Date: Fri, 23 Oct 2026 00:31:54 +0530 Subject: [PATCH 20/93] fix(engine): preserve browser deletion extents and Firefox sentinel regressions --- crates/typing-engine/src/session.rs | 63 ++++++++++++++++------ crates/typing-engine/tests/observations.rs | 45 ++++++++++++++++ 2 files changed, 91 insertions(+), 17 deletions(-) diff --git a/crates/typing-engine/src/session.rs b/crates/typing-engine/src/session.rs index e12d4059a..5cf4b7147 100644 --- a/crates/typing-engine/src/session.rs +++ b/crates/typing-engine/src/session.rs @@ -75,11 +75,16 @@ pub enum Command { #[serde(default)] is_composition_ending: Option, }, + #[serde(rename_all = "camelCase")] Delete { kind: String, now: f64, #[serde(default)] date: f64, + #[serde(default)] + observed_input: Option, + #[serde(default)] + sentinel_removed: bool, }, Start { now: f64, @@ -329,11 +334,23 @@ impl Session { } self.completion_checkpoint = true; } - Command::Delete { kind, now, .. } => { + Command::Delete { + kind, + now, + observed_input, + sentinel_removed, + .. + } => { if !["deleteContentBackward", "deleteWordBackward"].contains(&kind.as_str()) { return Err("Invalid deletion kind".into()); } - self.delete(&kind, now); + if observed_input.as_ref().is_some_and(|observed| { + !self.current().0.starts_with(&observed.0) + || (sentinel_removed && !observed.is_empty()) + }) { + return Err("Invalid deletion observation".into()); + } + self.delete(&kind, now, observed_input, sentinel_removed); } Command::Start { now, date } => self.start(now, date), Command::Finish { now, date, reason } => self.end(now, date, reason), @@ -660,7 +677,7 @@ impl Session { } } - fn delete(&mut self, kind: &str, now: f64) { + fn delete(&mut self, kind: &str, now: f64, observed: Option, sentinel_removed: bool) { if !self.active || self.ended { return; } @@ -681,11 +698,18 @@ impl Session { { return; } + let after = observed.unwrap_or_else(|| { + if kind == "deleteWordBackward" { + delete_word(&input) + } else { + Text(input.0[..input.len().saturating_sub(1)].to_vec()) + } + }); let unindent = self.config.language.starts_with("code") && self.config.code_unindent_on_backspace && !input.is_empty() && input.0.iter().all(|c| *c == 9) - && self.target().0.starts_with(&input.0); + && self.target().0.starts_with(&after.0); if unindent { self.log_delete( "deleteWordBackward", @@ -695,21 +719,18 @@ impl Session { false, ); } - let next = if input.is_empty() || unindent { - self.index = self.index.saturating_sub(1); - self.previous_input(kind) - } else if kind == "deleteWordBackward" { - delete_word(&input) - } else { - let mut next = input.clone(); - next.0.pop(); - next - }; - let char_index = if input.is_empty() || unindent { - next.len() + let regression = input.is_empty() || unindent || sentinel_removed; + let next = if regression { + if self.index == 0 { + Text::default() + } else { + self.index -= 1; + self.previous_input(kind) + } } else { - input.len() + after }; + let char_index = if regression { next.len() } else { input.len() }; self.log_delete( if unindent { "deleteContentBackward" @@ -721,6 +742,14 @@ impl Session { next, false, ); + if sentinel_removed && !unindent && !input.is_empty() { + if let Some(event) = self.events.last_mut() { + event.data["clearedNextWord"] = json!(true); + } + if let Some(input) = self.input.get_mut(self.index + 1) { + input.0.clear(); + } + } } fn advance(&mut self, now: f64, date: f64) { diff --git a/crates/typing-engine/tests/observations.rs b/crates/typing-engine/tests/observations.rs index a618abb05..2c3f2c902 100644 --- a/crates/typing-engine/tests/observations.rs +++ b/crates/typing-engine/tests/observations.rs @@ -79,3 +79,48 @@ fn composition_defers_difficulty_until_the_committed_end() { assert_eq!(session.state().reason.as_deref(), Some("difficulty")); assert_eq!(session.log()["events"][0]["data"]["wordIndex"], 0); } + +#[test] +fn browser_sentinel_regression_records_destination_and_clears_next_word() { + let mut session = Session::new( + serde_json::from_value(json!({"mode":"words","words":3})).unwrap(), + vec![Text::from("one "), Text::from("two "), Text::from("three")], + ) + .unwrap(); + apply( + &mut session, + json!({"op":"insert","data":[120,32,63],"now":0}), + ); + let update = apply( + &mut session, + json!({"op":"delete","kind":"deleteWordBackward","observedInput":[],"sentinelRemoved":true,"now":20}), + ); + assert_eq!(update.state.word_index, 0); + assert!(update.state.input_value.is_empty()); + assert_eq!( + update.events[0].data, + json!({"inputType":"deleteWordBackward","wordIndex":0,"charIndex":0,"inputValue":[],"clearedNextWord":true}) + ); +} + +#[test] +fn unicode_deletion_observation_preserves_browser_grapheme_extent() { + let mut session = Session::new( + serde_json::from_value(json!({"mode":"words","words":1})).unwrap(), + vec![Text::from("😀abc")], + ) + .unwrap(); + apply( + &mut session, + json!({"op":"insert","data":[55357,56832],"now":0}), + ); + let update = apply( + &mut session, + json!({"op":"delete","kind":"deleteContentBackward","observedInput":[],"now":20}), + ); + assert!(update.state.input_value.is_empty()); + assert_eq!(update.events[0].data["charIndex"], 2); + let before = session.log(); + assert!(session.apply(serde_json::from_value(json!({"op":"delete","kind":"deleteContentBackward","observedInput":[120],"now":30})).unwrap()).is_err()); + assert_eq!(session.log(), before); +} From 7107cdc91d0b81afcf848a630945eead5141e487 Mon Sep 17 00:00:00 2001 From: Lakshmi Tanmay Date: Fri, 23 Oct 2026 00:40:30 +0530 Subject: [PATCH 21/93] feat(engine): port numeric and Unicode funbox transforms against frozen references --- crates/typing-engine/src/transforms.rs | 195 +++++ crates/typing-engine/tests/transforms.rs | 29 + .../scripts/capture-transform-oracle.ts | 71 ++ .../typing-fixtures/oracle/transforms.json | 796 ++++++++++++++++++ 4 files changed, 1091 insertions(+) create mode 100644 crates/typing-engine/src/transforms.rs create mode 100644 crates/typing-engine/tests/transforms.rs create mode 100644 packages/typing-core/scripts/capture-transform-oracle.ts create mode 100644 packages/typing-fixtures/oracle/transforms.json diff --git a/crates/typing-engine/src/transforms.rs b/crates/typing-engine/src/transforms.rs new file mode 100644 index 000000000..9ae8adc5e --- /dev/null +++ b/crates/typing-engine/src/transforms.rs @@ -0,0 +1,195 @@ +use crate::{Text, generation::case, random::Random}; + +fn append(word: &mut Text, text: &str) { + word.0.extend(text.encode_utf16()); +} +pub fn numbers(random: &mut Random, max: usize) -> Result { + let length = random.index(max)? + 1; + let mut units = vec![]; + for index in 0..length { + units.push( + 48 + (if index == 0 { + random.index(9)? + 1 + } else { + random.index(10)? + }) as u16, + ); + } + Ok(Text(units)) +} +pub fn numeral_script(word: Text, language: &str) -> Text { + let base = if language.starts_with("kurdish") { + 0x660 + } else if language.starts_with("nepali") || language.starts_with("hindi") { + 0x966 + } else if language.starts_with("bangla") { + 0x9e6 + } else { + 48 + }; + Text( + word.0 + .into_iter() + .map(|unit| { + if (48..=57).contains(&unit) { + base + unit - 48 + } else { + unit + } + }) + .collect(), + ) +} +pub fn generated_word(name: &str, random: &mut Random) -> Result, String> { + let word = match name { + "58008" => numbers(random, 7)?, + "binary" => Text::from(format!("{:08b}", random.index(256)?).as_str()), + "hexadecimal" => { + let length = random.index(4)? + 1; + let mut word = String::new(); + for _ in 0..length { + word.push_str(&format!("{:02x}", random.index(256)?)); + } + Text::from(word.as_str()) + } + "gibberish" | "ascii" | "specials" => { + let length = random.index(if name == "ascii" { 10 } else { 7 })? + 1; + let mut units = vec![]; + let specials: Vec<_> = "`~!@#$%^&*()-_=+{}[]'\"/\\|?;:><,." + .encode_utf16() + .collect(); + for _ in 0..length { + units.push(match name { + "ascii" => 33 + random.index(94)? as u16, + "gibberish" => 97 + random.index(26)? as u16, + _ => specials[random.index(specials.len())?], + }); + } + Text(units) + } + _ => return Ok(None), + }; + Ok(Some(word)) +} +pub fn alter( + name: &str, + word: Text, + index: usize, + bound: usize, + random: &mut Random, +) -> Result { + let mut word = word; + match name { + "backwards" => word.0.reverse(), + "ALL_CAPS" => word = case(&word, true), + "capitals" => { + let mut output = vec![]; + let mut first = true; + for unit in word.0 { + if unit == 32 { + if output.last() != Some(&32) { + output.push(32); + } + first = true; + } else if first { + output.extend(case(&Text(vec![unit]), true).0); + first = false; + } else { + output.push(unit); + } + } + word = Text(output); + } + "sPoNgEcAsE" => { + let mut output = vec![]; + for (index, unit) in word.0.iter().enumerate() { + output.extend(case(&Text(vec![*unit]), index % 2 == 1).0); + } + word = Text(output); + } + "rAnDoMcAsE" => { + let mut output = vec![]; + let mut index = 0; + while index < word.len() { + let width = if (0xd800..=0xdbff).contains(&word.0[index]) + && word + .0 + .get(index + 1) + .is_some_and(|unit| (0xdc00..=0xdfff).contains(unit)) + { + 2 + } else { + 1 + }; + output.extend( + case( + &Text(word.0[index..index + width].to_vec()), + random.draw()? < 0.5, + ) + .0, + ); + index += width; + } + word = Text(output); + } + "rot13" => { + for unit in &mut word.0 { + let original = Text(vec![*unit]); + let lower = case(&original, false); + if lower.len() == 1 && (97..=122).contains(&lower.0[0]) { + let base = if case(&original, true) == original { + 65 + } else { + 97 + }; + *unit = base + (lower.0[0] - 97 + 13) % 26; + } + } + } + "ddoouubblleedd" => { + let mut output = vec![]; + let mut index = 0; + while index < word.len() { + let width = if (0xd800..=0xdbff).contains(&word.0[index]) + && word + .0 + .get(index + 1) + .is_some_and(|unit| (0xdc00..=0xdfff).contains(unit)) + { + 2 + } else { + 1 + }; + let units = &word.0[index..index + width]; + output.extend_from_slice(units); + if ![10, 13, 0x2028, 0x2029].contains(&units[0]) { + output.extend_from_slice(units); + } + index += width; + } + word = Text(output); + } + "instant_messaging" => { + word = case(&word, false); + let position = word.len(); + if position > 0 && matches!(word.0.get(position - 1), Some(46 | 33 | 63)) { + word.0[position - 1] = 10; + } + let mut output = vec![]; + for unit in word.0 { + if [40, 41, 46, 39, 34].contains(&unit) + || (unit == 10 && output.last() == Some(&10)) + { + continue; + } + output.push(unit); + } + word = Text(output); + } + "underscore_spaces" if index + 1 != bound => { + append(&mut word, "_"); + } + _ => {} + } + Ok(word) +} diff --git a/crates/typing-engine/tests/transforms.rs b/crates/typing-engine/tests/transforms.rs new file mode 100644 index 000000000..eccbcc9ed --- /dev/null +++ b/crates/typing-engine/tests/transforms.rs @@ -0,0 +1,29 @@ +use serde::Deserialize; +use typing_engine::{Text, random::Random, transforms::alter}; +#[derive(Deserialize)] +struct Case { + name: String, + word: Text, + index: usize, + bound: usize, + seed: u32, + expected: Text, +} +#[test] +fn funbox_transforms_preserve_frozen_unicode_and_control_character_behavior() { + let cases: Vec = serde_json::from_str(include_str!( + "../../../packages/typing-fixtures/oracle/transforms.json" + )) + .unwrap(); + for case in cases { + let actual = alter( + &case.name, + case.word.clone(), + case.index, + case.bound, + &mut Random::seeded(case.seed), + ) + .unwrap(); + assert_eq!(actual, case.expected, "{} {:?}", case.name, case.word); + } +} diff --git a/packages/typing-core/scripts/capture-transform-oracle.ts b/packages/typing-core/scripts/capture-transform-oracle.ts new file mode 100644 index 000000000..d01451f49 --- /dev/null +++ b/packages/typing-core/scripts/capture-transform-oracle.ts @@ -0,0 +1,71 @@ +import { writeFileSync } from "node:fs"; +import { resolve } from "node:path"; +import { mulberry32 } from "./lib/keystroke-plan"; +import { units } from "./rust-oracle"; + +// Capture new Unicode/control-character references once; no overwrite option. +let random = mulberry32(42); +const originalRandom = Math.random; +Math.random = () => random(); +try { + const { createFunboxWordFunctions } = + await import("../src/funbox-word-functions"); + const functions = createFunboxWordFunctions({ + getConfig: () => ({ language: "english", customPolyglot: [] }), + getLanguage: async () => { + throw new Error("Unexpected language request"); + }, + getPoem: async () => false, + getSection: async () => false, + disableFunbox: () => undefined, + setLanguage: () => undefined, + }); + const output = []; + for (const name of [ + "backwards", + "ALL_CAPS", + "capitals", + "sPoNgEcAsE", + "rAnDoMcAsE", + "rot13", + "ddoouubblleedd", + "instant_messaging", + "underscore_spaces", + ] as const) { + for (const word of [ + "hello", + "a😀b", + "ßéΣa", + "ab!\n", + "ab?\r", + "ab.\r\n", + "a\ud83d", + "\tHi\n", + "one two", + "Kİſ", + "\u2028x\u2029", + ]) { + const alter = functions[name]?.alterText; + if (alter === undefined) throw new Error(`Missing ${name}`); + random = mulberry32(42); + output.push({ + name, + word: units(word), + index: 1, + bound: 3, + seed: 42, + expected: units(alter(word, 1, 3)), + }); + } + } + writeFileSync( + resolve( + import.meta.dirname, + "../../typing-fixtures/oracle/transforms.json", + ), + `${JSON.stringify(output, null, 2)}\n`, + { flag: "wx" }, + ); +} finally { + Math.random = originalRandom; +} diff --git a/packages/typing-fixtures/oracle/transforms.json b/packages/typing-fixtures/oracle/transforms.json new file mode 100644 index 000000000..0721f8003 --- /dev/null +++ b/packages/typing-fixtures/oracle/transforms.json @@ -0,0 +1,796 @@ +[ + { + "name": "backwards", + "word": [104, 101, 108, 108, 111], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [111, 108, 108, 101, 104] + }, + { + "name": "backwards", + "word": [97, 55357, 56832, 98], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [98, 56832, 55357, 97] + }, + { + "name": "backwards", + "word": [223, 233, 931, 97], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [97, 931, 233, 223] + }, + { + "name": "backwards", + "word": [97, 98, 33, 10], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [10, 33, 98, 97] + }, + { + "name": "backwards", + "word": [97, 98, 63, 13], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [13, 63, 98, 97] + }, + { + "name": "backwards", + "word": [97, 98, 46, 13, 10], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [10, 13, 46, 98, 97] + }, + { + "name": "backwards", + "word": [97, 55357], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [55357, 97] + }, + { + "name": "backwards", + "word": [9, 72, 105, 10], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [10, 105, 72, 9] + }, + { + "name": "backwards", + "word": [111, 110, 101, 32, 32, 116, 119, 111], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [111, 119, 116, 32, 32, 101, 110, 111] + }, + { + "name": "backwards", + "word": [8490, 304, 383], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [383, 304, 8490] + }, + { + "name": "backwards", + "word": [8232, 120, 8233], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [8233, 120, 8232] + }, + { + "name": "ALL_CAPS", + "word": [104, 101, 108, 108, 111], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [72, 69, 76, 76, 79] + }, + { + "name": "ALL_CAPS", + "word": [97, 55357, 56832, 98], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [65, 55357, 56832, 66] + }, + { + "name": "ALL_CAPS", + "word": [223, 233, 931, 97], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [83, 83, 201, 931, 65] + }, + { + "name": "ALL_CAPS", + "word": [97, 98, 33, 10], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [65, 66, 33, 10] + }, + { + "name": "ALL_CAPS", + "word": [97, 98, 63, 13], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [65, 66, 63, 13] + }, + { + "name": "ALL_CAPS", + "word": [97, 98, 46, 13, 10], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [65, 66, 46, 13, 10] + }, + { + "name": "ALL_CAPS", + "word": [97, 55357], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [65, 55357] + }, + { + "name": "ALL_CAPS", + "word": [9, 72, 105, 10], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [9, 72, 73, 10] + }, + { + "name": "ALL_CAPS", + "word": [111, 110, 101, 32, 32, 116, 119, 111], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [79, 78, 69, 32, 32, 84, 87, 79] + }, + { + "name": "ALL_CAPS", + "word": [8490, 304, 383], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [8490, 304, 83] + }, + { + "name": "ALL_CAPS", + "word": [8232, 120, 8233], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [8232, 88, 8233] + }, + { + "name": "capitals", + "word": [104, 101, 108, 108, 111], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [72, 101, 108, 108, 111] + }, + { + "name": "capitals", + "word": [97, 55357, 56832, 98], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [65, 55357, 56832, 98] + }, + { + "name": "capitals", + "word": [223, 233, 931, 97], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [83, 83, 233, 931, 97] + }, + { + "name": "capitals", + "word": [97, 98, 33, 10], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [65, 98, 33, 10] + }, + { + "name": "capitals", + "word": [97, 98, 63, 13], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [65, 98, 63, 13] + }, + { + "name": "capitals", + "word": [97, 98, 46, 13, 10], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [65, 98, 46, 13, 10] + }, + { + "name": "capitals", + "word": [97, 55357], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [65, 55357] + }, + { + "name": "capitals", + "word": [9, 72, 105, 10], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [9, 72, 105, 10] + }, + { + "name": "capitals", + "word": [111, 110, 101, 32, 32, 116, 119, 111], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [79, 110, 101, 32, 84, 119, 111] + }, + { + "name": "capitals", + "word": [8490, 304, 383], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [8490, 304, 383] + }, + { + "name": "capitals", + "word": [8232, 120, 8233], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [8232, 120, 8233] + }, + { + "name": "sPoNgEcAsE", + "word": [104, 101, 108, 108, 111], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [104, 69, 108, 76, 111] + }, + { + "name": "sPoNgEcAsE", + "word": [97, 55357, 56832, 98], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [97, 55357, 56832, 66] + }, + { + "name": "sPoNgEcAsE", + "word": [223, 233, 931, 97], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [223, 201, 963, 65] + }, + { + "name": "sPoNgEcAsE", + "word": [97, 98, 33, 10], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [97, 66, 33, 10] + }, + { + "name": "sPoNgEcAsE", + "word": [97, 98, 63, 13], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [97, 66, 63, 13] + }, + { + "name": "sPoNgEcAsE", + "word": [97, 98, 46, 13, 10], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [97, 66, 46, 13, 10] + }, + { + "name": "sPoNgEcAsE", + "word": [97, 55357], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [97, 55357] + }, + { + "name": "sPoNgEcAsE", + "word": [9, 72, 105, 10], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [9, 72, 105, 10] + }, + { + "name": "sPoNgEcAsE", + "word": [111, 110, 101, 32, 32, 116, 119, 111], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [111, 78, 101, 32, 32, 84, 119, 79] + }, + { + "name": "sPoNgEcAsE", + "word": [8490, 304, 383], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [107, 304, 383] + }, + { + "name": "sPoNgEcAsE", + "word": [8232, 120, 8233], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [8232, 88, 8233] + }, + { + "name": "rAnDoMcAsE", + "word": [104, 101, 108, 108, 111], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [104, 69, 108, 108, 79] + }, + { + "name": "rAnDoMcAsE", + "word": [97, 55357, 56832, 98], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [97, 55357, 56832, 98] + }, + { + "name": "rAnDoMcAsE", + "word": [223, 233, 931, 97], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [223, 201, 963, 97] + }, + { + "name": "rAnDoMcAsE", + "word": [97, 98, 33, 10], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [97, 66, 33, 10] + }, + { + "name": "rAnDoMcAsE", + "word": [97, 98, 63, 13], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [97, 66, 63, 13] + }, + { + "name": "rAnDoMcAsE", + "word": [97, 98, 46, 13, 10], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [97, 66, 46, 13, 10] + }, + { + "name": "rAnDoMcAsE", + "word": [97, 55357], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [97, 55357] + }, + { + "name": "rAnDoMcAsE", + "word": [9, 72, 105, 10], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [9, 72, 105, 10] + }, + { + "name": "rAnDoMcAsE", + "word": [111, 110, 101, 32, 32, 116, 119, 111], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [111, 78, 101, 32, 32, 116, 87, 111] + }, + { + "name": "rAnDoMcAsE", + "word": [8490, 304, 383], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [107, 304, 383] + }, + { + "name": "rAnDoMcAsE", + "word": [8232, 120, 8233], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [8232, 88, 8233] + }, + { + "name": "rot13", + "word": [104, 101, 108, 108, 111], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [117, 114, 121, 121, 98] + }, + { + "name": "rot13", + "word": [97, 55357, 56832, 98], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [110, 55357, 56832, 111] + }, + { + "name": "rot13", + "word": [223, 233, 931, 97], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [223, 233, 931, 110] + }, + { + "name": "rot13", + "word": [97, 98, 33, 10], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [110, 111, 33, 10] + }, + { + "name": "rot13", + "word": [97, 98, 63, 13], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [110, 111, 63, 13] + }, + { + "name": "rot13", + "word": [97, 98, 46, 13, 10], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [110, 111, 46, 13, 10] + }, + { + "name": "rot13", + "word": [97, 55357], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [110, 55357] + }, + { + "name": "rot13", + "word": [9, 72, 105, 10], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [9, 85, 118, 10] + }, + { + "name": "rot13", + "word": [111, 110, 101, 32, 32, 116, 119, 111], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [98, 97, 114, 32, 32, 103, 106, 98] + }, + { + "name": "rot13", + "word": [8490, 304, 383], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [88, 304, 383] + }, + { + "name": "rot13", + "word": [8232, 120, 8233], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [8232, 107, 8233] + }, + { + "name": "ddoouubblleedd", + "word": [104, 101, 108, 108, 111], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [104, 104, 101, 101, 108, 108, 108, 108, 111, 111] + }, + { + "name": "ddoouubblleedd", + "word": [97, 55357, 56832, 98], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [97, 97, 55357, 56832, 55357, 56832, 98, 98] + }, + { + "name": "ddoouubblleedd", + "word": [223, 233, 931, 97], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [223, 223, 233, 233, 931, 931, 97, 97] + }, + { + "name": "ddoouubblleedd", + "word": [97, 98, 33, 10], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [97, 97, 98, 98, 33, 33, 10] + }, + { + "name": "ddoouubblleedd", + "word": [97, 98, 63, 13], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [97, 97, 98, 98, 63, 63, 13] + }, + { + "name": "ddoouubblleedd", + "word": [97, 98, 46, 13, 10], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [97, 97, 98, 98, 46, 46, 13, 10] + }, + { + "name": "ddoouubblleedd", + "word": [97, 55357], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [97, 97, 55357, 55357] + }, + { + "name": "ddoouubblleedd", + "word": [9, 72, 105, 10], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [9, 9, 72, 72, 105, 105, 10] + }, + { + "name": "ddoouubblleedd", + "word": [111, 110, 101, 32, 32, 116, 119, 111], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [ + 111, 111, 110, 110, 101, 101, 32, 32, 32, 32, 116, 116, 119, 119, 111, 111 + ] + }, + { + "name": "ddoouubblleedd", + "word": [8490, 304, 383], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [8490, 8490, 304, 304, 383, 383] + }, + { + "name": "ddoouubblleedd", + "word": [8232, 120, 8233], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [8232, 120, 120, 8233] + }, + { + "name": "instant_messaging", + "word": [104, 101, 108, 108, 111], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [104, 101, 108, 108, 111] + }, + { + "name": "instant_messaging", + "word": [97, 55357, 56832, 98], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [97, 55357, 56832, 98] + }, + { + "name": "instant_messaging", + "word": [223, 233, 931, 97], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [223, 233, 963, 97] + }, + { + "name": "instant_messaging", + "word": [97, 98, 33, 10], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [97, 98, 33, 10] + }, + { + "name": "instant_messaging", + "word": [97, 98, 63, 13], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [97, 98, 63, 13] + }, + { + "name": "instant_messaging", + "word": [97, 98, 46, 13, 10], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [97, 98, 13, 10] + }, + { + "name": "instant_messaging", + "word": [97, 55357], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [97, 55357] + }, + { + "name": "instant_messaging", + "word": [9, 72, 105, 10], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [9, 104, 105, 10] + }, + { + "name": "instant_messaging", + "word": [111, 110, 101, 32, 32, 116, 119, 111], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [111, 110, 101, 32, 32, 116, 119, 111] + }, + { + "name": "instant_messaging", + "word": [8490, 304, 383], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [107, 105, 775, 383] + }, + { + "name": "instant_messaging", + "word": [8232, 120, 8233], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [8232, 120, 8233] + }, + { + "name": "underscore_spaces", + "word": [104, 101, 108, 108, 111], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [104, 101, 108, 108, 111, 95] + }, + { + "name": "underscore_spaces", + "word": [97, 55357, 56832, 98], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [97, 55357, 56832, 98, 95] + }, + { + "name": "underscore_spaces", + "word": [223, 233, 931, 97], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [223, 233, 931, 97, 95] + }, + { + "name": "underscore_spaces", + "word": [97, 98, 33, 10], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [97, 98, 33, 10, 95] + }, + { + "name": "underscore_spaces", + "word": [97, 98, 63, 13], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [97, 98, 63, 13, 95] + }, + { + "name": "underscore_spaces", + "word": [97, 98, 46, 13, 10], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [97, 98, 46, 13, 10, 95] + }, + { + "name": "underscore_spaces", + "word": [97, 55357], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [97, 55357, 95] + }, + { + "name": "underscore_spaces", + "word": [9, 72, 105, 10], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [9, 72, 105, 10, 95] + }, + { + "name": "underscore_spaces", + "word": [111, 110, 101, 32, 32, 116, 119, 111], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [111, 110, 101, 32, 32, 116, 119, 111, 95] + }, + { + "name": "underscore_spaces", + "word": [8490, 304, 383], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [8490, 304, 383, 95] + }, + { + "name": "underscore_spaces", + "word": [8232, 120, 8233], + "index": 1, + "bound": 3, + "seed": 42, + "expected": [8232, 120, 8233, 95] + } +] From 3891f92967be20567b8c607d8fe88c708d5160c5 Mon Sep 17 00:00:00 2001 From: Lakshmi Tanmay Date: Fri, 23 Oct 2026 00:40:31 +0530 Subject: [PATCH 22/93] feat(engine): preserve multilingual punctuation and generator random draw order --- crates/typing-engine/src/generation.rs | 122 +++++++++- crates/typing-engine/src/lib.rs | 2 + crates/typing-engine/src/punctuation.rs | 276 +++++++++++++++++++++++ crates/typing-engine/tests/generation.rs | 33 ++- 4 files changed, 416 insertions(+), 17 deletions(-) create mode 100644 crates/typing-engine/src/punctuation.rs diff --git a/crates/typing-engine/src/generation.rs b/crates/typing-engine/src/generation.rs index 0f8d490a4..06e5b3a94 100644 --- a/crates/typing-engine/src/generation.rs +++ b/crates/typing-engine/src/generation.rs @@ -9,6 +9,7 @@ pub struct Language { pub words: Vec, pub right_to_left: Option, pub joining_script: bool, + pub original_punctuation: bool, } #[derive(Clone, Debug, Deserialize)] #[serde(default, rename_all = "camelCase")] @@ -84,8 +85,9 @@ pub struct Generator { previous: Vec, repeating: bool, generated: usize, + punctuation: crate::punctuation::Punctuation, } -fn case(text: &Text, upper: bool) -> Text { +pub(crate) fn case(text: &Text, upper: bool) -> Text { let mut output = vec![]; let mut valid = String::new(); fn flush(valid: &mut String, output: &mut Vec, upper: bool) { @@ -151,11 +153,34 @@ impl Generator { if config.mode == "quote" { return Err("Quote asset selection is not yet ported".into()); } - if config.punctuation - || config.numbers - || config.lazy_mode + if config.lazy_mode || config.british_english - || !config.funbox.is_empty() + || config.funbox.iter().any(|name| { + ![ + "58008", + "binary", + "hexadecimal", + "gibberish", + "ascii", + "specials", + "backwards", + "ALL_CAPS", + "capitals", + "sPoNgEcAsE", + "rAnDoMcAsE", + "rot13", + "ddoouubblleedd", + "instant_messaging", + "underscore_spaces", + "nospace", + "plus_zero", + "plus_one", + "plus_two", + "plus_three", + "zipf", + ] + .contains(&name.as_str()) + }) { return Err("Requested generator transform is not yet ported".into()); } @@ -168,11 +193,14 @@ impl Generator { { return Err("Invalid custom mode or limit".into()); } - let wordset = if config.mode == "custom" { + let mut wordset = if config.mode == "custom" { custom.as_ref().unwrap().text.clone() } else { language.words.clone() }; + if config.funbox.iter().any(|name| name == "backwards") { + wordset.reverse(); + } if config.mode != "zen" && (wordset.is_empty() || wordset.iter().all(Text::is_empty)) { return Err("Content has no words".into()); } @@ -193,6 +221,7 @@ impl Generator { previous: vec![], repeating: false, generated: 0, + punctuation: crate::punctuation::Punctuation::default(), }) } fn limit(&self) -> usize { @@ -218,6 +247,21 @@ impl Generator { if self.config.mode == "words" && self.config.words > 0 { limit = limit.min(self.config.words); } + for funbox in &self.config.funbox { + if let Some(count) = match funbox.as_str() { + "plus_zero" => Some(1), + "plus_one" => Some(2), + "plus_two" => Some(3), + "plus_three" => Some(4), + _ => None, + } { + limit = count; + break; + } + } + if self.config.mode == "words" && self.config.words > 0 { + limit = limit.min(self.config.words); + } limit } pub fn all_generated(&self) -> bool { @@ -239,7 +283,12 @@ impl Generator { false } fn random_word(&mut self) -> Result { - Ok(self.wordset[self.random.index(self.wordset.len())?].clone()) + let index = if self.config.funbox.iter().any(|name| name == "zipf") { + self.random.zipf_index(self.wordset.len())? + } else { + self.random.index(self.wordset.len())? + }; + Ok(self.wordset[index].clone()) } fn next_ordered(&mut self) -> Text { if self.ordered >= self.wordset.len() { @@ -321,7 +370,7 @@ impl Generator { pub fn next( &mut self, index: usize, - _bound: usize, + bound: usize, previous: Option<&Text>, previous2: Option<&Text>, ) -> Result { @@ -333,6 +382,7 @@ impl Generator { return Ok(next.clone()); } let mut word = self.random_word()?; + let previous_original = previous.cloned(); let previous = previous.map(|word| clean_previous(word, false)); let previous2 = previous2.map(|word| clean_previous(word, true)); if self.section.is_empty() { @@ -389,13 +439,15 @@ impl Generator { && (previous.as_ref() == Some(&first) || previous2.as_ref() == Some(&first) || (self.config.mode != "custom" - && (word.0 == [73] - || (!self.config.language.starts_with("code") + && ((!self.config.punctuation && word.0 == [73]) + || (!self.config.punctuation + && !self.config.language.starts_with("code") && word.0.iter().any(|unit| { b"-=_+[]{};'\\:\"|,./<>?".contains(&(*unit as u8)) && *unit < 128 })) - || word.0.iter().any(|unit| (48..=57).contains(unit))))) + || (!self.config.numbers + && word.0.iter().any(|unit| (48..=57).contains(unit)))))) { retries += 1; word = self.random_word()?; @@ -409,6 +461,20 @@ impl Generator { } } word = collapse_spaces(word); + for name in &self.config.funbox { + if let Some(generated) = crate::transforms::generated_word(name, &mut self.random)? + { + word = if name == "58008" + && (self.config.language.starts_with("kurdish") + || self.config.language.starts_with("nepali")) + { + crate::transforms::numeral_script(generated, &self.config.language) + } else { + generated + }; + break; + } + } self.section = word .0 .split(|unit| *unit == 32) @@ -422,6 +488,10 @@ impl Generator { return Err("Random word is empty".into()); } if self.config.mode != "custom" + && !self.config.punctuation + && !self.config.funbox.iter().any(|name| { + ["binary", "hexadecimal", "gibberish", "ascii", "specials"].contains(&name.as_str()) + }) && word.0.iter().any(|unit| (65..=90).contains(unit)) && !["german", "swiss_german", "code", "klingon"] .iter() @@ -441,8 +511,36 @@ impl Generator { } word = Text(out); } + if self.config.punctuation && !self.language.original_punctuation { + word = self.punctuation.apply( + word, + crate::punctuation::PunctuationContext { + previous: previous_original.as_ref(), + index, + bound, + language: &self.config.language, + funboxes: &self.config.funbox, + }, + &mut self.random, + )?; + } + if self.config.numbers && self.random.draw()? < 0.1 { + word = crate::transforms::numeral_script( + crate::transforms::numbers(&mut self.random, 4)?, + &self.config.language, + ); + } + for name in &self.config.funbox { + word = crate::transforms::alter(name, word, index, bound, &mut self.random)?; + } let mut committed = word.clone(); - if committed.0.last() != Some(&10) { + if committed.0.last() != Some(&10) + && !self + .config + .funbox + .iter() + .any(|name| ["nospace", "underscore_spaces"].contains(&name.as_str())) + { committed.0.push(32); } let next = NextWord { diff --git a/crates/typing-engine/src/lib.rs b/crates/typing-engine/src/lib.rs index 140ae1280..65b92d1b6 100644 --- a/crates/typing-engine/src/lib.rs +++ b/crates/typing-engine/src/lib.rs @@ -1,10 +1,12 @@ //! Platform-independent typing rules. Hosts provide content, observations and time. pub mod generation; pub mod pace; +pub mod punctuation; pub mod random; pub mod session; pub mod stats; pub mod text; +pub mod transforms; pub mod weak_spot; pub use session::{Command, Config, Session}; diff --git a/crates/typing-engine/src/punctuation.rs b/crates/typing-engine/src/punctuation.rs new file mode 100644 index 000000000..5d2f60957 --- /dev/null +++ b/crates/typing-engine/src/punctuation.rs @@ -0,0 +1,276 @@ +use crate::{Text, generation::case, random::Random}; +#[derive(Default)] +pub struct Punctuation { + spanish: Option, +} +pub struct PunctuationContext<'a> { + pub previous: Option<&'a Text>, + pub index: usize, + pub bound: usize, + pub language: &'a str, + pub funboxes: &'a [String], +} +fn add(word: &mut Text, value: &str) { + word.0.extend(value.encode_utf16()); +} +fn capitalize(word: Text) -> Text { + let Some(first) = word.0.first() else { + return word; + }; + let mut result = case(&Text(vec![*first]), true); + result.0.extend_from_slice(&word.0[1..]); + result +} +fn wrap(word: Text, prefix: &str, suffix: &str) -> Text { + let mut result = Text::from(prefix); + result.0.extend(word.0); + add(&mut result, suffix); + result +} +fn english(word: Text, random: &mut Random) -> Result { + const PAIRS: &[(&str, &[&str])] = &[ + ("are", &["aren't"]), + ("can", &["can't"]), + ("could", &["couldn't"]), + ("did", &["didn't"]), + ("does", &["doesn't"]), + ("do", &["don't"]), + ("had", &["hadn't"]), + ("has", &["hasn't"]), + ("have", &["haven't"]), + ("is", &["isn't"]), + ("it", &["it's", "it'll"]), + ("i", &["i'm", "i'll", "i've", "i'd"]), + ("you", &["you'll", "you're", "you've", "you'd"]), + ("that", &["that's", "that'll", "that'd"]), + ("must", &["mustn't", "must've"]), + ("there", &["there's", "there'll", "there'd"]), + ("he", &["he's", "he'll", "he'd"]), + ("she", &["she's", "she'll", "she'd"]), + ("we", &["we're", "we'll", "we'd"]), + ("they", &["they're", "they'll", "they'd"]), + ("should", &["shouldn't", "should've"]), + ("was", &["wasn't"]), + ("were", &["weren't"]), + ("will", &["won't"]), + ("would", &["wouldn't", "would've"]), + ("going", &["goin'"]), + ]; + let ascii_word = + |unit: &u16| *unit < 128 && ((*unit as u8).is_ascii_alphanumeric() || *unit == 95); + let Some(start) = word.0.iter().position(ascii_word) else { + return Ok(word); + }; + let end = word.0.iter().rposition(ascii_word).unwrap() + 1; + let middle = Text(word.0[start..end].to_vec()); + let lower = case(&middle, false); + let Some((_, replacements)) = PAIRS.iter().find(|(key, _)| Text::from(*key) == lower) else { + return Ok(word); + }; + let mut replacement = Text::from(replacements[random.index(replacements.len())?]); + if case(&Text(vec![middle.0[0]]), true).0 == [middle.0[0]] { + replacement = if middle.0 != [73] && middle == case(&middle, true) { + case(&replacement, true) + } else { + capitalize(replacement) + }; + } + let mut result = Text(word.0[..start].to_vec()); + result.0.extend(replacement.0); + result.0.extend_from_slice(&word.0[end..]); + Ok(result) +} +impl Punctuation { + pub fn apply( + &mut self, + mut word: Text, + context: PunctuationContext<'_>, + random: &mut Random, + ) -> Result { + let PunctuationContext { + previous, + index, + bound, + language, + funboxes, + } = context; + if funboxes.iter().any(|name| name == "hexadecimal") { + return Ok(wrap(word, "0x", "")); + } + if funboxes.iter().any(|name| name == "58008") { + if word.len() > 3 { + if random.draw()? < 0.5 { + let index = random.index(word.len() - 2)? + 1; + word.0[index] = 46; + } + if random.draw()? < 0.75 { + let index = random.index(word.len() - 2)? + 1; + if word.0[index - 1] != 46 && word.0[index + 1] != 46 && word.0[index + 1] != 48 + { + word.0[index] = [47, 42, 45, 43][random.index(4)?]; + } + } + } + return Ok(word); + } + let lang = language.split('_').next().unwrap_or_default(); + let last = previous.and_then(|word| word.0.last()).copied(); + let no_end = !matches!(last, Some(46 | 44)); + if lang != "code" + && lang != "georgian" + && (index == 0 || matches!(last, Some(63 | 33 | 46 | 0x61f))) + { + word = capitalize(word); + if lang == "turkish" { + for unit in &mut word.0 { + if *unit == 73 { + *unit = 0x130; + } + } + } + if lang == "spanish" { + let draw = random.draw()?; + if draw > 0.9 { + word = wrap(word, "¿", ""); + self.spanish = Some(63); + } else if draw > 0.8 { + word = wrap(word, "¡", ""); + self.spanish = Some(33); + } + } + } else if (random.draw()? < 0.1 && no_end && index != bound.saturating_sub(2)) + || index + 1 == bound + { + if lang == "spanish" { + if let Some(end) = self.spanish.take() { + word.0.push(end); + } + } else { + let draw = random.draw()?; + if draw <= 0.8 { + add( + &mut word, + match lang { + "nepali" | "bangla" | "hindi" => "।", + "japanese" | "chinese" => "。", + _ => ".", + }, + ); + } else if draw > 0.8 && draw < 0.9 { + if lang == "french" { + word = Text::from("?"); + } else { + add( + &mut word, + match lang { + "arabic" | "persian" | "urdu" | "kurdish" => "؟", + "greek" => ";", + "japanese" | "chinese" => "?", + _ => "?", + }, + ); + } + } else if lang == "french" { + word = Text::from("!"); + } else { + add( + &mut word, + if ["japanese", "chinese"].contains(&lang) { + "!" + } else { + "!" + }, + ); + } + } + } else if random.draw()? < 0.01 && no_end && lang != "russian" { + word = wrap(word, "\"", "\""); + } else if random.draw()? < 0.011 + && no_end + && !["russian", "ukrainian", "slovak"].contains(&lang) + { + word = wrap(word, "'", "'"); + } else if random.draw()? < 0.012 && no_end { + word = if lang == "code" { + let brackets = if language.starts_with("code_javascript") { + vec![("(", ")"), ("{", "}"), ("[", "]"), ("<", ">"), ("`", "`")] + } else { + vec![("(", ")"), ("{", "}"), ("[", "]"), ("<", ">")] + }; + let bracket = brackets[random.index(brackets.len())?]; + wrap(word, bracket.0, bracket.1) + } else if ["japanese", "chinese"].contains(&lang) { + wrap(word, "(", ")") + } else { + wrap(word, "(", ")") + }; + } else if random.draw()? < 0.013 + && no_end + && !matches!(last, Some(59 | 0x61b | 58 | 0xff1b | 0xff1a)) + { + if lang == "french" { + word = Text::from(":"); + } else { + add(&mut word, if lang == "chinese" { ":" } else { ":" }); + } + } else if random.draw()? < 0.014 && no_end && previous.is_none_or(|word| word.0 != [45]) { + word = Text::from("-"); + } else if random.draw()? < 0.015 + && no_end + && !matches!(last, Some(59 | 0x61b | 0xff1b | 0xff1a)) + { + if lang == "french" { + word = Text::from(";"); + } else if lang == "greek" { + word = Text::from("."); + } else { + add( + &mut word, + match lang { + "arabic" | "kurdish" => "؛", + "chinese" => ";", + _ => ";", + }, + ); + } + } else if random.draw()? < 0.2 && last != Some(44) { + add( + &mut word, + match lang { + "arabic" | "urdu" | "persian" | "kurdish" => "،", + "japanese" => "、", + "chinese" => ",", + _ => ",", + }, + ); + } else if random.draw()? < 0.25 && lang == "code" { + let basic = ["{", "}", "[", "]", "(", ")", ";", "=", "+", "%", "/"]; + let c = [ + "{", "}", "[", "]", "(", ")", ";", "=", "+", "%", "/", "/*", "*/", "//", "!=", + "==", "<=", ">=", "||", "&&", "<<", ">>", "%=", "&=", "*=", "++", "+=", "--", "-=", + "/=", "^=", "|=", + ]; + let choices = if (language.starts_with("code_c") && !language.starts_with("code_css")) + || language.starts_with("code_arduino") + { + c.to_vec() + } else if language.starts_with("code_javascript") { + let mut choices = basic.to_vec(); + choices.push("`"); + choices + } else { + basic.to_vec() + }; + word = Text::from(choices[random.index(choices.len())?]); + } else if random.draw()? < 0.5 && lang == "english" { + word = english(word, random)?; + } + for unit in [9, 10] { + if word.0.contains(&unit) { + word.0.retain(|value| *value != unit); + word.0.push(unit); + } + } + Ok(word) + } +} diff --git a/crates/typing-engine/tests/generation.rs b/crates/typing-engine/tests/generation.rs index f9434ee78..93543737f 100644 --- a/crates/typing-engine/tests/generation.rs +++ b/crates/typing-engine/tests/generation.rs @@ -31,11 +31,34 @@ fn base_generation_matches_immutable_browser_snapshots() { let mut checked = 0; for case in cases { let config: GenerationConfig = serde_json::from_value(case["config"].clone()).unwrap(); - if config.punctuation - || config.numbers - || config.lazy_mode + if config.lazy_mode || config.british_english - || !config.funbox.is_empty() + || config.funbox.iter().any(|name| { + ![ + "58008", + "binary", + "hexadecimal", + "gibberish", + "ascii", + "specials", + "backwards", + "ALL_CAPS", + "capitals", + "sPoNgEcAsE", + "rAnDoMcAsE", + "rot13", + "ddoouubblleedd", + "instant_messaging", + "underscore_spaces", + "nospace", + "plus_zero", + "plus_one", + "plus_two", + "plus_three", + "zipf", + ] + .contains(&name.as_str()) + }) || config.mode == "quote" { continue; @@ -105,5 +128,5 @@ fn base_generation_matches_immutable_browser_snapshots() { assert_eq!(actual, expected, "{name}"); checked += 1; } - assert!(checked >= 15, "Only {checked} cases exercised"); + assert!(checked >= 40, "Only {checked} cases exercised"); } From 2ed8d590df9f407f71597ae86f2bca49562ecf59 Mon Sep 17 00:00:00 2001 From: Lakshmi Tanmay Date: Fri, 23 Oct 2026 00:47:46 +0530 Subject: [PATCH 23/93] Port lazy and British language generation without changing snapshots --- crates/typing-engine/data/accents.json | 43 ++ .../typing-engine/data/british-english.json | 677 ++++++++++++++++++ crates/typing-engine/src/generation.rs | 94 ++- crates/typing-engine/src/language_rules.rs | 134 ++++ crates/typing-engine/src/lib.rs | 1 + crates/typing-engine/tests/generation.rs | 69 +- docs/rust-engine/progress.md | 22 +- 7 files changed, 971 insertions(+), 69 deletions(-) create mode 100644 crates/typing-engine/data/accents.json create mode 100644 crates/typing-engine/data/british-english.json create mode 100644 crates/typing-engine/src/language_rules.rs diff --git a/crates/typing-engine/data/accents.json b/crates/typing-engine/data/accents.json new file mode 100644 index 000000000..cac98bf7a --- /dev/null +++ b/crates/typing-engine/data/accents.json @@ -0,0 +1,43 @@ +[ + ["áàâäåãąą́āą̄ă", "a"], + ["éèêëẽęę́ēę̄ėě", "e"], + ["íìîïĩįį́īį̄ı", "i"], + ["óòôöøõóōǫǫ́ǭő", "o"], + ["úùûüŭũúūůű", "u"], + ["ńň", "n"], + ["çĉčć", "c"], + ["řŕṛ", "r"], + ["ďđḍ", "d"], + ["ťțṭ", "t"], + ["ṃ", "m"], + ["æ", "ae"], + ["œ", "oe"], + ["ẅŵ", "w"], + ["ĝğg̃", "g"], + ["ĥ", "h"], + ["ĵ", "j"], + ["ńṇṅ", "n"], + ["ŝśšșşṣ", "s"], + ["ß", "ss"], + ["żźž", "z"], + ["ÿỹýÿŷ", "y"], + ["łľĺ", "l"], + ["أإآ", "ا"], + ["َ", ""], + ["ُ", ""], + ["ِ", ""], + ["ْ", ""], + ["ً", ""], + ["ٌ", ""], + ["ٍ", ""], + ["ّ", ""], + ["ё", "е"], + ["ά", "α"], + ["έ", "ε"], + ["ί", "ι"], + ["ύ", "υ"], + ["ό", "ο"], + ["ή", "η"], + ["ώ", "ω"], + ["þ", "th"] +] diff --git a/crates/typing-engine/data/british-english.json b/crates/typing-engine/data/british-english.json new file mode 100644 index 000000000..35e7ab706 --- /dev/null +++ b/crates/typing-engine/data/british-english.json @@ -0,0 +1,677 @@ +{ + "laborers": "labourers", + "industrialized": "industrialised", + "acclimatizing": "acclimatising", + "acclimatized": "acclimatised", + "behoove": "behove", + "reorganization": "reorganisation", + "galvanize": "galvanise", + "demagogism": "demagoguism", + "canceled": "cancelled", + "baptize": "baptise", + "revitalize": "revitalise", + "skeptical": "sceptical", + "piscine": "piscinae", + "ionization": "ionisation", + "chili": "chilli", + "dexterous": "dextrous", + "likable": "likeable", + "neutralize": "neutralise", + "meter": "metre", + "meters": "metres", + "centimeter": "centimetre", + "hurrah": "hooray", + "multicolor": "multicolour", + "actualization": "actualisation", + "fertilizing": "fertilising", + "enamor": "enamour", + "skepticism": "scepticism", + "authorize": "authorise", + "minimize": "minimise", + "bandoleer": "bandolier", + "rigor": "rigour", + "fledgling": "fledgeling", + "mesquite": "mesquit", + "fogy": "fogey", + "characterize": "characterise", + "wagonette": "waggonette", + "plagiarize": "plagiarise", + "kilometers": "kilometres", + "molt": "moult", + "leveler": "leveller", + "checkered": "chequered", + "vendor": "vender", + "candor": "candour", + "moralize": "moralise", + "denationalization": "denationalisation", + "anemia": "anaemia", + "caiman": "cayman", + "chitterlings": "chitlins", + "acclimated": "acclimatised", + "acclimate": "acclimatise", + "skillful": "skilful", + "enamored": "enamoured", + "watercolor": "watercolour", + "extemporization": "extemporisation", + "lackluster": "lacklustre", + "equalizer": "equaliser", + "cipher": "cypher", + "polarization": "polarisation", + "polymerization": "polymerisation", + "ionize": "ionise", + "anesthetic": "anaesthetic", + "aerie": "eyrie", + "lachrymal": "lacrimal", + "decimeter": "decimetre", + "rajah": "raja", + "modernize": "modernise", + "gasoline": "petrol", + "alphabetized": "alphabetised", + "saber": "sabre", + "burglarize": "burglarise", + "terrorize": "terrorise", + "enthrall": "enthral", + "mustachioed": "moustachioed", + "anesthesia": "anaesthesia", + "sepulcher": "sepulchre", + "caftan": "kaftan", + "organize": "organise", + "manikin": "mannikin", + "dexterously": "dextrously", + "artifact": "artefact", + "kilometer": "kilometre", + "mommy": "mummy", + "brant": "brent", + "hyena": "hyaena", + "fuze": "fuse", + "victimize": "victimise", + "jihad": "jehad", + "airplane": "aeroplane", + "laborer": "labourer", + "vaporware": "vapourware", + "worshiped": "worshipped", + "taboret": "tabouret", + "rationalize": "rationalise", + "whimsy": "whimsey", + "scandalize": "scandalise", + "categorization": "categorisation", + "centered": "centred", + "kerosene": "kerosine", + "specialize": "specialise", + "specialized": "specialised", + "cicatrix": "cicatrice", + "jailer": "jailor", + "unfavorable": "unfavourable", + "guerrilla": "guerilla", + "emphasize": "emphasise", + "wacky": "whacky", + "ionizing": "ionising", + "czarina": "tzarina", + "ratline": "ratlin", + "abolitionize": "abolitionise", + "subsidize": "subsidise", + "plowing": "ploughing", + "untrammeled": "untrammelled", + "worshiping": "worshipping", + "memorization": "memorisation", + "anglicize": "anglicise", + "finalize": "finalise", + "gases": "gasses", + "fertilization": "fertilisation", + "libeling": "libelling", + "kinesthetic": "kinaesthetic", + "immunization": "immunisation", + "coloration": "colouration", + "overdraft": "overdraught", + "lambaste": "lambast", + "unpasteurized": "unpasteurised", + "ascendance": "ascendence", + "jeweler": "jeweller", + "fervor": "fervour", + "jeopardize": "jeopardise", + "abridgment": "abridgement", + "monopolize": "monopolise", + "colorless": "colourless", + "colorlessness": "colourlessness", + "tumors": "tumours", + "jinni": "jinn", + "aluminum": "aluminium", + "salable": "saleable", + "bronco": "broncho", + "lupine": "lupin", + "linchpin": "lynchpin", + "mesmerize": "mesmerise", + "internationalization": "internationalisation", + "anapestic": "anapaestic", + "supersede": "supercede", + "europeanize": "europeanise", + "optimized": "optimised", + "botanize": "botanise", + "organizational": "organisational", + "heme": "haem", + "estivation": "aestivation", + "edema": "oedema", + "penalize": "penalise", + "peddler": "pedlar", + "biased": "biassed", + "aggrandize": "aggrandise", + "rationalizing": "rationalising", + "deputize": "deputise", + "centers": "centres", + "unicolor": "unicolour", + "hypnotized": "hypnotised", + "anglicized": "anglicised", + "initialization": "initialisation", + "topee": "topi", + "nightie": "nighty", + "tricolor": "tricolour", + "reorganize": "reorganise", + "barbecue": "barbeque", + "fertilized": "fertilised", + "synthesize": "synthesise", + "economize": "economise", + "colter": "coulter", + "paleontologist": "palaeontologist", + "acclimatize": "acclimatise", + "idealize": "idealise", + "fossilization": "fossilisation", + "lionize": "lionise", + "acclimating": "acclimatising", + "luster": "lustre", + "demeanor": "demeanour", + "unauthorized": "unauthorised", + "smolder": "smoulder", + "abettor": "abetter", + "jewelry": "jewellery", + "demoralization": "demoralisation", + "fullness": "fulness", + "neighbored": "neighboured", + "neighbor": "neighbour", + "neighbors": "neighbours", + "neighboring": "neighbouring", + "neighborly": "neighbourly", + "neighborhood": "neighbourhood", + "neighborhoods": "neighbourhoods", + "magnetizer": "magnetiser", + "appetizer": "appetiser", + "hebraizing": "hebraising", + "carbonization": "carbonisation", + "favorably": "favourably", + "favorite": "favourite", + "behavioral": "behavioural", + "cognizant": "cognisant", + "skeptic": "sceptic", + "fiber": "fibre", + "fibers": "fibres", + "labeled": "labelled", + "analyze": "analyse", + "disorganization": "disorganisation", + "concenter": "concentre", + "categorize": "categorise", + "novelization": "novelisation", + "proselytize": "proselytise", + "draftsman": "draughtsman", + "paralyze": "paralyse", + "ostracize": "ostracise", + "italicize": "italicise", + "micrometer": "micrometre", + "cognizance": "cognisance", + "saltpeter": "saltpetre", + "conceptualization": "conceptualisation", + "containerization": "containerisation", + "anapest": "anapaest", + "leukemia": "leukaemia", + "omelet": "omelette", + "industrialize": "industrialise", + "characterization": "characterisation", + "amortize": "amortise", + "mazurka": "mazourka", + "macadamize": "macadamise", + "stymie": "stymy", + "analog": "analogue", + "prizer": "priser", + "cauterize": "cauterise", + "dysmenorrhea": "dysmenorrhoea", + "hippie": "hippy", + "mechanization": "mechanisation", + "commercialize": "commercialise", + "unsavory": "unsavoury", + "somber": "sombre", + "esophageal": "oesophageal", + "iodized": "iodised", + "tyrannize": "tyrannise", + "liberalize": "liberalise", + "computerization": "computerisation", + "synthesizer": "synthesiser", + "fertilizes": "fertilises", + "airdrome": "aerodrome", + "uncivilized": "uncivilised", + "mold": "mould", + "nitrogenized": "nitrogenised", + "legalize": "legalise", + "tantalize": "tantalise", + "eulogize": "eulogise", + "depolarization": "depolarisation", + "fecal": "faecal", + "bastardize": "bastardise", + "summarize": "summarise", + "cony": "coney", + "authorization": "authorisation", + "colorful": "colourful", + "temporized": "temporised", + "adviser": "advisor", + "tendinitis": "tendonitis", + "crystallization": "crystallisation", + "humor": "humour", + "humorous": "humourous", + "chanty": "shanty", + "dyspnea": "dyspnoea", + "aging": "ageing", + "mousy": "mousey", + "succor": "succour", + "distill": "distil", + "decentralization": "decentralisation", + "cauterization": "cauterisation", + "specter": "spectre", + "snowplow": "snowplough", + "pediatrics": "paediatrics", + "discoloration": "discolouration", + "tumor": "tumour", + "empathize": "empathise", + "pawpaw": "papaya", + "aggrandizement": "aggrandisement", + "fontanel": "fontanelle", + "brunet": "brunette", + "rumor": "rumour", + "epaulet": "epaulette", + "glycerin": "glycerine", + "fraternize": "fraternise", + "latinize": "latinise", + "sniveling": "snivelling", + "pasteurize": "pasteurise", + "modernization": "modernisation", + "patronize": "patronise", + "totaled": "totalled", + "normalization": "normalisation", + "niter": "nitre", + "shoveler": "shoveller", + "flier": "flyer", + "equalization": "equalisation", + "meagerly": "meagrely", + "favoritism": "favouritism", + "libelous": "libellous", + "popularized": "popularised", + "armory": "armoury", + "humanization": "humanisation", + "minuscule": "miniscule", + "collectible": "collectable", + "tepee": "teepee", + "stabilization": "stabilisation", + "moldy": "mouldy", + "colonize": "colonise", + "idealized": "idealised", + "meager": "meagre", + "armorer": "armourer", + "vitalize": "vitalise", + "fossilized": "fossilised", + "antagonize": "antagonise", + "criticize": "criticise", + "liter": "litre", + "stigmatize": "stigmatise", + "theorize": "theorise", + "combated": "combatted", + "desalination": "desalinisation", + "evangelize": "evangelise", + "idolization": "idolisation", + "smoldering": "smouldering", + "commercialization": "commercialisation", + "laureled": "laurelled", + "hematology": "haematology", + "fulfill": "fulfil", + "disorganized": "disorganised", + "accouterments": "accoutrements", + "busses": "buses", + "utilize": "utilise", + "enroll": "enrol", + "goiter": "goitre", + "stylized": "stylised", + "diarrhea": "diarrhoea", + "hypothesize": "hypothesise", + "esthetic": "aesthetic", + "dishonor": "dishonour", + "victualer": "victualler", + "paleolithic": "palaeolithic", + "sanatorium": "sanitarium", + "feminization": "feminisation", + "flotation": "floatation", + "teetotaler": "teetotaller", + "feudalization": "feudalisation", + "visualize": "visualise", + "fertilize": "fertilise", + "czar": "tzar", + "nationalization": "nationalisation", + "septicemia": "septicaemia", + "organizer": "organiser", + "recrystallization": "recrystallisation", + "dolor": "dolour", + "agonizing": "agonising", + "romanization": "romanisation", + "synchronize": "synchronise", + "amorist": "amourist", + "agonized": "agonised", + "belabor": "belabour", + "caster": "castor", + "softy": "softie", + "apothegm": "apophthegm", + "demobilization": "demobilisation", + "agonizes": "agonises", + "misdemeanor": "misdemeanour", + "recognizable": "recognisable", + "asshole": "arsehole", + "brutalization": "brutalisation", + "chamois": "shammy", + "clamor": "clamour", + "fertilizer": "fertiliser", + "deodorization": "deodorisation", + "privatization": "privatisation", + "tidbit": "titbit", + "molding": "moulding", + "alleluia": "halleluiah", + "canonization": "canonisation", + "customize": "customise", + "customization": "customisations", + "customizer": "customiser", + "customizers": "customisers", + "customizes": "customises", + "customizing": "customising", + "popularize": "popularise", + "specialty": "speciality", + "meagerness": "meagreness", + "esophagus": "oesophagus", + "millimeter": "millimetre", + "harmonization": "harmonisation", + "atomizer": "atomiser", + "dieresis": "diaeresis", + "maneuver": "manoeuvre", + "capitalize": "capitalise", + "gynecologist": "gynaecologist", + "acclimation": "acclimatisation", + "generalize": "generalise", + "temporize": "temporise", + "premunire": "praemunire", + "estival": "aestival", + "selvage": "selvedge", + "brier": "briar", + "hyperesthesia": "hyperaesthesia", + "maximize": "maximise", + "draftsmen": "draughtsmen", + "colonization": "colonisation", + "capitalization": "capitalisation", + "tenia": "taenia", + "paleontology": "palaeontology", + "libeler": "libeller", + "arytenoid": "arytaenoid", + "wainscoting": "wainscotting", + "globalization": "globalisation", + "legalization": "legalisation", + "magnetization": "magnetisation", + "stabilize": "stabilise", + "mesmerized": "mesmerised", + "savory": "savoury", + "reconnoiter": "reconnoitre", + "bastardized": "bastardised", + "rancor": "rancour", + "checker": "chequer", + "industrialization": "industrialisation", + "epitomize": "epitomise", + "socialize": "socialise", + "externalization": "externalisation", + "chivy": "chivvy", + "optimize": "optimise", + "romanize": "romanise", + "vulcanize": "vulcanise", + "plow": "plough", + "memorize": "memorise", + "sterilization": "sterilisation", + "routing": "routeing", + "raveling": "ravelling", + "savor": "savour", + "harmonize": "harmonise", + "cannibalize": "cannibalise", + "vaporize": "vaporise", + "utilization": "utilisation", + "hybridization": "hybridisation", + "mechanize": "mechanise", + "arbor": "arbour", + "arbors": "arbours", + "licorice": "liquorice", + "counselor": "counsellor", + "orthopedic": "orthopaedic", + "lusterless": "lustreless", + "numskull": "numbskull", + "paleozoic": "palaeozoic", + "malodor": "malodour", + "amenorrhea": "amenorrhoea", + "criticized": "criticised", + "gonorrhea": "gonorrhoea", + "fossilize": "fossilise", + "revolutionize": "revolutionise", + "woolen": "woollen", + "dishabille": "deshabille", + "organdy": "organdie", + "asses": "arses", + "furor": "furore", + "paycheck": "paycheque", + "pulverize": "pulverise", + "dramatization": "dramatisation", + "yodeler": "yodeller", + "apologize": "apologise", + "apologized": "apologised", + "blond": "blonde", + "oxidize": "oxidise", + "extemporize": "extemporise", + "whiz": "whizz", + "edile": "aedile", + "appall": "appal", + "legitimize": "legitimise", + "urbanization": "urbanisation", + "magnetize": "magnetise", + "womanize": "womanise", + "orthopedics": "orthopaedics", + "cognize": "cognise", + "christianization": "christianisation", + "vocalize": "vocalise", + "individualization": "individualisation", + "hemoglobin": "haemoglobin", + "racket": "racquet", + "agonize": "agonise", + "aggrandizes": "aggrandises", + "accouter": "accoutre", + "mollusk": "mollusc", + "publicize": "publicise", + "fraternization": "fraternisation", + "odorless": "odourless", + "odor": "odour", + "odorous": "odourous", + "rationalization": "rationalisation", + "pretense": "pretence", + "louver": "louvre", + "kopeck": "kopek", + "lissome": "lissom", + "idealization": "idealisation", + "nosy": "nosey", + "stogie": "stogy", + "althea": "althaea", + "immobilization": "immobilisation", + "majolica": "maiolica", + "savior": "saviour", + "civilize": "civilise", + "granddad": "grandad", + "centralization": "centralisation", + "localization": "localisation", + "evangelization": "evangelisation", + "ruble": "rouble", + "pajamas": "pyjamas", + "sterilize": "sterilise", + "math": "maths", + "localize": "localise", + "hospitalization": "hospitalisation", + "scrutinize": "scrutinise", + "acclimatization": "acclimatisation", + "misbehavior": "misbehaviour", + "behavior": "behaviour", + "color": "colour", + "tire": { + "britishWord": "tyre", + "exceptPreviousWords": ["will"] + }, + "gray": "grey", + "grays": "greys", + "theater": "theatre", + "amphitheater": "amphitheatre", + "center": "centre", + "realize": "realise", + "realizing": "realising", + "realization": "realisation", + "license": "licence", + "mom": "mum", + "mom'l": "mum'll", + "favor": "favour", + "favors": "favours", + "favorites": "favourites", + "analyzed": "analysed", + "characterized": "characterised", + "colored": "coloured", + "colors": "colours", + "counseling": "counselling", + "customized": "customised", + "defense": "defence", + "offense": "offence", + "enrollment": "enrolment", + "flavor": "flavour", + "flavors": "flavours", + "flavoring": "flavouring", + "harbor": "harbour", + "harbored": "harboured", + "honor": "honour", + "honors": "honours", + "labor": "labour", + "licensed": "licenced", + "licenses": "licences", + "licensing": "licencing", + "modeling": "modelling", + "optimization": "optimisation", + "organization": "organisation", + "organizations": "organisations", + "organized": "organised", + "organizing": "organising", + "pediatric": "paediatric", + "personalized": "personalised", + "practice": "practise", + "realized": "realised", + "recognize": "recognise", + "recognized": "recognised", + "specialties": "specialities", + "specializing": "specialising", + "theaters": "theatres", + "travelers": "travellers", + "traveler": "traveller", + "traveling": "travelling", + "authorized": "authorised", + "behaviors": "behaviours", + "traveled": "travelled", + "utilizing": "utilising", + "utilized": "utilised", + "honored": "honoured", + "specializes": "specialises", + "practicing": "practising", + "signaling": "signalling", + "analyzing": "analysing", + "recognizes": "recognises", + "organizers": "organisers", + "visualization": "visualisation", + "standardized": "standardised", + "favorable": "favourable", + "analyzer": "analyser", + "recognizing": "recognising", + "uncategorized": "uncategorised", + "categorized": "categorised", + "vapor": "vapour", + "synchronization": "synchronisation", + "synchronized": "synchronised", + "rumors": "rumours", + "coloring": "colouring", + "honorable": "honourable", + "generalized": "generalised", + "generalization": "generalisation", + "mobilize": "mobilise", + "armor": "armour", + "armored": "armoured", + "labeling": "labelling", + "endeavor": "endeavour", + "civilized": "civilised", + "civilization": "civilisation", + "parlor": "parlour", + "tranquilize": "tranquilise", + "tranquillized": "tranquillised", + "tranquillization": "tranquillisation", + "catalog": "catalogue", + "cataloger": "cataloguer", + "cataloging": "cataloguing", + "catalogs": "catalogues", + "cataloged": "catalogued", + "uncataloged": "uncatalogued", + "catalogers": "cataloguers", + "discolored": "discoloured", + "discolor": "discolour", + "encyclopedias": "encyclopaedias", + "endeavored": "endeavoured", + "endeavors": "endeavours", + "endeavoring": "endeavouring", + "judgments": "judgements", + "judgment": "judgement", + "paneling": "panelling", + "defenses": "defences", + "criticizing": "criticising", + "mustache": "moustache", + "emphasized": "emphasised", + "emphasizes": "emphasises", + "emphasizing": "emphasising", + "fetus": "foetus", + "programs": "programmes", + "yogurt": "yoghurt", + "valor": "valour", + "moisturize": "moisturise", + "moisturized": "moisturised", + "moisturizes": "moisturises", + "moisturizing": "moisturising", + "favoring": "favouring", + "marvelous": "marvellous", + "hematuria": "haematuria", + "hemoptysis": "haemoptysis", + "hemorrhoid": "haemorrhoid", + "hemorrhagic": "haemorrhagic", + "hypercalcemia": "hypercalcaemia", + "hyperglycemia": "hyperglycaemia", + "hypoglycemia": "hypoglycaemia", + "toxemia": "toxaemia", + "hypoxemia": "hypoxaemia", + "bacteremia": "bacteraemia", + "hypernatremia": "hypernatraemia", + "hyponatremia": "hyponatraemia", + "leukocytosis": "leucocytosis", + "leukocyte": "leucocyte", + "leukopenia": "leucopenia", + "apnea": "apnoea", + "bradypnea": "bradypnoea", + "tachypnea": "tachypnoea", + "orthopnea": "orthopnoea", + "ileocecal": "ileocaecal", + "metastasize": "metastasise", + "lymphedema": "lymphoedema", + "neuron": "neurone", + "hemianopsia": "hemianopia", + "galactorrhea": "galactorrhoea", + "nebulizer": "nebuliser", + "paresthesia": "paraesthesia" +} diff --git a/crates/typing-engine/src/generation.rs b/crates/typing-engine/src/generation.rs index 06e5b3a94..b186233c9 100644 --- a/crates/typing-engine/src/generation.rs +++ b/crates/typing-engine/src/generation.rs @@ -10,6 +10,8 @@ pub struct Language { pub right_to_left: Option, pub joining_script: bool, pub original_punctuation: bool, + pub no_lazy_mode: bool, + pub additional_accents: Vec<(Text, Text)>, } #[derive(Clone, Debug, Deserialize)] #[serde(default, rename_all = "camelCase")] @@ -86,6 +88,7 @@ pub struct Generator { repeating: bool, generated: usize, punctuation: crate::punctuation::Punctuation, + rules: crate::language_rules::LanguageRules, } pub(crate) fn case(text: &Text, upper: bool) -> Text { let mut output = vec![]; @@ -153,35 +156,32 @@ impl Generator { if config.mode == "quote" { return Err("Quote asset selection is not yet ported".into()); } - if config.lazy_mode - || config.british_english - || config.funbox.iter().any(|name| { - ![ - "58008", - "binary", - "hexadecimal", - "gibberish", - "ascii", - "specials", - "backwards", - "ALL_CAPS", - "capitals", - "sPoNgEcAsE", - "rAnDoMcAsE", - "rot13", - "ddoouubblleedd", - "instant_messaging", - "underscore_spaces", - "nospace", - "plus_zero", - "plus_one", - "plus_two", - "plus_three", - "zipf", - ] - .contains(&name.as_str()) - }) - { + if config.funbox.iter().any(|name| { + ![ + "58008", + "binary", + "hexadecimal", + "gibberish", + "ascii", + "specials", + "backwards", + "ALL_CAPS", + "capitals", + "sPoNgEcAsE", + "rAnDoMcAsE", + "rot13", + "ddoouubblleedd", + "instant_messaging", + "underscore_spaces", + "nospace", + "plus_zero", + "plus_one", + "plus_two", + "plus_three", + "zipf", + ] + .contains(&name.as_str()) + }) { return Err("Requested generator transform is not yet ported".into()); } if config.mode == "custom" && custom.is_none() { @@ -222,6 +222,7 @@ impl Generator { repeating: false, generated: 0, punctuation: crate::punctuation::Punctuation::default(), + rules: crate::language_rules::LanguageRules::new()?, }) } fn limit(&self) -> usize { @@ -290,6 +291,13 @@ impl Generator { }; Ok(self.wordset[index].clone()) } + fn lazy(&self, word: Text) -> Text { + if self.config.lazy_mode && (!self.language.no_lazy_mode || self.config.mode == "custom") { + self.rules.lazy(&word, &self.language.additional_accents) + } else { + word + } + } fn next_ordered(&mut self) -> Text { if self.ordered >= self.wordset.len() { self.ordered = 0; @@ -381,6 +389,20 @@ impl Generator { self.generated = index + 1; return Ok(next.clone()); } + if self.repeating { + let continuation = self.config.mode == "time" + || (self.config.mode == "words" && index < self.config.words) + || (self.config.mode == "custom" + && self.custom.as_ref().is_some_and(|custom| { + custom.limit.mode == "time" + || (custom.limit.mode == "word" && index < custom.limit.value) + || (custom.limit.mode == "section" + && self.section_index < custom.limit.value) + })); + if !continuation { + return Err("Repeated word is undefined".into()); + } + } let mut word = self.random_word()?; let previous_original = previous.cloned(); let previous = previous.map(|word| clean_previous(word, false)); @@ -425,7 +447,7 @@ impl Generator { } } else { let mut retries = 0; - let mut first = case( + let mut first = self.lazy(case( &Text( word.0 .split(|unit| *unit == 32) @@ -434,7 +456,7 @@ impl Generator { .to_vec(), ), false, - ); + )); while retries < 100 && (previous.as_ref() == Some(&first) || previous2.as_ref() == Some(&first) @@ -451,13 +473,13 @@ impl Generator { { retries += 1; word = self.random_word()?; - first = Text( + first = self.lazy(Text( word.0 .split(|unit| *unit == 32) .next() .unwrap_or_default() .to_vec(), - ); + )); } } word = collapse_spaces(word); @@ -500,6 +522,7 @@ impl Generator { word = case(&word, false); } word = collapse_spaces(word); + word = self.lazy(word); if self.config.language.starts_with("swiss_german") { let mut out = vec![]; for unit in word.0 { @@ -524,6 +547,11 @@ impl Generator { &mut self.random, )?; } + if self.config.british_english && self.config.language.contains("english") { + word = self + .rules + .british(word, previous.as_ref(), self.config.mode == "quote"); + } if self.config.numbers && self.random.draw()? < 0.1 { word = crate::transforms::numeral_script( crate::transforms::numbers(&mut self.random, 4)?, diff --git a/crates/typing-engine/src/language_rules.rs b/crates/typing-engine/src/language_rules.rs new file mode 100644 index 000000000..202f68a9f --- /dev/null +++ b/crates/typing-engine/src/language_rules.rs @@ -0,0 +1,134 @@ +use crate::{Text, generation::case}; +use serde::Deserialize; +use std::collections::BTreeMap; +pub struct LanguageRules { + accents: Vec<(Text, Text)>, + british: BTreeMap, +} +#[derive(Deserialize)] +#[serde(untagged)] +enum BritishRule { + Word(String), + WithExceptions { + #[serde(rename = "britishWord")] + word: String, + #[serde(rename = "exceptPreviousWords")] + previous: Vec, + }, +} +impl LanguageRules { + pub fn new() -> Result { + let accents: Vec<(String, String)> = + serde_json::from_str(include_str!("../data/accents.json")) + .map_err(|e| e.to_string())?; + let british = serde_json::from_str(include_str!("../data/british-english.json")) + .map_err(|e| e.to_string())?; + Ok(Self { + accents: accents + .into_iter() + .map(|(a, b)| (Text::from(a.as_str()), Text::from(b.as_str()))) + .collect(), + british, + }) + } + pub fn lazy(&self, word: &Text, additional: &[(Text, Text)]) -> Text { + let uppercase = case(word, true); + let mut cases = vec![]; + let mut index = 0; + while index < word.len() { + let width = if (0xd800..=0xdbff).contains(&word.0[index]) + && word + .0 + .get(index + 1) + .is_some_and(|c| (0xdc00..=0xdfff).contains(c)) + { + 2 + } else { + 1 + }; + cases.push(width == 1 && uppercase.0.get(cases.len()) == word.0.get(index)); + index += width; + } + let mut output = vec![]; + for index in 0..word.len() { + let lookup = case(&Text(word.0[index..].to_vec()), false) + .0 + .first() + .copied(); + let replacement = lookup.and_then(|lookup| { + additional + .iter() + .rev() + .chain(self.accents.iter().rev()) + .find(|(chars, _)| chars.0.contains(&lookup)) + .map(|(_, replacement)| replacement) + }); + if let Some(replacement) = replacement { + for (offset, unit) in replacement.0.iter().enumerate() { + output.extend( + case(&Text(vec![*unit]), cases.get(index + offset) == Some(&true)).0, + ); + } + } else { + output.extend(case(&Text(vec![word.0[index]]), cases.get(index) == Some(&true)).0); + } + } + Text(output) + } + pub fn british(&self, word: Text, previous: Option<&Text>, quote: bool) -> Text { + let mut word = Text( + word.0 + .into_iter() + .map(|c| if c == 34 { 39 } else { c }) + .collect(), + ); + if word.0.contains(&45) { + let mut output = vec![]; + for (index, part) in word.0.split(|c| *c == 45).enumerate() { + if index > 0 { + output.push(45); + } + output.extend(self.british(Text(part.to_vec()), previous, quote).0); + } + return Text(output); + } + let ascii = |c: &u16| *c < 128 && ((*c as u8).is_ascii_alphanumeric() || *c == 95); + let Some(start) = word.0.iter().position(ascii) else { + return word; + }; + let end = word.0.iter().rposition(ascii).unwrap() + 1; + let middle = Text(word.0[start..end].to_vec()); + let key = case(&middle, false).display_lossy(); + let Some(rule) = self.british.get(&key) else { + return word; + }; + let (replacement, exceptions) = match rule { + BritishRule::Word(word) => (word, &[][..]), + BritishRule::WithExceptions { word, previous } => (word, previous.as_slice()), + }; + if quote + && previous.is_some_and(|previous| { + exceptions + .iter() + .any(|exception| Text::from(exception.as_str()) == *previous) + }) + { + return word; + } + let mut replacement = Text::from(replacement.as_str()); + if case(&Text(vec![middle.0[0]]), true).0 == [middle.0[0]] { + if case(&middle, true) == middle { + replacement = case(&replacement, true); + } else if let Some(first) = replacement.0.first() { + let mut title = case(&Text(vec![*first]), true); + title.0.extend_from_slice(&replacement.0[1..]); + replacement = title; + } + } + let mut output = word.0[..start].to_vec(); + output.extend(replacement.0); + output.extend_from_slice(&word.0[end..]); + word = Text(output); + word + } +} diff --git a/crates/typing-engine/src/lib.rs b/crates/typing-engine/src/lib.rs index 65b92d1b6..b5f935b86 100644 --- a/crates/typing-engine/src/lib.rs +++ b/crates/typing-engine/src/lib.rs @@ -1,5 +1,6 @@ //! Platform-independent typing rules. Hosts provide content, observations and time. pub mod generation; +pub mod language_rules; pub mod pace; pub mod punctuation; pub mod random; diff --git a/crates/typing-engine/tests/generation.rs b/crates/typing-engine/tests/generation.rs index 93543737f..d41cc5707 100644 --- a/crates/typing-engine/tests/generation.rs +++ b/crates/typing-engine/tests/generation.rs @@ -6,6 +6,18 @@ use typing_engine::{ }; fn neutral_words(value: &mut Value) { + if let Some(accents) = value + .get_mut("additionalAccents") + .and_then(Value::as_array_mut) + { + for pair in accents { + for text in pair.as_array_mut().unwrap() { + if let Some(value) = text.as_str() { + *text = json!(Text::from(value)); + } + } + } + } if let Some(words) = value.get_mut("words").and_then(Value::as_array_mut) { for word in words { if let Some(value) = word.as_str() { @@ -31,35 +43,32 @@ fn base_generation_matches_immutable_browser_snapshots() { let mut checked = 0; for case in cases { let config: GenerationConfig = serde_json::from_value(case["config"].clone()).unwrap(); - if config.lazy_mode - || config.british_english - || config.funbox.iter().any(|name| { - ![ - "58008", - "binary", - "hexadecimal", - "gibberish", - "ascii", - "specials", - "backwards", - "ALL_CAPS", - "capitals", - "sPoNgEcAsE", - "rAnDoMcAsE", - "rot13", - "ddoouubblleedd", - "instant_messaging", - "underscore_spaces", - "nospace", - "plus_zero", - "plus_one", - "plus_two", - "plus_three", - "zipf", - ] - .contains(&name.as_str()) - }) - || config.mode == "quote" + if config.funbox.iter().any(|name| { + ![ + "58008", + "binary", + "hexadecimal", + "gibberish", + "ascii", + "specials", + "backwards", + "ALL_CAPS", + "capitals", + "sPoNgEcAsE", + "rAnDoMcAsE", + "rot13", + "ddoouubblleedd", + "instant_messaging", + "underscore_spaces", + "nospace", + "plus_zero", + "plus_one", + "plus_two", + "plus_three", + "zipf", + ] + .contains(&name.as_str()) + }) || config.mode == "quote" { continue; } @@ -128,5 +137,5 @@ fn base_generation_matches_immutable_browser_snapshots() { assert_eq!(actual, expected, "{name}"); checked += 1; } - assert!(checked >= 40, "Only {checked} cases exercised"); + assert!(checked >= 48, "Only {checked} cases exercised"); } diff --git a/docs/rust-engine/progress.md b/docs/rust-engine/progress.md index f52d8b4b2..a12072304 100644 --- a/docs/rust-engine/progress.md +++ b/docs/rust-engine/progress.md @@ -12,18 +12,18 @@ empty commits to inflate count. Do not publish production or releases implicitly ## Current position -Stage 1, Phases 1.1–1.3 complete; Phase 1.4 in progress. Read root AGENTS.md and the complete migration +Stage 1, Phases 1.1–1.3 complete; Phases 1.4–1.6 active; full engine gate remains open. Read root AGENTS.md and the complete migration plan before edits. Installed workspace dependencies with frozen lockfile. Phase 1.1 and 1.2 gates passed: inventory/baselines, lossless contract, frozen oracle and mismatch detection. Existing website engine remains authoritative. | Phase | Status | Evidence / remaining gate | | --- | --- | --- | | 1.1 Inventory / baselines | Complete | See inventory.md, features.md, baseline.md and raw measurements. | -| 1.2 Contracts / fixtures | Complete | 13 command traces, 15 oracle/integrity tests; contract and classified gaps. | +| 1.2 Contracts / fixtures | Complete | 19 command traces, 21 oracle/integrity tests; contract and classified gaps. | | 1.3 Native / browser proof | Complete | Native 7 traces; Cloudflare real-browser finite slice; MIME/CSP, lifecycle, boundary benchmark. Budgets frozen. | -| 1.4 Input / lifecycle / timing | Active | Input penalties/code and physical recording now match 14 traces and 8 browser recordings. Browser differences/timing failures still open. | -| 1.5 Generation / configuration | Pending | Requires contract and proof. | -| 1.6 Events / results / replay | Pending | Requires input and generation. | +| 1.4 Input / lifecycle / timing | Active | 19 native/browser command traces; 8 original recordings. Composition, batch failure, deferred work, visible-word bounds and browser deletion observations covered. Full generator/session integration remains open. | +| 1.5 Generation / configuration | Active | 48/61 unchanged generator snapshots. Quote and remaining funboxes pending. | +| 1.6 Events / results / replay | Active | All computed summary fields match 8 original CompletedEvents exactly. Full results, eligibility, replay pending. | | 1.7 Binding / engine gate | Pending | Full three-path parity and budgets. | | 2.1–2.6 Website | Pending | Requires Stage 1 gate. | | 3.1–3.5 Terminal | Pending | Requires Stage 2 gate. | @@ -33,7 +33,7 @@ Phase 1.1 and 1.2 gates passed: inventory/baselines, lossless contract, frozen o - `vp install --frozen-lockfile`: pass; 1,102 packages, lockfile unchanged. - `vp test run --project=@oxytype/typing-core`: pass; 349 tests, 28 files. - Environment: Linux, Node 24.21.0; installed Rust 1.99.1; GitHub CLI authenticated. - Toolchain/binding pins not yet selected. Browser/OS support unverified. + Pins: Rust 1.99.1, wasm-bindgen 0.2.129. Full browser/OS matrix pending. - Phase 1.2: `vp test run packages/typing-core/__test__/rust-oracle.spec.ts`: 15 pass. - Phase 1.2 type-aware lint: pass. Capture created new traces once, refuses overwrite. @@ -66,3 +66,13 @@ None established. Missing verification is pending work, not a passed gate. Read this file, inventory, contract, decisions, then `vp exec git status --short`. Continue the first incomplete gate. Record commands and actual outcomes here; do not rerun completed checks without a new reason. + +## Latest implementation evidence + +- Native and actual Cloudflare-browser Wasm match all 19 frozen command traces. +- Original CompletedEvent summaries match exactly (including floating-point outputs); exhaustive Hangul compatibility hash covers all 65,536 UTF-16 units. +- Pace, weak-spot learning, explicit Mulberry32/strict random streams, 99 frozen transform cases, language punctuation, lazy mode and British conversion implemented. +- Native generator currently matches 48/61 original snapshots; unsupported cases remain explicitly skipped and prevent the full Stage 1 gate. +- Native test, Clippy and format checks passed after language-rule integration. Final current-head browser/build rerun pending. +- Draft Stage 1 PR: https://github.com/voltcrash/oxytype/pull/184. Initial CI native/Wasm, SSR, delivery and frontend checks passed; package routing failure fixed locally (60 routing tests pass). Whole current-head CI not yet verified. +- Stages 2 and 3 have no implementation yet. No production deployment or release published. From b86b02b083bb39dae20dd01b0147d2dda92ca8f5 Mon Sep 17 00:00:00 2001 From: Lakshmi Tanmay Date: Fri, 23 Oct 2026 00:47:47 +0530 Subject: [PATCH 24/93] Validate deserialized random draws and document Mulberry32 contract --- crates/typing-engine/src/random.rs | 3 +++ docs/rust-engine/contract.md | 5 +++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/crates/typing-engine/src/random.rs b/crates/typing-engine/src/random.rs index 20d5d90cd..8169e7d0c 100644 --- a/crates/typing-engine/src/random.rs +++ b/crates/typing-engine/src/random.rs @@ -31,6 +31,9 @@ impl Random { } Self::Stream { values, cursor } => { let value = *values.get(*cursor).ok_or("Random stream exhausted")?; + if !value.is_finite() || !(0.0..1.0).contains(&value) { + return Err("Random stream values must be finite and in [0,1)".into()); + } *cursor += 1; Ok(value) } diff --git a/docs/rust-engine/contract.md b/docs/rust-engine/contract.md index 556c999af..1d8a01f6d 100644 --- a/docs/rust-engine/contract.md +++ b/docs/rust-engine/contract.md @@ -75,7 +75,8 @@ plus residue; preserve clearedNextWord. Host callbacks do not supply word index. Known headless/web differences requiring recorded web cases before input cutover: - Web checks failure/completion after the last multi-input unit and outside active - composition; headless session checks every unit. + composition; headless session checks every unit. Rust now checks at the final + committed unit, proved by the actual-browser master multi-input regression. - Web schedules automatic tabs on a later task; headless session inserts recursively. - Web records DOM-based limits/visible words; headless session has no observations. - Web normalizes prior to ellipsis expansion; headless path checks blocking first. @@ -95,7 +96,7 @@ metadata; transport Zod schemas remain authoritative and mechanically checked. Fixtures supply the exact floating-point random stream in [0,1); exhaustion is an error, not reseeding. Production uses a specified seeded algorithm, host-provided seed, stable draw order; seed algorithm/behavior version accompanies native history. -Legacy generator snapshots require their existing seedrandom stream, not matching +Legacy generator snapshots require their existing Mulberry32 stream, not matching only distributions. Host asset loaders supply English punctuation/replacement data; Rust decides transform order and random consumption. From d93d29ef9915d26c386b550e4090a4c3e88c7299 Mon Sep 17 00:00:00 2001 From: Lakshmi Tanmay Date: Fri, 23 Oct 2026 00:49:21 +0530 Subject: [PATCH 25/93] Port arrows, IP, pseudolang and weak-spot word generators --- .../typing-engine/src/generated_funboxes.rs | 222 ++++++++++++++++++ crates/typing-engine/src/generation.rs | 50 +++- crates/typing-engine/src/lib.rs | 1 + crates/typing-engine/src/punctuation.rs | 3 + crates/typing-engine/tests/generation.rs | 7 +- 5 files changed, 278 insertions(+), 5 deletions(-) create mode 100644 crates/typing-engine/src/generated_funboxes.rs diff --git a/crates/typing-engine/src/generated_funboxes.rs b/crates/typing-engine/src/generated_funboxes.rs new file mode 100644 index 000000000..1193f1e7c --- /dev/null +++ b/crates/typing-engine/src/generated_funboxes.rs @@ -0,0 +1,222 @@ +use crate::{Text, random::Random}; +use std::collections::BTreeMap; + +#[derive(Default)] +pub struct Arrows { + foot: bool, + facing: i32, + facing_count: i32, + last_left: i32, + last_right: i32, + left_count: i32, + right_count: i32, + initialized: bool, +} +impl Arrows { + pub fn word(&mut self, first: bool, random: &mut Random) -> Result { + if !self.initialized { + self.last_right = 3; + self.initialized = true; + } + let mut result = vec![]; + for index in 0..4 { + self.facing_count -= 1; + let mut step = crate::js_round(random.draw()?) as i32; + // Preserve the unused draw in the original footwork algorithm. + let _ = random.draw()?; + let value; + if index == 0 && first { + self.foot = crate::js_round(random.draw()?) != 0.0; + value = if self.foot { 3 } else { 0 }; + } else { + if self.foot { + self.left_count = if self.last_left == step { + self.left_count + 1 + } else { + 0 + }; + if self.left_count > 1 || (self.right_count > 0 && self.left_count > 0) { + step = 1 - step; + self.left_count = 0; + } + self.last_left = step; + value = step * (self.facing + 1); + } else { + self.right_count = if self.last_right == step { + self.right_count + 1 + } else { + 0 + }; + if self.right_count > 1 || (self.right_count > 0 && self.left_count > 0) { + step = 1 - step; + self.right_count = 0; + } + self.last_right = step; + value = 3 - step * (self.facing + 1); + } + self.foot = !self.foot; + if self.facing_count < 0 && step == 0 { + self.facing = 1 - self.facing; + self.facing_count = random.index(3)? as i32 + 3; + } + } + result.push([0x2190, 0x2193, 0x2191, 0x2192][value as usize]); + } + Ok(Text(result)) + } +} + +pub fn ip(version: u8, random: &mut Random) -> Result { + let mut parts = vec![]; + for _ in 0..if version == 4 { 4 } else { 8 } { + let n = random.index(if version == 4 { 256 } else { 65536 })?; + parts.push(if version == 4 { + n.to_string() + } else { + format!("{n:04x}") + }); + } + Ok(Text::from( + parts.join(if version == 4 { "." } else { ":" }).as_str(), + )) +} +pub fn punctuate_ip(word: Text, random: &mut Random) -> Result { + let text = word.display_lossy(); + let ipv6 = text.contains(':'); + let sep = if ipv6 { ':' } else { '.' }; + let mut parts: Vec = text + .split(sep) + .map(|part| { + u32::from_str_radix(part, if ipv6 { 16 } else { 10 }) + .map_err(|_| "Invalid generated IP".to_string()) + }) + .collect::>()?; + let mut suffix = String::new(); + if random.draw()? < 0.25 { + let mask = if ipv6 { + (random.index(17)? + 16) * 4 + } else { + random.index(25)? + 8 + }; + let bits = if ipv6 { 16 } else { 8 }; + let mut left = mask as i32; + for part in &mut parts { + left -= bits; + if left < 0 { + *part = if -left <= bits { + *part & (((1u32 << bits) - 1) ^ ((1u32 << -left) - 1)) + } else { + 0 + }; + } + } + suffix = format!("/{mask}"); + } + let mut parts: Vec = parts + .into_iter() + .map(|part| { + if ipv6 { + format!("{part:x}") + } else { + part.to_string() + } + }) + .collect(); + if ipv6 { + let mut best = (0, 0); + let mut start = 0; + for index in 0..=parts.len() { + if index == parts.len() || parts[index] != "0" { + if index - start > best.1 { + best = (start, index - start); + } + start = index + 1; + } + } + if best.1 > 1 { + for part in parts.iter_mut().skip(best.0).take(best.1) { + *part = ":".into(); + } + } + } + let mut result = parts.join(&sep.to_string()); + while result.contains(":::") { + result = result.replace(":::", "::"); + } + result.push_str(&suffix); + Ok(Text::from(result.as_str())) +} + +pub struct Pseudolang { + ngrams: BTreeMap, Vec<(Text, usize)>>, +} +impl Pseudolang { + pub fn new(words: &[Text]) -> Self { + let mut ngrams: BTreeMap, Vec<(Text, usize)>> = BTreeMap::new(); + for word in words { + let mut prefix = vec![]; + let mut units = word.0.clone(); + units.push(32); + let mut index = 0; + while index < units.len() { + let width = if (0xd800..=0xdbff).contains(&units[index]) + && units + .get(index + 1) + .is_some_and(|v| (0xdc00..=0xdfff).contains(v)) + { + 2 + } else { + 1 + }; + let next = Text(units[index..index + width].to_vec()); + let choices = ngrams.entry(prefix.clone()).or_default(); + if let Some((_, count)) = choices.iter_mut().find(|(text, _)| text == &next) { + *count += 1; + } else { + choices.push((next.clone(), 1)); + } + prefix.extend(next.0); + if prefix.len() > 2 { + prefix.drain(..prefix.len() - 2); + } + index += width; + } + } + // Object.entries enumerates one-digit integer keys before insertion-ordered characters. + for choices in ngrams.values_mut() { + choices.sort_by_key(|(text, _)| { + if text.len() == 1 && (48..=57).contains(&text.0[0]) { + text.0[0] - 48 + } else { + 10 + } + }); + } + Self { ngrams } + } + pub fn word(&self, random: &mut Random) -> Result { + let mut word = Text(vec![]); + for _ in 0..65536 { + let prefix = &word.0[word.len().saturating_sub(2)..]; + let choices = self + .ngrams + .get(prefix) + .ok_or("Incomplete pseudolang distribution")?; + let mut choice = random.index(choices.iter().map(|(_, count)| count).sum())?; + let mut selected = None; + for (text, count) in choices { + if choice < *count { + selected = Some(text); + break; + } + choice -= count; + } + let selected = selected.ok_or("Empty pseudolang distribution")?; + if selected.0 == [32] { + return Ok(word); + } + word.0.extend_from_slice(&selected.0); + } + Err("Pseudolang generation exceeded bounded work".into()) + } +} diff --git a/crates/typing-engine/src/generation.rs b/crates/typing-engine/src/generation.rs index b186233c9..f0209e8aa 100644 --- a/crates/typing-engine/src/generation.rs +++ b/crates/typing-engine/src/generation.rs @@ -89,6 +89,9 @@ pub struct Generator { generated: usize, punctuation: crate::punctuation::Punctuation, rules: crate::language_rules::LanguageRules, + arrows: crate::generated_funboxes::Arrows, + pseudolang: Option, + pub weak_spot: crate::weak_spot::WeakSpot, } pub(crate) fn case(text: &Text, upper: bool) -> Text { let mut output = vec![]; @@ -158,6 +161,11 @@ impl Generator { } if config.funbox.iter().any(|name| { ![ + "arrows", + "weakspot", + "pseudolang", + "IPv4", + "IPv6", "58008", "binary", "hexadecimal", @@ -223,6 +231,9 @@ impl Generator { generated: 0, punctuation: crate::punctuation::Punctuation::default(), rules: crate::language_rules::LanguageRules::new()?, + arrows: crate::generated_funboxes::Arrows::default(), + pseudolang: None, + weak_spot: crate::weak_spot::WeakSpot::default(), }) } fn limit(&self) -> usize { @@ -284,6 +295,12 @@ impl Generator { false } fn random_word(&mut self) -> Result { + if self.config.funbox.iter().any(|name| name == "pseudolang") { + let generator = self + .pseudolang + .get_or_insert_with(|| crate::generated_funboxes::Pseudolang::new(&self.wordset)); + return generator.word(&mut self.random); + } let index = if self.config.funbox.iter().any(|name| name == "zipf") { self.random.zipf_index(self.wordset.len())? } else { @@ -420,6 +437,7 @@ impl Generator { .as_ref() .is_some_and(|custom| custom.mode == "random") && self.wordset.len() < 4 + && !self.config.funbox.iter().any(|name| name == "pseudolang") { word = self.random_word()?; } else if self.config.mode == "custom" @@ -484,8 +502,21 @@ impl Generator { } word = collapse_spaces(word); for name in &self.config.funbox { - if let Some(generated) = crate::transforms::generated_word(name, &mut self.random)? - { + let generated = match name.as_str() { + "arrows" => Some(self.arrows.word(index == 0, &mut self.random)?), + "IPv4" => Some(crate::generated_funboxes::ip(4, &mut self.random)?), + "IPv6" => Some(crate::generated_funboxes::ip(6, &mut self.random)?), + "weakspot" => { + let mut candidates = vec![]; + for _ in 0..20 { + candidates + .push(self.wordset[self.random.index(self.wordset.len())?].clone()); + } + self.weak_spot.best(&candidates).cloned() + } + _ => crate::transforms::generated_word(name, &mut self.random)?, + }; + if let Some(generated) = generated { word = if name == "58008" && (self.config.language.starts_with("kurdish") || self.config.language.starts_with("nepali")) @@ -512,7 +543,18 @@ impl Generator { if self.config.mode != "custom" && !self.config.punctuation && !self.config.funbox.iter().any(|name| { - ["binary", "hexadecimal", "gibberish", "ascii", "specials"].contains(&name.as_str()) + [ + "binary", + "hexadecimal", + "gibberish", + "ascii", + "specials", + "arrows", + "IPv4", + "IPv6", + "weakspot", + ] + .contains(&name.as_str()) }) && word.0.iter().any(|unit| (65..=90).contains(unit)) && !["german", "swiss_german", "code", "klingon"] @@ -567,7 +609,7 @@ impl Generator { .config .funbox .iter() - .any(|name| ["nospace", "underscore_spaces"].contains(&name.as_str())) + .any(|name| ["nospace", "underscore_spaces", "arrows"].contains(&name.as_str())) { committed.0.push(32); } diff --git a/crates/typing-engine/src/lib.rs b/crates/typing-engine/src/lib.rs index b5f935b86..47fd04ce8 100644 --- a/crates/typing-engine/src/lib.rs +++ b/crates/typing-engine/src/lib.rs @@ -1,4 +1,5 @@ //! Platform-independent typing rules. Hosts provide content, observations and time. +mod generated_funboxes; pub mod generation; pub mod language_rules; pub mod pace; diff --git a/crates/typing-engine/src/punctuation.rs b/crates/typing-engine/src/punctuation.rs index 5d2f60957..2e56f661e 100644 --- a/crates/typing-engine/src/punctuation.rs +++ b/crates/typing-engine/src/punctuation.rs @@ -94,6 +94,9 @@ impl Punctuation { language, funboxes, } = context; + if funboxes.iter().any(|name| name == "IPv4" || name == "IPv6") { + return crate::generated_funboxes::punctuate_ip(word, random); + } if funboxes.iter().any(|name| name == "hexadecimal") { return Ok(wrap(word, "0x", "")); } diff --git a/crates/typing-engine/tests/generation.rs b/crates/typing-engine/tests/generation.rs index d41cc5707..235b27d6e 100644 --- a/crates/typing-engine/tests/generation.rs +++ b/crates/typing-engine/tests/generation.rs @@ -45,6 +45,11 @@ fn base_generation_matches_immutable_browser_snapshots() { let config: GenerationConfig = serde_json::from_value(case["config"].clone()).unwrap(); if config.funbox.iter().any(|name| { ![ + "arrows", + "weakspot", + "pseudolang", + "IPv4", + "IPv6", "58008", "binary", "hexadecimal", @@ -137,5 +142,5 @@ fn base_generation_matches_immutable_browser_snapshots() { assert_eq!(actual, expected, "{name}"); checked += 1; } - assert!(checked >= 48, "Only {checked} cases exercised"); + assert!(checked >= 53, "Only {checked} cases exercised"); } From 1eea8744b23748cd783a47db0b7e094737f134b9 Mon Sep 17 00:00:00 2001 From: Lakshmi Tanmay Date: Fri, 23 Oct 2026 00:49:32 +0530 Subject: [PATCH 26/93] Preserve Unicode normalization and slash commits in Morse generation --- Cargo.lock | 16 +++++ crates/typing-engine/Cargo.toml | 1 + crates/typing-engine/src/generation.rs | 3 +- crates/typing-engine/src/transforms.rs | 79 ++++++++++++++++++++++++ crates/typing-engine/tests/generation.rs | 3 +- 5 files changed, 100 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c1c7beee1..5e868d204 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -238,6 +238,12 @@ dependencies = [ "syn 3.0.9", ] +[[package]] +name = "tinyvec" +version = "1.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd3ca314f692efd6c868f8408f53fe444634a845f96c028b97d35f6a1f79f0ee" + [[package]] name = "ts-rs" version = "12.0.1" @@ -268,6 +274,7 @@ dependencies = [ "serde", "serde_json", "ts-rs", + "unicode-normalization", ] [[package]] @@ -287,6 +294,15 @@ version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2c754d6c33795a1c324727428e5a7dedb5b06195f9890bdbcba760d3e246563" +[[package]] +name = "unicode-normalization" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" +dependencies = [ + "tinyvec", +] + [[package]] name = "wasm-bindgen" version = "0.2.129" diff --git a/crates/typing-engine/Cargo.toml b/crates/typing-engine/Cargo.toml index 363ba8801..d16b0b11d 100644 --- a/crates/typing-engine/Cargo.toml +++ b/crates/typing-engine/Cargo.toml @@ -10,6 +10,7 @@ repository.workspace = true serde.workspace = true serde_json.workspace = true ts-rs.workspace = true +unicode-normalization = "=0.1.24" [lints] workspace = true diff --git a/crates/typing-engine/src/generation.rs b/crates/typing-engine/src/generation.rs index f0209e8aa..6a3ad66e4 100644 --- a/crates/typing-engine/src/generation.rs +++ b/crates/typing-engine/src/generation.rs @@ -161,6 +161,7 @@ impl Generator { } if config.funbox.iter().any(|name| { ![ + "morse", "arrows", "weakspot", "pseudolang", @@ -609,7 +610,7 @@ impl Generator { .config .funbox .iter() - .any(|name| ["nospace", "underscore_spaces", "arrows"].contains(&name.as_str())) + .any(|name| ["nospace", "underscore_spaces", "arrows", "morse"].contains(&name.as_str())) { committed.0.push(32); } diff --git a/crates/typing-engine/src/transforms.rs b/crates/typing-engine/src/transforms.rs index 9ae8adc5e..26a00073d 100644 --- a/crates/typing-engine/src/transforms.rs +++ b/crates/typing-engine/src/transforms.rs @@ -80,6 +80,7 @@ pub fn alter( ) -> Result { let mut word = word; match name { + "morse" => word = morse(word), "backwards" => word.0.reverse(), "ALL_CAPS" => word = case(&word, true), "capitals" => { @@ -193,3 +194,81 @@ pub fn alter( } Ok(word) } + +fn morse(word: Text) -> Text { + use unicode_normalization::UnicodeNormalization; + const MAP: &[(&str, &str)] = &[ + ("a", ".-"), + ("b", "-..."), + ("c", "-.-."), + ("d", "-.."), + ("e", "."), + ("f", "..-."), + ("g", "--."), + ("h", "...."), + ("i", ".."), + ("j", ".---"), + ("k", "-.-"), + ("l", ".-.."), + ("m", "--"), + ("n", "-."), + ("o", "---"), + ("p", ".--."), + ("q", "--.-"), + ("r", ".-."), + ("s", "..."), + ("t", "-"), + ("u", "..-"), + ("v", "...-"), + ("w", ".--"), + ("x", "-..-"), + ("y", "-.--"), + ("z", "--.."), + ("0", "-----"), + ("1", ".----"), + ("2", "..---"), + ("3", "...--"), + ("4", "....-"), + ("5", "....."), + ("6", "-...."), + ("7", "--..."), + ("8", "---.."), + ("9", "----."), + (".", ".-.-.-"), + (",", "--..--"), + ("?", "..--.."), + ("'", ".----."), + ("/", "-..-."), + ("(", "-.--."), + (")", "-.--.-"), + ("&", ".-..."), + (":", "---..."), + (";", "-.-.-."), + ("=", "-...-"), + ("+", ".-.-."), + ("-", "-....-"), + ("_", "..--.-"), + ("\"", ".-..-."), + ("$", "...-..-"), + ("!", "-.-.--"), + ("@", ".--.-."), + ]; + // A surrogate cannot map to Morse; replacement also cannot map. Preserve + // JS's original UTF-16 loop bound even if lowercasing expands the text. + let normalized = Text::from( + word.display_lossy() + .nfd() + .filter(|c| !('\u{0300}'..='\u{036f}').contains(c)) + .collect::() + .as_str(), + ); + let lower = case(&normalized, false); + let mut output = String::new(); + for unit in lower.0.iter().take(normalized.len()) { + if let Some((_, code)) = MAP.iter().find(|(key, _)| key.encode_utf16().eq([*unit])) { + output.push_str(code); + output.push('/'); + } + } + Text::from(output.as_str()) +} diff --git a/crates/typing-engine/tests/generation.rs b/crates/typing-engine/tests/generation.rs index 235b27d6e..9f9e86088 100644 --- a/crates/typing-engine/tests/generation.rs +++ b/crates/typing-engine/tests/generation.rs @@ -45,6 +45,7 @@ fn base_generation_matches_immutable_browser_snapshots() { let config: GenerationConfig = serde_json::from_value(case["config"].clone()).unwrap(); if config.funbox.iter().any(|name| { ![ + "morse", "arrows", "weakspot", "pseudolang", @@ -142,5 +143,5 @@ fn base_generation_matches_immutable_browser_snapshots() { assert_eq!(actual, expected, "{name}"); checked += 1; } - assert!(checked >= 53, "Only {checked} cases exercised"); + assert!(checked >= 54, "Only {checked} cases exercised"); } From 749af6b4c8ffd3d0ba6579557a8068f83377f9ac Mon Sep 17 00:00:00 2001 From: Lakshmi Tanmay Date: Fri, 23 Oct 2026 00:51:11 +0530 Subject: [PATCH 27/93] Port quote queues and polyglot generation; verify all 61 legacy snapshots --- crates/typing-engine/src/generation.rs | 154 +++++++++++++++--- crates/typing-engine/src/lib.rs | 1 + crates/typing-engine/src/quotes.rs | 193 +++++++++++++++++++++++ crates/typing-engine/tests/generation.rs | 78 ++++----- docs/rust-engine/decisions.md | 8 + docs/rust-engine/progress.md | 4 +- 6 files changed, 379 insertions(+), 59 deletions(-) create mode 100644 crates/typing-engine/src/quotes.rs diff --git a/crates/typing-engine/src/generation.rs b/crates/typing-engine/src/generation.rs index 6a3ad66e4..1234f4df3 100644 --- a/crates/typing-engine/src/generation.rs +++ b/crates/typing-engine/src/generation.rs @@ -25,6 +25,8 @@ pub struct GenerationConfig { pub lazy_mode: bool, pub british_english: bool, pub funbox: Vec, + pub quote_length: Vec, + pub custom_polyglot: Vec, } impl Default for GenerationConfig { fn default() -> Self { @@ -39,6 +41,13 @@ impl Default for GenerationConfig { lazy_mode: false, british_english: false, funbox: vec![], + quote_length: vec![1], + custom_polyglot: vec![ + "english".into(), + "spanish".into(), + "french".into(), + "german".into(), + ], } } } @@ -73,6 +82,14 @@ pub struct Generated { pub all_right_to_left: Option, pub all_joining_script: bool, } +#[derive(Default)] +pub struct Assets { + pub quotes: crate::quotes::Quotes, + pub quote_asset: Option, + pub selected_quote_id: u32, + pub favorite_quote_ids: Vec, + pub polyglot: Vec, +} pub struct Generator { config: GenerationConfig, language: Language, @@ -92,6 +109,9 @@ pub struct Generator { arrows: crate::generated_funboxes::Arrows, pseudolang: Option, pub weak_spot: crate::weak_spot::WeakSpot, + pub current_quote: Option, + polyglot: Vec, + word_languages: std::collections::BTreeMap, usize>, } pub(crate) fn case(text: &Text, upper: bool) -> Text { let mut output = vec![]; @@ -152,15 +172,22 @@ impl Generator { language: Language, custom: Option, random: Random, + ) -> Result { + Self::with_assets(config, language, custom, random, &mut Assets::default()) + } + pub fn with_assets( + config: GenerationConfig, + mut language: Language, + custom: Option, + mut random: Random, + assets: &mut Assets, ) -> Result { if !["time", "words", "custom", "zen", "quote"].contains(&config.mode.as_str()) { return Err("Invalid mode".into()); } - if config.mode == "quote" { - return Err("Quote asset selection is not yet ported".into()); - } if config.funbox.iter().any(|name| { ![ + "polyglot", "morse", "arrows", "weakspot", @@ -202,11 +229,63 @@ impl Generator { { return Err("Invalid custom mode or limit".into()); } - let mut wordset = if config.mode == "custom" { + let current_quote = if config.mode == "quote" { + if let Some(asset) = assets.quote_asset.take() { + assets + .quotes + .load(asset, &config.quote_length, &mut random)?; + } + Some(assets.quotes.select( + &config.language, + &config.quote_length, + assets.selected_quote_id, + &assets.favorite_quote_ids, + config.british_english, + &mut random, + )?) + } else { + None + }; + let mut wordset = if let Some(quote) = ¤t_quote { + quote.text_split.clone() + } else if config.mode == "custom" { custom.as_ref().unwrap().text.clone() } else { language.words.clone() }; + let mut word_languages = std::collections::BTreeMap::new(); + let polyglot = if config.funbox.iter().any(|name| name == "polyglot") { + if assets.polyglot.len() < 2 { + return Err("Polyglot requires at least two valid languages".into()); + } + let all_rtl = assets + .polyglot + .iter() + .all(|lang| lang.right_to_left == Some(true)); + let all_ltr = assets + .polyglot + .iter() + .all(|lang| lang.right_to_left != Some(true)); + if (language.right_to_left == Some(true) && all_ltr) + || (language.right_to_left != Some(true) && all_rtl) + { + return Err("Polyglot language direction conflict; update main language".into()); + } + wordset.clear(); + for (index, lang) in assets.polyglot.iter().enumerate() { + for word in &lang.words { + if !word_languages.contains_key(&word.0) { + wordset.push(word.clone()); + } + word_languages.insert(word.0.clone(), index); + } + } + random.shuffle(&mut wordset)?; + language.joining_script = assets.polyglot.iter().any(|lang| lang.joining_script); + assets.polyglot.clone() + } else { + vec![] + }; if config.funbox.iter().any(|name| name == "backwards") { wordset.reverse(); } @@ -235,6 +314,9 @@ impl Generator { arrows: crate::generated_funboxes::Arrows::default(), pseudolang: None, weak_spot: crate::weak_spot::WeakSpot::default(), + current_quote, + polyglot, + word_languages, }) } fn limit(&self) -> usize { @@ -275,9 +357,19 @@ impl Generator { if self.config.mode == "words" && self.config.words > 0 { limit = limit.min(self.config.words); } + if let Some(quote) = &self.current_quote { + limit = if self.config.show_all_lines { + quote.text_split.len() + } else { + limit.min(quote.text_split.len()) + }; + } limit } pub fn all_generated(&self) -> bool { + if let Some(quote) = &self.current_quote { + return self.generated >= quote.text_split.len(); + } if self.config.mode == "words" { return self.config.words > 0 && self.generated >= self.config.words; } @@ -310,8 +402,18 @@ impl Generator { Ok(self.wordset[index].clone()) } fn lazy(&self, word: Text) -> Text { - if self.config.lazy_mode && (!self.language.no_lazy_mode || self.config.mode == "custom") { - self.rules.lazy(&word, &self.language.additional_accents) + let language = if self.polyglot.is_empty() { + Some(&self.language) + } else { + self.word_languages + .get(&word.0) + .and_then(|index| self.polyglot.get(*index)) + }; + if let Some(language) = language + && self.config.lazy_mode + && (!language.no_lazy_mode || self.config.mode == "custom") + { + self.rules.lazy(&word, &language.additional_accents) } else { word } @@ -401,13 +503,14 @@ impl Generator { previous2: Option<&Text>, ) -> Result { if self.repeating + && self.config.mode != "quote" && let Some(next) = self.previous.get(index) { self.section_index += 1; self.generated = index + 1; return Ok(next.clone()); } - if self.repeating { + if self.repeating && self.config.mode != "quote" { let continuation = self.config.mode == "time" || (self.config.mode == "words" && index < self.config.words) || (self.config.mode == "custom" @@ -427,10 +530,11 @@ impl Generator { let previous2 = previous2.map(|word| clean_previous(word, true)); if self.section.is_empty() { let custom = self.custom.clone(); - if self.config.mode == "custom" - && custom - .as_ref() - .is_some_and(|custom| custom.mode == "repeat") + if self.config.mode == "quote" + || (self.config.mode == "custom" + && custom + .as_ref() + .is_some_and(|custom| custom.mode == "repeat")) { word = self.next_ordered(); } else if self.config.mode == "custom" @@ -541,7 +645,15 @@ impl Generator { if word.is_empty() { return Err("Random word is empty".into()); } + let word_language = self + .word_languages + .get(&word.0) + .and_then(|index| self.polyglot.get(*index)) + .map_or(self.config.language.as_str(), |language| { + language.name.as_str() + }); if self.config.mode != "custom" + && self.config.mode != "quote" && !self.config.punctuation && !self.config.funbox.iter().any(|name| { [ @@ -560,7 +672,7 @@ impl Generator { && word.0.iter().any(|unit| (65..=90).contains(unit)) && !["german", "swiss_german", "code", "klingon"] .iter() - .any(|prefix| self.config.language.starts_with(prefix)) + .any(|prefix| word_language.starts_with(prefix)) { word = case(&word, false); } @@ -590,7 +702,15 @@ impl Generator { &mut self.random, )?; } - if self.config.british_english && self.config.language.contains("english") { + if self.config.british_english + && self.config.language.contains("english") + && !self.current_quote.as_ref().is_some_and(|quote| { + quote + .british_text + .as_ref() + .is_some_and(|text| !text.is_empty()) + }) + { word = self .rules .british(word, previous.as_ref(), self.config.mode == "quote"); @@ -606,11 +726,9 @@ impl Generator { } let mut committed = word.clone(); if committed.0.last() != Some(&10) - && !self - .config - .funbox - .iter() - .any(|name| ["nospace", "underscore_spaces", "arrows", "morse"].contains(&name.as_str())) + && !self.config.funbox.iter().any(|name| { + ["nospace", "underscore_spaces", "arrows", "morse"].contains(&name.as_str()) + }) { committed.0.push(32); } diff --git a/crates/typing-engine/src/lib.rs b/crates/typing-engine/src/lib.rs index 47fd04ce8..6b5d95438 100644 --- a/crates/typing-engine/src/lib.rs +++ b/crates/typing-engine/src/lib.rs @@ -4,6 +4,7 @@ pub mod generation; pub mod language_rules; pub mod pace; pub mod punctuation; +pub mod quotes; pub mod random; pub mod session; pub mod stats; diff --git a/crates/typing-engine/src/quotes.rs b/crates/typing-engine/src/quotes.rs new file mode 100644 index 000000000..3af582788 --- /dev/null +++ b/crates/typing-engine/src/quotes.rs @@ -0,0 +1,193 @@ +use crate::{Text, random::Random}; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct Quote { + pub text: Text, + #[serde(skip_serializing_if = "Option::is_none")] + pub british_text: Option, + pub source: String, + pub length: usize, + pub id: u32, + #[serde(default)] + pub group: usize, + #[serde(default)] + pub language: String, + #[serde(default)] + pub text_split: Vec, +} +#[derive(Clone, Debug, Deserialize)] +pub struct QuoteAsset { + pub language: String, + pub groups: Vec<(usize, usize)>, + pub quotes: Vec, +} +/// Host-owned lifetime, engine-owned selection; never a process-global queue. +#[derive(Default)] +pub struct Quotes { + language: Option, + quotes: Vec, + groups: Vec>, + queue: Vec, + cursor: usize, +} +impl Quotes { + pub fn load( + &mut self, + mut asset: QuoteAsset, + lengths: &[i32], + random: &mut Random, + ) -> Result<(), String> { + if self.language.as_ref() == Some(&asset.language) { + return Ok(()); + } + if asset.quotes.is_empty() { + return Err("No quotes found".into()); + } + if asset.quotes.len() > 1_000_000 + || asset + .quotes + .iter() + .any(|quote| quote.text.len() > 1_000_000) + { + return Err("Quote asset exceeds bound".into()); + } + self.groups = asset + .groups + .iter() + .enumerate() + .map(|(group, (lower, upper))| { + asset + .quotes + .iter_mut() + .enumerate() + .filter_map(|(index, quote)| { + quote.language = asset.language.clone(); + if (*lower..=*upper).contains("e.length) { + quote.group = group; + Some(index) + } else { + None + } + }) + .collect() + }) + .collect(); + self.language = Some(asset.language); + self.quotes = asset.quotes; + self.update_queue(lengths, random) + } + pub fn update_queue(&mut self, lengths: &[i32], random: &mut Random) -> Result<(), String> { + self.queue.clear(); + for group in lengths { + if *group >= 0 + && let Some(indexes) = self.groups.get(*group as usize) + { + self.queue.extend_from_slice(indexes); + } + } + random.shuffle(&mut self.queue)?; + self.cursor = 0; + Ok(()) + } + pub fn select( + &mut self, + language: &str, + lengths: &[i32], + selected: u32, + favorites: &[u32], + british: bool, + random: &mut Random, + ) -> Result { + let mut quote = if lengths == [-2] { + self.quotes + .iter() + .find(|q| q.id == selected) + .cloned() + .ok_or("Selected quote does not exist")? + } else if lengths.contains(&-3) { + let id = favorites[random.index(favorites.len())?]; + self.quotes + .iter() + .find(|q| q.id == id) + .cloned() + .ok_or("Favorite quote does not exist")? + } else { + if self.queue.is_empty() { + return Err("No quotes found for selected length".into()); + } + if self.cursor >= self.queue.len() { + self.cursor = 0; + random.shuffle(&mut self.queue)?; + } + let quote = self.quotes[self.queue[self.cursor]].clone(); + self.cursor += 1; + quote + }; + quote.language = remove_language_size(language); + quote.text = normalize(quote.text); + let text = if british { + quote + .british_text + .as_ref() + .filter(|text| !text.is_empty()) + .unwrap_or("e.text) + } else { + "e.text + }; + quote.text_split = text + .0 + .split(|unit| *unit == 32) + .map(|word| Text(word.to_vec())) + .collect(); + Ok(quote) + } +} +fn remove_language_size(language: &str) -> String { + let mut parts: Vec<_> = language.split('_').collect(); + if parts.last().is_some_and(|part| { + !part.is_empty() + && part + .trim_end_matches('k') + .chars() + .all(|c| c.is_ascii_digit()) + }) { + parts.pop(); + } + parts.join("_") +} +fn normalize(text: Text) -> Text { + let mut units = vec![]; + let mut index = 0; + while index < text.len() { + let unit = text.0[index]; + if unit == 13 || unit == 10 { + while units.last() == Some(&32) { + units.pop(); + } + units.extend([10, 32]); + if unit == 13 && text.0.get(index + 1) == Some(&10) { + index += 1; + } + while text.0.get(index + 1) == Some(&32) { + index += 1; + } + } else if unit == 0x2026 { + units.extend([46, 46, 46]); + } else if unit != 32 || units.last() != Some(&32) { + units.push(unit); + } + index += 1; + } + let whitespace = |unit: &u16| matches!(*unit, 9..=13 | 32 | 0xa0 | 0x1680 | 0x2000..=0x200a | 0x2028 | 0x2029 | 0x202f | 0x205f | 0x3000 | 0xfeff); + let start = units + .iter() + .position(|unit| !whitespace(unit)) + .unwrap_or(units.len()); + let end = units + .iter() + .rposition(|unit| !whitespace(unit)) + .map_or(start, |index| index + 1); + Text(units[start..end].to_vec()) +} diff --git a/crates/typing-engine/tests/generation.rs b/crates/typing-engine/tests/generation.rs index 9f9e86088..aaa45128b 100644 --- a/crates/typing-engine/tests/generation.rs +++ b/crates/typing-engine/tests/generation.rs @@ -1,7 +1,7 @@ use serde_json::{Value, json}; use typing_engine::{ Text, - generation::{CustomText, GenerationConfig, Generator, Language}, + generation::{Assets, CustomText, GenerationConfig, Generator, Language}, random::Random, }; @@ -41,43 +41,9 @@ fn base_generation_matches_immutable_browser_snapshots() { ) .unwrap(); let mut checked = 0; + let mut assets = Assets::default(); for case in cases { let config: GenerationConfig = serde_json::from_value(case["config"].clone()).unwrap(); - if config.funbox.iter().any(|name| { - ![ - "morse", - "arrows", - "weakspot", - "pseudolang", - "IPv4", - "IPv6", - "58008", - "binary", - "hexadecimal", - "gibberish", - "ascii", - "specials", - "backwards", - "ALL_CAPS", - "capitals", - "sPoNgEcAsE", - "rAnDoMcAsE", - "rot13", - "ddoouubblleedd", - "instant_messaging", - "underscore_spaces", - "nospace", - "plus_zero", - "plus_one", - "plus_two", - "plus_three", - "zipf", - ] - .contains(&name.as_str()) - }) || config.mode == "quote" - { - continue; - } let name = case["name"].as_str().unwrap(); let mut language: Value = serde_json::from_slice( &std::fs::read(root.join(format!( @@ -94,11 +60,42 @@ fn base_generation_matches_immutable_browser_snapshots() { neutral_words(&mut value); serde_json::from_value::(value).unwrap() }); - let mut generator = Generator::new( + if config.mode == "quote" { + let mut quote_asset: Value = serde_json::from_slice( + &std::fs::read(root.join("frontend/static/quotes/english.json")).unwrap(), + ) + .unwrap(); + for quote in quote_asset["quotes"].as_array_mut().unwrap() { + for key in ["text", "britishText"] { + if let Some(value) = quote.get(key).and_then(Value::as_str) { + quote[key] = json!(Text::from(value)); + } + } + } + assets.quote_asset = Some(serde_json::from_value(quote_asset).unwrap()); + assets.selected_quote_id = case["selectedQuoteId"].as_u64().unwrap_or(1) as u32; + } + if config.funbox.iter().any(|name| name == "polyglot") { + assets.polyglot = config + .custom_polyglot + .iter() + .map(|name| { + let mut value: Value = serde_json::from_slice( + &std::fs::read(root.join(format!("frontend/static/languages/{name}.json"))) + .unwrap(), + ) + .unwrap(); + neutral_words(&mut value); + serde_json::from_value(value).unwrap() + }) + .collect(); + } + let mut generator = Generator::with_assets( config, language, custom, Random::seeded(case["seed"].as_u64().unwrap() as u32), + &mut assets, ) .unwrap(); let mut generated = generator.generate(false).unwrap(); @@ -126,7 +123,10 @@ fn base_generation_matches_immutable_browser_snapshots() { let mut actual = json!(generated); actual["extra"] = json!(extra); actual["allWordsGenerated"] = json!(generator.all_generated()); - actual["currentQuote"] = Value::Null; + actual["currentQuote"] = generator.current_quote.as_ref().map_or( + Value::Null, + |quote| json!({"id": quote.id, "group": quote.group}), + ); let mut expected: Value = serde_json::from_slice( &std::fs::read(root.join(format!( "packages/typing-fixtures/reference/words/__snapshots__/{name}.json" @@ -143,5 +143,5 @@ fn base_generation_matches_immutable_browser_snapshots() { assert_eq!(actual, expected, "{name}"); checked += 1; } - assert!(checked >= 54, "Only {checked} cases exercised"); + assert_eq!(checked, 61, "Every original generator case must run"); } diff --git a/docs/rust-engine/decisions.md b/docs/rust-engine/decisions.md index 1550a01be..9a133fc22 100644 --- a/docs/rust-engine/decisions.md +++ b/docs/rust-engine/decisions.md @@ -14,3 +14,11 @@ - Korean scoring preserves hangul-js 0.2.6 disassembly, including its NUL→ㄱ lookup quirk. An exhaustive frozen digest covers every UTF-16 unit. Correcting that legacy interpretation requires a separately versioned behavior change. + +## Stateful quote selection and generator bounds + +Quote queues belong to an explicit `Quotes` instance; loading the same language preserves the queue, matching the original controller. Changing selected lengths requires explicit `update_queue`, as on the website. Original generator fixtures share one controller across cases; the Rust harness preserves this lifecycle rather than resetting or regenerating snapshots. All 61 run without exclusions. Polyglot assets are host supplied; Rust merges/deduplicates in JS Map order, shuffles, and retains per-word language properties. Direction conflicts and insufficient valid assets currently return errors; final binding must project the existing host recovery actions before cutover. + +Pseudolang retains UTF-16 prefix and JS property enumeration semantics. Its legacy unbounded loop is bounded at 65,536 draws with an explicit error; no random fallback. Individual source words and generation requests are bounded at 1,000,000 UTF-16 units/words. Hosts must surface failures; never silently alter generated content. + +`accents.json` and `british-english.json` are mechanical copies of existing GPL-3.0 project transformation tables, not regenerated reference snapshots. `unicode-normalization` supplies canonical NFD for Morse; only U+0300–036F marks are removed, preserving the original rule. diff --git a/docs/rust-engine/progress.md b/docs/rust-engine/progress.md index a12072304..4b33f2093 100644 --- a/docs/rust-engine/progress.md +++ b/docs/rust-engine/progress.md @@ -22,7 +22,7 @@ Phase 1.1 and 1.2 gates passed: inventory/baselines, lossless contract, frozen o | 1.2 Contracts / fixtures | Complete | 19 command traces, 21 oracle/integrity tests; contract and classified gaps. | | 1.3 Native / browser proof | Complete | Native 7 traces; Cloudflare real-browser finite slice; MIME/CSP, lifecycle, boundary benchmark. Budgets frozen. | | 1.4 Input / lifecycle / timing | Active | 19 native/browser command traces; 8 original recordings. Composition, batch failure, deferred work, visible-word bounds and browser deletion observations covered. Full generator/session integration remains open. | -| 1.5 Generation / configuration | Active | 48/61 unchanged generator snapshots. Quote and remaining funboxes pending. | +| 1.5 Generation / configuration | Active | 61/61 unchanged generator snapshots. Content requests, configuration validation and generator/session integration pending. | | 1.6 Events / results / replay | Active | All computed summary fields match 8 original CompletedEvents exactly. Full results, eligibility, replay pending. | | 1.7 Binding / engine gate | Pending | Full three-path parity and budgets. | | 2.1–2.6 Website | Pending | Requires Stage 1 gate. | @@ -72,7 +72,7 @@ do not rerun completed checks without a new reason. - Native and actual Cloudflare-browser Wasm match all 19 frozen command traces. - Original CompletedEvent summaries match exactly (including floating-point outputs); exhaustive Hangul compatibility hash covers all 65,536 UTF-16 units. - Pace, weak-spot learning, explicit Mulberry32/strict random streams, 99 frozen transform cases, language punctuation, lazy mode and British conversion implemented. -- Native generator currently matches 48/61 original snapshots; unsupported cases remain explicitly skipped and prevent the full Stage 1 gate. +- Native generator now matches all 61 original snapshots with no skipped cases, including quote queue state, repeated quotes, IDs, British text, Morse, arrows, IPs, pseudolang, weakspot and polyglot. Full configuration/content/session gate remains open. - Native test, Clippy and format checks passed after language-rule integration. Final current-head browser/build rerun pending. - Draft Stage 1 PR: https://github.com/voltcrash/oxytype/pull/184. Initial CI native/Wasm, SSR, delivery and frontend checks passed; package routing failure fixed locally (60 routing tests pass). Whole current-head CI not yet verified. - Stages 2 and 3 have no implementation yet. No production deployment or release published. From cbc6b566302c1610b2f5d3e1c57b1461057b1ca1 Mon Sep 17 00:00:00 2001 From: Lakshmi Tanmay Date: Fri, 23 Oct 2026 04:10:51 +0530 Subject: [PATCH 28/93] Expose typed content bindings and prove all generators in Cloudflare browser Wasm --- crates/typing-engine-wasm/src/lib.rs | 71 ++++++++++ .../typing-engine/src/bin/export-bindings.rs | 4 + crates/typing-engine/src/generation.rs | 55 +++++++- crates/typing-engine/src/quotes.rs | 5 +- crates/typing-engine/src/random.rs | 3 +- docs/rust-engine/progress.md | 3 + frontend/e2e/engine-proof.migration.ts | 122 ++++++++++++++++++ frontend/src/routes/engine-proof.tsx | 68 ++++++++++ packages/typing-engine/src/browser.client.ts | 62 ++++++++- 9 files changed, 383 insertions(+), 10 deletions(-) diff --git a/crates/typing-engine-wasm/src/lib.rs b/crates/typing-engine-wasm/src/lib.rs index e018cc752..575bfcb1d 100644 --- a/crates/typing-engine-wasm/src/lib.rs +++ b/crates/typing-engine-wasm/src/lib.rs @@ -60,3 +60,74 @@ impl WasmSession { self.session = None; } } + +#[wasm_bindgen] +pub struct WasmContent { + content: Option, +} +#[wasm_bindgen] +impl WasmContent { + #[wasm_bindgen(constructor)] + pub fn new(version: u32) -> Result { + if version != BINDING_VERSION { + return Err(JsError::new("Binding version mismatch")); + } + Ok(Self { + content: Some(typing_engine::generation::Content::default()), + }) + } + pub fn prepare(&mut self, input: &str) -> Result<(), JsError> { + let input = serde_json::from_str(input)?; + self.content + .as_mut() + .ok_or_else(|| JsError::new("Content disposed"))? + .prepare(input) + .map_err(|message| JsError::new(&message)) + } + pub fn generate(&mut self, repeating: bool) -> Result { + let generator = self + .content + .as_mut() + .ok_or_else(|| JsError::new("Content disposed"))? + .generator() + .map_err(|message| JsError::new(&message))?; + let generated = generator + .generate(repeating) + .map_err(|message| JsError::new(&message))?; + Ok(serde_json::to_string(&generated)?) + } + pub fn next( + &mut self, + index: usize, + bound: usize, + previous: &str, + previous2: &str, + ) -> Result { + let previous: Option = serde_json::from_str(previous)?; + let previous2: Option = serde_json::from_str(previous2)?; + let generator = self + .content + .as_mut() + .ok_or_else(|| JsError::new("Content disposed"))? + .generator() + .map_err(|message| JsError::new(&message))?; + let next = generator + .next(index, bound, previous.as_ref(), previous2.as_ref()) + .map_err(|message| JsError::new(&message))?; + Ok(serde_json::to_string(&next)?) + } + pub fn metadata(&mut self) -> Result { + let generator = self + .content + .as_mut() + .ok_or_else(|| JsError::new("Content disposed"))? + .generator() + .map_err(|message| JsError::new(&message))?; + Ok(serde_json::to_string( + &serde_json::json!({ "allWordsGenerated": generator.all_generated(), "currentQuote": generator.current_quote }), + )?) + } + pub fn dispose(&mut self) { + self.content = None; + } +} diff --git a/crates/typing-engine/src/bin/export-bindings.rs b/crates/typing-engine/src/bin/export-bindings.rs index 4c00e38d1..d1ba1f025 100644 --- a/crates/typing-engine/src/bin/export-bindings.rs +++ b/crates/typing-engine/src/bin/export-bindings.rs @@ -12,5 +12,9 @@ fn main() -> Result<(), Box> { Config::export_all(&config)?; State::export_all(&config)?; Update::export_all(&config)?; + typing_engine::generation::Prepare::export_all(&config)?; + typing_engine::generation::Generated::export_all(&config)?; + typing_engine::generation::NextWord::export_all(&config)?; + typing_engine::quotes::Quote::export_all(&config)?; Ok(()) } diff --git a/crates/typing-engine/src/generation.rs b/crates/typing-engine/src/generation.rs index 1234f4df3..3e996fc3e 100644 --- a/crates/typing-engine/src/generation.rs +++ b/crates/typing-engine/src/generation.rs @@ -1,8 +1,9 @@ use crate::{Text, random::Random}; use serde::{Deserialize, Serialize}; use std::collections::VecDeque; +use ts_rs::TS; -#[derive(Clone, Debug, Default, Deserialize)] +#[derive(Clone, Debug, Default, Deserialize, TS)] #[serde(default, rename_all = "camelCase")] pub struct Language { pub name: String, @@ -13,7 +14,7 @@ pub struct Language { pub no_lazy_mode: bool, pub additional_accents: Vec<(Text, Text)>, } -#[derive(Clone, Debug, Deserialize)] +#[derive(Clone, Debug, Deserialize, TS)] #[serde(default, rename_all = "camelCase")] pub struct GenerationConfig { pub mode: String, @@ -51,12 +52,12 @@ impl Default for GenerationConfig { } } } -#[derive(Clone, Debug, Deserialize)] +#[derive(Clone, Debug, Deserialize, TS)] pub struct CustomLimit { pub mode: String, pub value: usize, } -#[derive(Clone, Debug, Deserialize)] +#[derive(Clone, Debug, Deserialize, TS)] #[serde(rename_all = "camelCase")] pub struct CustomText { pub text: Vec, @@ -64,14 +65,14 @@ pub struct CustomText { pub limit: CustomLimit, pub pipe_delimiter: bool, } -#[derive(Clone, Debug, Serialize, PartialEq, Eq)] +#[derive(Clone, Debug, Serialize, PartialEq, Eq, TS)] #[serde(rename_all = "camelCase")] pub struct NextWord { pub word: Text, pub word_raw: Text, pub section_index: usize, } -#[derive(Debug, Serialize)] +#[derive(Debug, Serialize, TS)] #[serde(rename_all = "camelCase")] pub struct Generated { pub words: Vec, @@ -742,3 +743,45 @@ impl Generator { Ok(next) } } + +#[derive(Deserialize, TS)] +#[serde(rename_all = "camelCase")] +pub struct Prepare { + pub config: GenerationConfig, + pub language: Language, + pub custom: Option, + pub random: Random, + pub quotes: Option, + #[serde(default)] + pub polyglot: Vec, + #[serde(default)] + pub selected_quote_id: u32, + #[serde(default)] + pub favorite_quote_ids: Vec, +} +/// Retains only explicitly owned content-selection state across restarts. +#[derive(Default)] +pub struct Content { + assets: Assets, + generator: Option, +} +impl Content { + pub fn prepare(&mut self, input: Prepare) -> Result<(), String> { + self.generator = None; + self.assets.quote_asset = input.quotes; + self.assets.polyglot = input.polyglot; + self.assets.selected_quote_id = input.selected_quote_id; + self.assets.favorite_quote_ids = input.favorite_quote_ids; + self.generator = Some(Generator::with_assets( + input.config, + input.language, + input.custom, + input.random, + &mut self.assets, + )?); + Ok(()) + } + pub fn generator(&mut self) -> Result<&mut Generator, String> { + self.generator.as_mut().ok_or("Content not prepared".into()) + } +} diff --git a/crates/typing-engine/src/quotes.rs b/crates/typing-engine/src/quotes.rs index 3af582788..d67a7ccde 100644 --- a/crates/typing-engine/src/quotes.rs +++ b/crates/typing-engine/src/quotes.rs @@ -1,7 +1,8 @@ use crate::{Text, random::Random}; use serde::{Deserialize, Serialize}; +use ts_rs::TS; -#[derive(Clone, Debug, Deserialize, Serialize)] +#[derive(Clone, Debug, Deserialize, Serialize, TS)] #[serde(rename_all = "camelCase")] pub struct Quote { pub text: Text, @@ -17,7 +18,7 @@ pub struct Quote { #[serde(default)] pub text_split: Vec, } -#[derive(Clone, Debug, Deserialize)] +#[derive(Clone, Debug, Deserialize, TS)] pub struct QuoteAsset { pub language: String, pub groups: Vec<(usize, usize)>, diff --git a/crates/typing-engine/src/random.rs b/crates/typing-engine/src/random.rs index 8169e7d0c..06f8ab812 100644 --- a/crates/typing-engine/src/random.rs +++ b/crates/typing-engine/src/random.rs @@ -1,8 +1,9 @@ use serde::{Deserialize, Serialize}; +use ts_rs::TS; /// Stable Mulberry32 stream, matching the original generator fixtures. No host /// RNG, hidden clock or library-selected algorithm participates in generation. -#[derive(Clone, Debug, Deserialize, Serialize)] +#[derive(Clone, Debug, Deserialize, Serialize, TS)] #[serde(tag = "kind", rename_all = "camelCase")] pub enum Random { Seeded { state: u32 }, diff --git a/docs/rust-engine/progress.md b/docs/rust-engine/progress.md index 4b33f2093..20a0bbc25 100644 --- a/docs/rust-engine/progress.md +++ b/docs/rust-engine/progress.md @@ -76,3 +76,6 @@ do not rerun completed checks without a new reason. - Native test, Clippy and format checks passed after language-rule integration. Final current-head browser/build rerun pending. - Draft Stage 1 PR: https://github.com/voltcrash/oxytype/pull/184. Initial CI native/Wasm, SSR, delivery and frontend checks passed; package routing failure fixed locally (60 routing tests pass). Whole current-head CI not yet verified. - Stages 2 and 3 have no implementation yet. No production deployment or release published. + +- Current Cloudflare production build passes. Real-browser `engine-proof.migration.ts`: 2 tests pass (19 command traces and 61 generator snapshots). Generated binding types now cover content preparation and generator outputs. +- Expanded Wasm: 1,123,350 bytes raw (over 1 MiB budget), ~292.77 kB gzip (under budget). Optimization required; size gate explicitly not passed. diff --git a/frontend/e2e/engine-proof.migration.ts b/frontend/e2e/engine-proof.migration.ts index d9171b97d..70161d2c8 100644 --- a/frontend/e2e/engine-proof.migration.ts +++ b/frontend/e2e/engine-proof.migration.ts @@ -108,3 +108,125 @@ test("native fixture matches browser Wasm through built Cloudflare assets", asyn await expect(page.getByTestId("engine-proof-error")).toBeEmpty(); } }); + +test("all 61 unchanged generator snapshots match browser Wasm", async ({ + page, +}) => { + const staticRoot = resolve(import.meta.dirname, "../static"); + const cases = JSON.parse( + await readFile(resolve(root, "reference/words/cases.json"), "utf8"), + ) as { + name: string; + seed: number; + config: Record; + customText?: unknown; + repeat?: boolean; + extraWords?: number; + selectedQuoteId?: number; + }[]; + const units = (text: string): number[] => + Array.from({ length: text.length }, (_, index) => text.charCodeAt(index)); + const neutral = (value: unknown, key = ""): unknown => { + if ( + typeof value === "string" && + [ + "words", + "text", + "britishText", + "word", + "wordRaw", + "additionalAccents", + ].includes(key) + ) { + return units(value); + } + if (Array.isArray(value)) return value.map((item) => neutral(item, key)); + if (value !== null && typeof value === "object") { + return Object.fromEntries( + Object.entries(value).map(([key, item]) => [key, neutral(item, key)]), + ); + } + return value; + }; + const inputs = []; + const expected = []; + let quotesLoaded = false; + for (const fixture of cases) { + const language = + typeof fixture.config["language"] === "string" + ? fixture.config["language"] + : "english"; + const polyglot = + fixture.config["funbox"] instanceof Array && + fixture.config["funbox"].includes("polyglot") + ? await Promise.all( + (fixture.config["customPolyglot"] as string[]).map(async (name) => + neutral( + JSON.parse( + await readFile( + resolve(staticRoot, `languages/${name}.json`), + "utf8", + ), + ), + ), + ), + ) + : []; + let quotes: unknown = null; + if (fixture.config["mode"] === "quote" && !quotesLoaded) { + quotes = neutral( + JSON.parse( + await readFile(resolve(staticRoot, "quotes/english.json"), "utf8"), + ), + ); + quotesLoaded = true; + } + inputs.push({ + input: { + config: fixture.config, + language: neutral( + JSON.parse( + await readFile( + resolve(staticRoot, `languages/${language}.json`), + "utf8", + ), + ), + ), + custom: neutral(fixture.customText ?? null), + random: { kind: "seeded", state: fixture.seed }, + quotes, + polyglot, + selectedQuoteId: fixture.selectedQuoteId ?? 1, + favoriteQuoteIds: [], + }, + repeat: fixture.repeat, + extraWords: fixture.extraWords, + }); + expected.push( + neutral( + JSON.parse( + await readFile( + resolve(root, `reference/words/__snapshots__/${fixture.name}.json`), + "utf8", + ), + ), + ), + ); + } + await page.goto("/engine-proof"); + await page + .getByRole("textbox", { name: "Command fixture" }) + .fill(JSON.stringify(inputs)); + await page.getByRole("button", { name: "Run generation" }).click(); + await expect(page.getByTestId("engine-proof-result")).not.toBeEmpty({ + timeout: 30_000, + }); + await expect(page.getByTestId("engine-proof-error")).toBeEmpty(); + const actual = JSON.parse( + await page.getByTestId("engine-proof-result").innerText(), + ) as unknown[]; + expect(actual).toHaveLength(61); + for (const [index, fixture] of cases.entries()) { + expect(actual[index], fixture.name).toEqual(expected[index]); + } +}); diff --git a/frontend/src/routes/engine-proof.tsx b/frontend/src/routes/engine-proof.tsx index cf3f6d29a..30c5d64fe 100644 --- a/frontend/src/routes/engine-proof.tsx +++ b/frontend/src/routes/engine-proof.tsx @@ -3,6 +3,7 @@ import type { Command, Config, Text, + Prepare, } from "@oxytype/typing-engine/browser.client"; import { createFileRoute } from "@tanstack/solid-router"; @@ -92,6 +93,65 @@ function EngineProof() { setFailure(error instanceof Error ? error.message : String(error)); } }; + const runGeneration = async (): Promise => { + setResult(""); + setFailure(""); + try { + const cases = JSON.parse(fixture()) as { + input: Prepare; + repeat?: boolean; + extraWords?: number; + }[]; + const { loadEngine } = + await import("@oxytype/typing-engine/browser.client"); + const engine = await loadEngine(); + if (disposed) return; + const content = engine.createContent(); + try { + const results = cases.map(({ input, repeat, extraWords }) => { + content.prepare(input); + let generated = content.generate(); + if (repeat === true) generated = content.generate(true); + const words = [...generated.words]; + const extra = []; + const strip = (word: Text | undefined): Text | null => + word === undefined + ? null + : [32, 10].includes(word.at(-1) ?? -1) + ? word.slice(0, -1) + : word; + for (let index = 0; index < (extraWords ?? 0); index++) { + const next = content.next( + words.length, + 100, + strip(words.at(-1)), + strip(words.at(-2)), + ); + words.push(next.word); + extra.push(next); + } + const metadata = content.metadata(); + return { + ...generated, + extra, + allWordsGenerated: metadata.allWordsGenerated, + currentQuote: + metadata.currentQuote === null + ? null + : { + id: metadata.currentQuote.id, + group: metadata.currentQuote.group, + }, + }; + }); + setResult(JSON.stringify(results)); + } finally { + content.dispose(); + } + } catch (error) { + setFailure(error instanceof Error ? error.message : String(error)); + } + }; return ( Run fixture +
{result()}
{failure()}
{benchmark()}
diff --git a/packages/typing-engine/src/browser.client.ts b/packages/typing-engine/src/browser.client.ts index ac8303674..6cf68679d 100644 --- a/packages/typing-engine/src/browser.client.ts +++ b/packages/typing-engine/src/browser.client.ts @@ -1,6 +1,7 @@ import initialize, { binding_version, WasmSession, + WasmContent, } from "../generated/engine.js"; import wasmUrl from "../generated/engine_bg.wasm?url"; import type { Command } from "../generated/types/Command"; @@ -8,7 +9,20 @@ import type { Config } from "../generated/types/Config"; import type { Text } from "../generated/types/Text"; import type { Update } from "../generated/types/Update"; -export type { Command, Config, Text, Update }; +import type { Prepare } from "../generated/types/Prepare"; +import type { Generated } from "../generated/types/Generated"; +import type { NextWord } from "../generated/types/NextWord"; +import type { Quote } from "../generated/types/Quote"; +export type { + Command, + Config, + Text, + Update, + Prepare, + Generated, + NextWord, + Quote, +}; const BINDING_VERSION = 1; export function encodeText(value: string): Text { @@ -28,6 +42,7 @@ export function decodeText(units: Text): string { export async function loadEngine(): Promise<{ createSession: (config: Partial, words: Text[]) => BrowserSession; memoryBytes: () => number; + createContent: () => BrowserContent; }> { const module = await initialize({ module_or_path: wasmUrl }); if (binding_version() !== BINDING_VERSION) { @@ -36,6 +51,7 @@ export async function loadEngine(): Promise<{ return { createSession: (config, words) => new BrowserSession(config, words), memoryBytes: () => module.memory.buffer.byteLength, + createContent: () => new BrowserContent(), }; } @@ -115,3 +131,47 @@ export function benchmarkBoundary(): { jsonMs: number[]; objectMs: number[] } { } return { jsonMs, objectMs }; } + +export class BrowserContent { + private handle: WasmContent | undefined = new WasmContent(BINDING_VERSION); + private live(): WasmContent { + if (this.handle === undefined) { + throw new Error("Typing engine content disposed"); + } + return this.handle; + } + prepare(input: Prepare): void { + this.live().prepare(JSON.stringify(input)); + } + generate(repeating = false): Generated { + return JSON.parse(this.live().generate(repeating)) as Generated; + } + next( + index: number, + bound: number, + previous: Text | null, + previous2: Text | null, + ): NextWord { + return JSON.parse( + this.live().next( + index, + bound, + JSON.stringify(previous), + JSON.stringify(previous2), + ), + ) as NextWord; + } + metadata(): { allWordsGenerated: boolean; currentQuote: Quote | null } { + return JSON.parse(this.live().metadata()) as { + allWordsGenerated: boolean; + currentQuote: Quote | null; + }; + } + dispose(): void { + const handle = this.handle; + if (handle === undefined) return; + this.handle = undefined; + handle.dispose(); + handle.free(); + } +} From d71e4397c70f9b036894248f9b4f2ab07509ed7f Mon Sep 17 00:00:00 2001 From: Lakshmi Tanmay Date: Fri, 23 Oct 2026 04:13:33 +0530 Subject: [PATCH 29/93] Optimize Wasm with pinned Binaryen to meet the frozen payload budget --- package.json | 1 + pnpm-lock.yaml | 9 +++++++++ scripts/engine.ts | 19 ++++++++++++++++++- 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 70edf2047..f4b4dc1f9 100644 --- a/package.json +++ b/package.json @@ -52,6 +52,7 @@ "@commitlint/cli": "21.2.3", "@types/node": "24.19.0", "@vitest/coverage-v8": "catalog:", + "binaryen": "132.0.0", "caniuse-lite": "1.0.30001814", "conventional-changelog-conventionalcommits": "10.4.0", "husky": "9.1.7", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 351686784..9343efbcd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -240,6 +240,9 @@ importers: '@vitest/coverage-v8': specifier: 'catalog:' version: 5.0.3(@vitest/browser@5.0.3)(vitest@5.0.3) + binaryen: + specifier: 132.0.0 + version: 132.0.0 caniuse-lite: specifier: 1.0.30001814 version: 1.0.30001814 @@ -4704,6 +4707,10 @@ packages: bidi-js@1.1.0: resolution: {integrity: sha512-fX1Onk0tdVPC7obPWB5EbJ1z7NVhLq4m2xZLq2YXBkxzMXIGRpNMU88n0EPgWseKl12J7zXs7qrDxPK4sRs2fg==} + binaryen@132.0.0: + resolution: {integrity: sha512-4Usg7yEvwO3FMso3Oov6qkOOHSod1IEv+boKkh77kTljM+eNBouqCTbqWswGzbG+vDNO/b/v2C4gfEh1WgB9Lg==} + hasBin: true + birpc@4.2.0: resolution: {integrity: sha512-KxgKcZPfrtzJDDALHPguGpGJUrzdgpymyiQQgzFjWreHMOpWrnFNVREr5J48x2DBh8ZVioscrV1SBkDipGiX+Q==} @@ -11278,6 +11285,8 @@ snapshots: dependencies: require-from-string: 2.0.2 + binaryen@132.0.0: {} + birpc@4.2.0: {} bl@4.1.0: diff --git a/scripts/engine.ts b/scripts/engine.ts index 9de06d2a2..b95d99ea5 100644 --- a/scripts/engine.ts +++ b/scripts/engine.ts @@ -1,6 +1,7 @@ import { spawnSync } from "node:child_process"; import { mkdirSync } from "node:fs"; -import { resolve } from "node:path"; +import { dirname, resolve } from "node:path"; +import { createRequire } from "node:module"; const root = resolve(import.meta.dirname, ".."); const output = resolve(root, "packages/typing-engine/generated"); @@ -47,6 +48,22 @@ switch (process.argv[2]) { "target/wasm32-unknown-unknown/release/typing_engine_wasm.wasm", ), ]); + const binaryen = dirname( + createRequire(import.meta.url).resolve("binaryen/package.json"), + ); + const wasm = resolve(output, "engine_bg.wasm"); + run(process.execPath, [ + resolve(binaryen, "bin/wasm-opt"), + wasm, + "-Oz", + "--enable-bulk-memory", + "--enable-nontrapping-float-to-int", + "--enable-sign-ext", + "--enable-multivalue", + "--enable-reference-types", + "-o", + wasm, + ]); run("cargo", [ "run", "--locked", From f4188e27269aabc20e1a006962e1ee8a3de1be9f Mon Sep 17 00:00:00 2001 From: Lakshmi Tanmay Date: Fri, 23 Oct 2026 04:15:21 +0530 Subject: [PATCH 30/93] Unify session and generator configuration defaults in Rust --- crates/typing-engine/src/generation.rs | 55 +++++--------------------- crates/typing-engine/src/quotes.rs | 8 +--- crates/typing-engine/src/session.rs | 19 +++++++++ 3 files changed, 30 insertions(+), 52 deletions(-) diff --git a/crates/typing-engine/src/generation.rs b/crates/typing-engine/src/generation.rs index 3e996fc3e..77fed3969 100644 --- a/crates/typing-engine/src/generation.rs +++ b/crates/typing-engine/src/generation.rs @@ -14,44 +14,7 @@ pub struct Language { pub no_lazy_mode: bool, pub additional_accents: Vec<(Text, Text)>, } -#[derive(Clone, Debug, Deserialize, TS)] -#[serde(default, rename_all = "camelCase")] -pub struct GenerationConfig { - pub mode: String, - pub words: usize, - pub language: String, - pub show_all_lines: bool, - pub punctuation: bool, - pub numbers: bool, - pub lazy_mode: bool, - pub british_english: bool, - pub funbox: Vec, - pub quote_length: Vec, - pub custom_polyglot: Vec, -} -impl Default for GenerationConfig { - fn default() -> Self { - let config = crate::Config::default(); - Self { - mode: config.mode, - words: config.words as usize, - language: config.language, - show_all_lines: false, - punctuation: false, - numbers: false, - lazy_mode: false, - british_english: false, - funbox: vec![], - quote_length: vec![1], - custom_polyglot: vec![ - "english".into(), - "spanish".into(), - "french".into(), - "german".into(), - ], - } - } -} +pub type GenerationConfig = crate::Config; #[derive(Clone, Debug, Deserialize, TS)] pub struct CustomLimit { pub mode: String, @@ -326,9 +289,9 @@ impl Generator { } let mut limit = 100; if self.config.show_all_lines && self.config.mode == "words" { - limit = self.config.words; + limit = self.config.words as usize; } - if self.config.mode == "words" && self.config.words == 0 { + if self.config.mode == "words" && self.config.words as usize == 0 { limit = 100; } if let Some(custom) = &self.custom @@ -340,8 +303,8 @@ impl Generator { custom.limit.value.min(100) }; } - if self.config.mode == "words" && self.config.words > 0 { - limit = limit.min(self.config.words); + if self.config.mode == "words" && self.config.words as usize > 0 { + limit = limit.min(self.config.words as usize); } for funbox in &self.config.funbox { if let Some(count) = match funbox.as_str() { @@ -355,8 +318,8 @@ impl Generator { break; } } - if self.config.mode == "words" && self.config.words > 0 { - limit = limit.min(self.config.words); + if self.config.mode == "words" && self.config.words as usize > 0 { + limit = limit.min(self.config.words as usize); } if let Some(quote) = &self.current_quote { limit = if self.config.show_all_lines { @@ -372,7 +335,7 @@ impl Generator { return self.generated >= quote.text_split.len(); } if self.config.mode == "words" { - return self.config.words > 0 && self.generated >= self.config.words; + return self.config.words as usize > 0 && self.generated >= self.config.words as usize; } if self.config.mode == "custom" && let Some(custom) = &self.custom @@ -513,7 +476,7 @@ impl Generator { } if self.repeating && self.config.mode != "quote" { let continuation = self.config.mode == "time" - || (self.config.mode == "words" && index < self.config.words) + || (self.config.mode == "words" && index < self.config.words as usize) || (self.config.mode == "custom" && self.custom.as_ref().is_some_and(|custom| { custom.limit.mode == "time" diff --git a/crates/typing-engine/src/quotes.rs b/crates/typing-engine/src/quotes.rs index d67a7ccde..774055533 100644 --- a/crates/typing-engine/src/quotes.rs +++ b/crates/typing-engine/src/quotes.rs @@ -145,14 +145,10 @@ impl Quotes { Ok(quote) } } -fn remove_language_size(language: &str) -> String { +pub fn remove_language_size(language: &str) -> String { let mut parts: Vec<_> = language.split('_').collect(); if parts.last().is_some_and(|part| { - !part.is_empty() - && part - .trim_end_matches('k') - .chars() - .all(|c| c.is_ascii_digit()) + part.ends_with('k') && part[..part.len() - 1].chars().all(|c| c.is_ascii_digit()) }) { parts.pop(); } diff --git a/crates/typing-engine/src/session.rs b/crates/typing-engine/src/session.rs index 5cf4b7147..3f6a41179 100644 --- a/crates/typing-engine/src/session.rs +++ b/crates/typing-engine/src/session.rs @@ -24,6 +24,13 @@ pub struct Config { pub blind_mode: bool, pub code_unindent_on_backspace: bool, pub funbox: Vec, + pub show_all_lines: bool, + pub punctuation: bool, + pub numbers: bool, + pub lazy_mode: bool, + pub british_english: bool, + pub quote_length: Vec, + pub custom_polyglot: Vec, pub min_burst: String, pub min_burst_custom_speed: f64, pub min_wpm: String, @@ -49,6 +56,18 @@ impl Default for Config { blind_mode: false, code_unindent_on_backspace: false, funbox: vec![], + show_all_lines: false, + punctuation: false, + numbers: false, + lazy_mode: false, + british_english: false, + quote_length: vec![1], + custom_polyglot: vec![ + "english".into(), + "spanish".into(), + "french".into(), + "german".into(), + ], min_burst: "off".into(), min_burst_custom_speed: 100.0, min_wpm: "off".into(), From 58189bea2ba1bf897d0a234ea8434094943e91bb Mon Sep 17 00:00:00 2001 From: Lakshmi Tanmay Date: Fri, 23 Oct 2026 04:15:34 +0530 Subject: [PATCH 31/93] Freeze historical replay, practice and save-validity reference outputs --- .../scripts/capture-analysis-oracle.ts | 123 + packages/typing-fixtures/oracle/analysis.json | 31849 ++++++++++++++++ 2 files changed, 31972 insertions(+) create mode 100644 packages/typing-core/scripts/capture-analysis-oracle.ts create mode 100644 packages/typing-fixtures/oracle/analysis.json diff --git a/packages/typing-core/scripts/capture-analysis-oracle.ts b/packages/typing-core/scripts/capture-analysis-oracle.ts new file mode 100644 index 000000000..7515c1ceb --- /dev/null +++ b/packages/typing-core/scripts/capture-analysis-oracle.ts @@ -0,0 +1,123 @@ +import { writeFileSync } from "node:fs"; +import { resolve } from "node:path"; +import { loadParityCases } from "../__test__/fixtures"; +import type { EventLog } from "../src/events/types"; +import { + replayFrame, + replayDuration, + getReplayWords, + getReplaySteps, + createReplayPrompt, + applyReplayStep, + seekReplay, +} from "../src/replay"; +import { buildPracticeWords } from "../src/practise-words"; +import { getInvalidResultReason } from "../src/result-validity"; +import { units } from "./rust-oracle"; + +const neutralPrompt = (words: ReturnType): unknown => + words.map((word) => ({ + ...word, + letters: word.letters.map((letter) => ({ + ...letter, + char: units(letter.char), + })), + })); +const references = loadParityCases().map(({ name, fixture }) => { + const log = fixture.eventLog as EventLog; + const duration = replayDuration(log); + const replay = [false, true].map((zen) => { + const words = getReplayWords(log, zen); + const steps = getReplaySteps(log, zen); + const prompt = createReplayPrompt(words, steps); + const cursor = { wordIndex: 0, letterIndex: 0 }; + const sounds = steps.map( + (step) => applyReplayStep(prompt, cursor, step) ?? null, + ); + const seeks = [ + { wordIndex: 0, letterIndex: 0 }, + { wordIndex: 1, letterIndex: 2 }, + { wordIndex: 3, letterIndex: 0 }, + ].map((target) => { + const seekWords = createReplayPrompt(getReplayWords(log, zen), steps); + const result = seekReplay(seekWords, steps, target); + return { target, ...result, words: neutralPrompt(seekWords) }; + }); + return { + zen, + words: words.map(units), + steps: steps.map((step) => ({ + ...step, + ...(typeof step.value === "string" ? { value: units(step.value) } : {}), + })), + final: neutralPrompt(prompt), + cursor, + sounds, + seeks, + }; + }); + const words = log.context.targetWords.map((word) => + word.replace(/[ \n]$/, ""), + ); + const practice = (["off", "words", "biwords"] as const).flatMap((missed) => + [false, true].map((slow) => { + const notifications: string[] = []; + const result = buildPracticeWords(log, words, missed, slow, (message) => + notifications.push(message), + ); + return { + missed, + slow, + result: + result === null ? null : { ...result, text: result.text.map(units) }, + notifications, + }; + }), + ); + const recorded = fixture.completedEvent; + const result = { + mode: String(recorded["mode"]), + mode2: String(recorded["mode2"]), + testDuration: Number(recorded["testDuration"]), + wpm: Number(recorded["wpm"]), + rawWpm: Number(recorded["rawWpm"]), + acc: Number(recorded["acc"]), + } as Parameters[0]["result"]; + const validity = [false, true].flatMap((bailedOut) => + [false, true].flatMap((failed) => + [false, true].map((repeated) => { + const input = { + result, + bailedOut, + failed, + repeated, + lbOptOut: false, + customLimit: { mode: "word" as const, value: 10 }, + }; + return { + input, + reason: getInvalidResultReason({ ...input, eventLog: log }) ?? null, + }; + }), + ), + ); + return { + name, + frames: [-1, 0, duration / 4, duration / 2, duration].map((time) => { + const frame = replayFrame(log, time); + return { + time, + inputs: frame.inputs.map(units), + activeIndex: frame.activeIndex, + }; + }), + replay, + practice, + validity, + }; +}); +writeFileSync( + resolve(import.meta.dirname, "../../typing-fixtures/oracle/analysis.json"), + `${JSON.stringify(references, null, 2)}\n`, + { flag: "wx" }, +); diff --git a/packages/typing-fixtures/oracle/analysis.json b/packages/typing-fixtures/oracle/analysis.json new file mode 100644 index 000000000..dad3b5d1a --- /dev/null +++ b/packages/typing-fixtures/oracle/analysis.json @@ -0,0 +1,31849 @@ +[ + { + "name": "custom-repeat", + "frames": [ + { + "time": -1, + "inputs": [], + "activeIndex": 0 + }, + { + "time": 0, + "inputs": [[84]], + "activeIndex": 0 + }, + { + "time": 1911.175, + "inputs": [[84, 104, 101, 32], [113, 117, 105, 99, 107, 32], [98]], + "activeIndex": 2 + }, + { + "time": 3822.35, + "inputs": [ + [84, 104, 101, 32], + [113, 117, 105, 99, 107, 32], + [98, 114, 111, 119, 110, 115, 32], + [102, 111, 120, 32], + [106] + ], + "activeIndex": 4 + }, + { + "time": 7644.7, + "inputs": [ + [84, 104, 101, 32], + [113, 117, 105, 99, 107, 32], + [98, 114, 111, 119, 110, 115, 32], + [102, 111, 120, 32], + [106, 117, 109, 112, 115, 32], + [111, 118, 101, 114, 32], + [116, 104, 101, 32], + [108, 97, 122, 121, 32], + [100, 111, 103] + ], + "activeIndex": 8 + } + ], + "replay": [ + { + "zen": false, + "words": [ + [84, 104, 101, 32], + [113, 117, 105, 99, 107, 32], + [98, 114, 111, 119, 110, 32], + [102, 111, 120, 32], + [106, 117, 109, 112, 115, 32], + [111, 118, 101, 114, 32], + [116, 104, 101, 32], + [108, 97, 122, 121, 32], + [100, 111, 103] + ], + "steps": [ + { + "action": "correctLetter", + "value": [84], + "time": 0 + }, + { + "action": "correctLetter", + "value": [104], + "time": 106.5 + }, + { + "action": "correctLetter", + "value": [101], + "time": 205 + }, + { + "action": "correctLetter", + "value": [32], + "time": 385.9 + }, + { + "action": "submitCorrectWord", + "time": 685.6 + }, + { + "action": "correctLetter", + "value": [113], + "time": 685.6 + }, + { + "action": "correctLetter", + "value": [117], + "time": 867.4 + }, + { + "action": "correctLetter", + "value": [105], + "time": 1036.4 + }, + { + "action": "correctLetter", + "value": [99], + "time": 1184.7 + }, + { + "action": "correctLetter", + "value": [107], + "time": 1382.7 + }, + { + "action": "correctLetter", + "value": [32], + "time": 1563.1 + }, + { + "action": "submitCorrectWord", + "time": 1811 + }, + { + "action": "correctLetter", + "value": [98], + "time": 1811 + }, + { + "action": "correctLetter", + "value": [114], + "time": 1998.5 + }, + { + "action": "correctLetter", + "value": [111], + "time": 2118.2 + }, + { + "action": "correctLetter", + "value": [119], + "time": 2264.6 + }, + { + "action": "correctLetter", + "value": [110], + "time": 2385.4 + }, + { + "action": "incorrectLetter", + "value": [115], + "time": 2577.8 + }, + { + "action": "incorrectLetter", + "value": [32], + "time": 2714.7 + }, + { + "action": "submitErrorWord", + "time": 2995.9 + }, + { + "action": "correctLetter", + "value": [102], + "time": 2995.9 + }, + { + "action": "correctLetter", + "value": [111], + "time": 3109.2 + }, + { + "action": "correctLetter", + "value": [120], + "time": 3288.7 + }, + { + "action": "correctLetter", + "value": [32], + "time": 3439 + }, + { + "action": "submitCorrectWord", + "time": 3745.1 + }, + { + "action": "correctLetter", + "value": [106], + "time": 3745.1 + }, + { + "action": "correctLetter", + "value": [117], + "time": 3913.6 + }, + { + "action": "correctLetter", + "value": [109], + "time": 4040.4 + }, + { + "action": "correctLetter", + "value": [112], + "time": 4202 + }, + { + "action": "correctLetter", + "value": [115], + "time": 4406.9 + }, + { + "action": "correctLetter", + "value": [32], + "time": 4537.6 + }, + { + "action": "submitCorrectWord", + "time": 4803 + }, + { + "action": "correctLetter", + "value": [111], + "time": 4803 + }, + { + "action": "correctLetter", + "value": [118], + "time": 4993.9 + }, + { + "action": "correctLetter", + "value": [101], + "time": 5202.3 + }, + { + "action": "correctLetter", + "value": [114], + "time": 5331.6 + }, + { + "action": "correctLetter", + "value": [32], + "time": 5457.4 + }, + { + "action": "submitCorrectWord", + "time": 5681.2 + }, + { + "action": "correctLetter", + "value": [116], + "time": 5681.2 + }, + { + "action": "correctLetter", + "value": [104], + "time": 5856.9 + }, + { + "action": "correctLetter", + "value": [101], + "time": 5977.8 + }, + { + "action": "correctLetter", + "value": [32], + "time": 6071.7 + }, + { + "action": "submitCorrectWord", + "time": 6409.2 + }, + { + "action": "correctLetter", + "value": [108], + "time": 6409.2 + }, + { + "action": "correctLetter", + "value": [97], + "time": 6562 + }, + { + "action": "correctLetter", + "value": [122], + "time": 6755.5 + }, + { + "action": "correctLetter", + "value": [121], + "time": 6960.8 + }, + { + "action": "correctLetter", + "value": [32], + "time": 7111.9 + }, + { + "action": "submitCorrectWord", + "time": 7302.6 + }, + { + "action": "correctLetter", + "value": [100], + "time": 7302.6 + }, + { + "action": "correctLetter", + "value": [111], + "time": 7406.3 + }, + { + "action": "correctLetter", + "value": [103], + "time": 7594 + } + ], + "final": [ + { + "letters": [ + { + "char": [84], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [104], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [113], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [117], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [105], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [99], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [107], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [98], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [119], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": true, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": true, + "extra": true + } + ], + "error": true + }, + { + "letters": [ + { + "char": [102], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [120], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [106], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [117], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [109], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [112], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [118], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [116], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [104], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [108], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [122], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [121], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [100], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [103], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + } + ], + "cursor": { + "wordIndex": 8, + "letterIndex": 3 + }, + "sounds": [ + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "error", + "error", + "error", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click" + ], + "seeks": [ + { + "target": { + "wordIndex": 0, + "letterIndex": 0 + }, + "cursor": { + "wordIndex": 0, + "letterIndex": 0 + }, + "nextIndex": 0, + "words": [ + { + "letters": [ + { + "char": [84], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [104], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [113], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [117], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [105], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [99], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [107], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [98], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [119], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [102], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [120], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [106], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [117], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [109], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [112], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [118], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [104], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [108], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [122], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [121], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [100], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [103], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + } + ] + }, + { + "target": { + "wordIndex": 1, + "letterIndex": 2 + }, + "cursor": { + "wordIndex": 1, + "letterIndex": 2 + }, + "nextIndex": 7, + "words": [ + { + "letters": [ + { + "char": [84], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [104], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [113], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [117], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [105], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [99], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [107], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [98], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [119], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [102], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [120], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [106], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [117], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [109], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [112], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [118], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [104], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [108], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [122], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [121], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [100], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [103], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + } + ] + }, + { + "target": { + "wordIndex": 3, + "letterIndex": 0 + }, + "cursor": { + "wordIndex": 3, + "letterIndex": 0 + }, + "nextIndex": 20, + "words": [ + { + "letters": [ + { + "char": [84], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [104], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [113], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [117], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [105], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [99], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [107], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [98], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [119], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": true, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": true, + "extra": true + } + ], + "error": true + }, + { + "letters": [ + { + "char": [102], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [120], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [106], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [117], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [109], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [112], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [118], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [104], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [108], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [122], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [121], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [100], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [103], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + } + ] + } + ] + }, + { + "zen": true, + "words": [ + [84, 104, 101, 32], + [113, 117, 105, 99, 107, 32], + [98, 114, 111, 119, 110, 115, 32], + [102, 111, 120, 32], + [106, 117, 109, 112, 115, 32], + [111, 118, 101, 114, 32], + [116, 104, 101, 32], + [108, 97, 122, 121, 32], + [100, 111, 103] + ], + "steps": [ + { + "action": "correctLetter", + "value": [84], + "time": 0 + }, + { + "action": "correctLetter", + "value": [104], + "time": 106.5 + }, + { + "action": "correctLetter", + "value": [101], + "time": 205 + }, + { + "action": "correctLetter", + "value": [32], + "time": 385.9 + }, + { + "action": "submitCorrectWord", + "time": 685.6 + }, + { + "action": "correctLetter", + "value": [113], + "time": 685.6 + }, + { + "action": "correctLetter", + "value": [117], + "time": 867.4 + }, + { + "action": "correctLetter", + "value": [105], + "time": 1036.4 + }, + { + "action": "correctLetter", + "value": [99], + "time": 1184.7 + }, + { + "action": "correctLetter", + "value": [107], + "time": 1382.7 + }, + { + "action": "correctLetter", + "value": [32], + "time": 1563.1 + }, + { + "action": "submitCorrectWord", + "time": 1811 + }, + { + "action": "correctLetter", + "value": [98], + "time": 1811 + }, + { + "action": "correctLetter", + "value": [114], + "time": 1998.5 + }, + { + "action": "correctLetter", + "value": [111], + "time": 2118.2 + }, + { + "action": "correctLetter", + "value": [119], + "time": 2264.6 + }, + { + "action": "correctLetter", + "value": [110], + "time": 2385.4 + }, + { + "action": "incorrectLetter", + "value": [115], + "time": 2577.8 + }, + { + "action": "incorrectLetter", + "value": [32], + "time": 2714.7 + }, + { + "action": "submitCorrectWord", + "time": 2995.9 + }, + { + "action": "correctLetter", + "value": [102], + "time": 2995.9 + }, + { + "action": "correctLetter", + "value": [111], + "time": 3109.2 + }, + { + "action": "correctLetter", + "value": [120], + "time": 3288.7 + }, + { + "action": "correctLetter", + "value": [32], + "time": 3439 + }, + { + "action": "submitCorrectWord", + "time": 3745.1 + }, + { + "action": "correctLetter", + "value": [106], + "time": 3745.1 + }, + { + "action": "correctLetter", + "value": [117], + "time": 3913.6 + }, + { + "action": "correctLetter", + "value": [109], + "time": 4040.4 + }, + { + "action": "correctLetter", + "value": [112], + "time": 4202 + }, + { + "action": "correctLetter", + "value": [115], + "time": 4406.9 + }, + { + "action": "correctLetter", + "value": [32], + "time": 4537.6 + }, + { + "action": "submitCorrectWord", + "time": 4803 + }, + { + "action": "correctLetter", + "value": [111], + "time": 4803 + }, + { + "action": "correctLetter", + "value": [118], + "time": 4993.9 + }, + { + "action": "correctLetter", + "value": [101], + "time": 5202.3 + }, + { + "action": "correctLetter", + "value": [114], + "time": 5331.6 + }, + { + "action": "correctLetter", + "value": [32], + "time": 5457.4 + }, + { + "action": "submitCorrectWord", + "time": 5681.2 + }, + { + "action": "correctLetter", + "value": [116], + "time": 5681.2 + }, + { + "action": "correctLetter", + "value": [104], + "time": 5856.9 + }, + { + "action": "correctLetter", + "value": [101], + "time": 5977.8 + }, + { + "action": "correctLetter", + "value": [32], + "time": 6071.7 + }, + { + "action": "submitCorrectWord", + "time": 6409.2 + }, + { + "action": "correctLetter", + "value": [108], + "time": 6409.2 + }, + { + "action": "correctLetter", + "value": [97], + "time": 6562 + }, + { + "action": "correctLetter", + "value": [122], + "time": 6755.5 + }, + { + "action": "correctLetter", + "value": [121], + "time": 6960.8 + }, + { + "action": "correctLetter", + "value": [32], + "time": 7111.9 + }, + { + "action": "submitCorrectWord", + "time": 7302.6 + }, + { + "action": "correctLetter", + "value": [100], + "time": 7302.6 + }, + { + "action": "correctLetter", + "value": [111], + "time": 7406.3 + }, + { + "action": "correctLetter", + "value": [103], + "time": 7594 + } + ], + "final": [ + { + "letters": [ + { + "char": [84], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [104], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [113], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [117], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [105], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [99], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [107], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [98], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [119], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": false, + "incorrect": true, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": true, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [102], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [120], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [106], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [117], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [109], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [112], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [118], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [116], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [104], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [108], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [122], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [121], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [100], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [103], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + } + ], + "cursor": { + "wordIndex": 8, + "letterIndex": 3 + }, + "sounds": [ + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "error", + "error", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click" + ], + "seeks": [ + { + "target": { + "wordIndex": 0, + "letterIndex": 0 + }, + "cursor": { + "wordIndex": 0, + "letterIndex": 0 + }, + "nextIndex": 0, + "words": [ + { + "letters": [ + { + "char": [84], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [104], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [113], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [117], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [105], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [99], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [107], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [98], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [119], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [102], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [120], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [106], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [117], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [109], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [112], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [118], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [104], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [108], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [122], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [121], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [100], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [103], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + } + ] + }, + { + "target": { + "wordIndex": 1, + "letterIndex": 2 + }, + "cursor": { + "wordIndex": 1, + "letterIndex": 2 + }, + "nextIndex": 7, + "words": [ + { + "letters": [ + { + "char": [84], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [104], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [113], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [117], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [105], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [99], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [107], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [98], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [119], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [102], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [120], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [106], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [117], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [109], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [112], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [118], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [104], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [108], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [122], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [121], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [100], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [103], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + } + ] + }, + { + "target": { + "wordIndex": 3, + "letterIndex": 0 + }, + "cursor": { + "wordIndex": 3, + "letterIndex": 0 + }, + "nextIndex": 20, + "words": [ + { + "letters": [ + { + "char": [84], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [104], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [113], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [117], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [105], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [99], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [107], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [98], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [119], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": false, + "incorrect": true, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": true, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [102], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [120], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [106], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [117], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [109], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [112], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [118], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [104], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [108], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [122], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [121], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [100], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [103], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + } + ] + } + ] + } + ], + "practice": [ + { + "missed": "off", + "slow": false, + "result": null, + "notifications": ["Could not start a new custom test"] + }, + { + "missed": "off", + "slow": true, + "result": { + "text": [ + [113, 117, 105, 99, 107], + [113, 117, 105, 99, 107], + [108, 97, 122, 121] + ], + "sectionLimit": 10 + }, + "notifications": [] + }, + { + "missed": "words", + "slow": false, + "result": { + "text": [ + [98, 114, 111, 119, 110], + [98, 114, 111, 119, 110] + ], + "sectionLimit": 5 + }, + "notifications": [] + }, + { + "missed": "words", + "slow": true, + "result": { + "text": [ + [98, 114, 111, 119, 110], + [98, 114, 111, 119, 110], + [113, 117, 105, 99, 107], + [113, 117, 105, 99, 107], + [108, 97, 122, 121] + ], + "sectionLimit": 15 + }, + "notifications": [] + }, + { + "missed": "biwords", + "slow": false, + "result": { + "text": [ + [113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110], + [113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110] + ], + "sectionLimit": 5 + }, + "notifications": [] + }, + { + "missed": "biwords", + "slow": true, + "result": { + "text": [ + [113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110], + [113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110], + [113, 117, 105, 99, 107], + [113, 117, 105, 99, 107], + [108, 97, 122, 121] + ], + "sectionLimit": 15 + }, + "notifications": [] + } + ], + "validity": [ + { + "input": { + "result": { + "mode": "custom", + "mode2": "custom", + "testDuration": 7.5954, + "wpm": 58.46, + "rawWpm": 69.52, + "acc": 95.45 + }, + "bailedOut": false, + "failed": false, + "repeated": false, + "lbOptOut": false, + "customLimit": { + "mode": "word", + "value": 10 + } + }, + "reason": null + }, + { + "input": { + "result": { + "mode": "custom", + "mode2": "custom", + "testDuration": 7.5954, + "wpm": 58.46, + "rawWpm": 69.52, + "acc": 95.45 + }, + "bailedOut": false, + "failed": false, + "repeated": true, + "lbOptOut": false, + "customLimit": { + "mode": "word", + "value": 10 + } + }, + "reason": "repeated" + }, + { + "input": { + "result": { + "mode": "custom", + "mode2": "custom", + "testDuration": 7.5954, + "wpm": 58.46, + "rawWpm": 69.52, + "acc": 95.45 + }, + "bailedOut": false, + "failed": true, + "repeated": false, + "lbOptOut": false, + "customLimit": { + "mode": "word", + "value": 10 + } + }, + "reason": "failed" + }, + { + "input": { + "result": { + "mode": "custom", + "mode2": "custom", + "testDuration": 7.5954, + "wpm": 58.46, + "rawWpm": 69.52, + "acc": 95.45 + }, + "bailedOut": false, + "failed": true, + "repeated": true, + "lbOptOut": false, + "customLimit": { + "mode": "word", + "value": 10 + } + }, + "reason": "failed" + }, + { + "input": { + "result": { + "mode": "custom", + "mode2": "custom", + "testDuration": 7.5954, + "wpm": 58.46, + "rawWpm": 69.52, + "acc": 95.45 + }, + "bailedOut": true, + "failed": false, + "repeated": false, + "lbOptOut": false, + "customLimit": { + "mode": "word", + "value": 10 + } + }, + "reason": null + }, + { + "input": { + "result": { + "mode": "custom", + "mode2": "custom", + "testDuration": 7.5954, + "wpm": 58.46, + "rawWpm": 69.52, + "acc": 95.45 + }, + "bailedOut": true, + "failed": false, + "repeated": true, + "lbOptOut": false, + "customLimit": { + "mode": "word", + "value": 10 + } + }, + "reason": "repeated" + }, + { + "input": { + "result": { + "mode": "custom", + "mode2": "custom", + "testDuration": 7.5954, + "wpm": 58.46, + "rawWpm": 69.52, + "acc": 95.45 + }, + "bailedOut": true, + "failed": true, + "repeated": false, + "lbOptOut": false, + "customLimit": { + "mode": "word", + "value": 10 + } + }, + "reason": "failed" + }, + { + "input": { + "result": { + "mode": "custom", + "mode2": "custom", + "testDuration": 7.5954, + "wpm": 58.46, + "rawWpm": 69.52, + "acc": 95.45 + }, + "bailedOut": true, + "failed": true, + "repeated": true, + "lbOptOut": false, + "customLimit": { + "mode": "word", + "value": 10 + } + }, + "reason": "failed" + } + ] + }, + { + "name": "quote-short", + "frames": [ + { + "time": -1, + "inputs": [], + "activeIndex": 0 + }, + { + "time": 0, + "inputs": [[120]], + "activeIndex": 0 + }, + { + "time": 2904.1125, + "inputs": [ + [67, 111, 117, 114, 97, 103, 101, 32], + [105, 115, 32], + [114, 101, 115, 105, 115] + ], + "activeIndex": 2 + }, + { + "time": 5808.225, + "inputs": [ + [67, 111, 117, 114, 97, 103, 101, 32], + [105, 115, 32], + [114, 101, 115, 105, 115, 116, 97, 110, 99, 101, 32], + [116, 111, 32], + [102, 101, 97, 114, 44, 32], + [109, 97, 115, 116] + ], + "activeIndex": 5 + }, + { + "time": 11616.45, + "inputs": [ + [67, 111, 117, 114, 97, 103, 101, 32], + [105, 115, 32], + [114, 101, 115, 105, 115, 116, 97, 110, 99, 101, 32], + [116, 111, 32], + [102, 101, 97, 114, 44, 32], + [109, 97, 115, 116, 101, 114, 121, 32], + [111, 102, 32], + [102, 101, 97, 114, 32], + [45, 32], + [110, 111, 116, 32], + [97, 98, 115, 101, 110, 99, 101, 32], + [111, 102, 32], + [102, 101, 97, 114, 46] + ], + "activeIndex": 12 + } + ], + "replay": [ + { + "zen": false, + "words": [ + [67, 111, 117, 114, 97, 103, 101, 32], + [105, 115, 32], + [114, 101, 115, 105, 115, 116, 97, 110, 99, 101, 32], + [116, 111, 32], + [102, 101, 97, 114, 44, 32], + [109, 97, 115, 116, 101, 114, 121, 32], + [111, 102, 32], + [102, 101, 97, 114, 32], + [45, 32], + [110, 111, 116, 32], + [97, 98, 115, 101, 110, 99, 101, 32], + [111, 102, 32], + [102, 101, 97, 114, 46] + ], + "steps": [ + { + "action": "incorrectLetter", + "value": [120], + "time": 0 + }, + { + "action": "setLetterIndex", + "value": 0, + "time": 164.4 + }, + { + "action": "correctLetter", + "value": [67], + "time": 319.1 + }, + { + "action": "correctLetter", + "value": [111], + "time": 411.7 + }, + { + "action": "correctLetter", + "value": [117], + "time": 615.1 + }, + { + "action": "correctLetter", + "value": [114], + "time": 820.2 + }, + { + "action": "correctLetter", + "value": [97], + "time": 910.2 + }, + { + "action": "correctLetter", + "value": [103], + "time": 1057.4 + }, + { + "action": "correctLetter", + "value": [101], + "time": 1222.6 + }, + { + "action": "correctLetter", + "value": [32], + "time": 1358.9 + }, + { + "action": "submitCorrectWord", + "time": 1640 + }, + { + "action": "correctLetter", + "value": [105], + "time": 1640 + }, + { + "action": "correctLetter", + "value": [115], + "time": 1759.8 + }, + { + "action": "correctLetter", + "value": [32], + "time": 1953.6 + }, + { + "action": "submitCorrectWord", + "time": 2289.2 + }, + { + "action": "correctLetter", + "value": [114], + "time": 2289.2 + }, + { + "action": "correctLetter", + "value": [101], + "time": 2494.1 + }, + { + "action": "correctLetter", + "value": [115], + "time": 2609.5 + }, + { + "action": "correctLetter", + "value": [105], + "time": 2725.1 + }, + { + "action": "correctLetter", + "value": [115], + "time": 2841.3 + }, + { + "action": "correctLetter", + "value": [116], + "time": 2979.1 + }, + { + "action": "correctLetter", + "value": [97], + "time": 3137 + }, + { + "action": "correctLetter", + "value": [110], + "time": 3297.3 + }, + { + "action": "correctLetter", + "value": [99], + "time": 3473.6 + }, + { + "action": "correctLetter", + "value": [101], + "time": 3595.9 + }, + { + "action": "correctLetter", + "value": [32], + "time": 3786.4 + }, + { + "action": "submitCorrectWord", + "time": 3996.1 + }, + { + "action": "correctLetter", + "value": [116], + "time": 3996.1 + }, + { + "action": "correctLetter", + "value": [111], + "time": 4165.7 + }, + { + "action": "correctLetter", + "value": [32], + "time": 4295.7 + }, + { + "action": "submitCorrectWord", + "time": 4501 + }, + { + "action": "correctLetter", + "value": [102], + "time": 4501 + }, + { + "action": "correctLetter", + "value": [101], + "time": 4623 + }, + { + "action": "correctLetter", + "value": [97], + "time": 4723.6 + }, + { + "action": "correctLetter", + "value": [114], + "time": 4821.9 + }, + { + "action": "correctLetter", + "value": [44], + "time": 4971.2 + }, + { + "action": "correctLetter", + "value": [32], + "time": 5123 + }, + { + "action": "submitCorrectWord", + "time": 5319.7 + }, + { + "action": "correctLetter", + "value": [109], + "time": 5319.7 + }, + { + "action": "correctLetter", + "value": [97], + "time": 5489.6 + }, + { + "action": "correctLetter", + "value": [115], + "time": 5613.9 + }, + { + "action": "correctLetter", + "value": [116], + "time": 5718.3 + }, + { + "action": "correctLetter", + "value": [101], + "time": 5879.4 + }, + { + "action": "correctLetter", + "value": [114], + "time": 6053.4 + }, + { + "action": "correctLetter", + "value": [121], + "time": 6162.2 + }, + { + "action": "correctLetter", + "value": [32], + "time": 6304 + }, + { + "action": "submitCorrectWord", + "time": 6545.3 + }, + { + "action": "correctLetter", + "value": [111], + "time": 6545.3 + }, + { + "action": "correctLetter", + "value": [102], + "time": 6737.9 + }, + { + "action": "correctLetter", + "value": [32], + "time": 6838.5 + }, + { + "action": "submitCorrectWord", + "time": 7077.4 + }, + { + "action": "correctLetter", + "value": [102], + "time": 7077.4 + }, + { + "action": "correctLetter", + "value": [101], + "time": 7227.1 + }, + { + "action": "correctLetter", + "value": [97], + "time": 7411.1 + }, + { + "action": "correctLetter", + "value": [114], + "time": 7532.9 + }, + { + "action": "correctLetter", + "value": [32], + "time": 7662.6 + }, + { + "action": "submitCorrectWord", + "time": 7882.1 + }, + { + "action": "correctLetter", + "value": [45], + "time": 7882.1 + }, + { + "action": "correctLetter", + "value": [32], + "time": 7994.4 + }, + { + "action": "submitCorrectWord", + "time": 8196 + }, + { + "action": "correctLetter", + "value": [110], + "time": 8196 + }, + { + "action": "correctLetter", + "value": [111], + "time": 8373.1 + }, + { + "action": "correctLetter", + "value": [116], + "time": 8538.3 + }, + { + "action": "correctLetter", + "value": [32], + "time": 8736.8 + }, + { + "action": "submitCorrectWord", + "time": 9021.5 + }, + { + "action": "correctLetter", + "value": [97], + "time": 9021.5 + }, + { + "action": "correctLetter", + "value": [98], + "time": 9224.5 + }, + { + "action": "correctLetter", + "value": [115], + "time": 9397.8 + }, + { + "action": "correctLetter", + "value": [101], + "time": 9534 + }, + { + "action": "correctLetter", + "value": [110], + "time": 9647.8 + }, + { + "action": "correctLetter", + "value": [99], + "time": 9749.1 + }, + { + "action": "correctLetter", + "value": [101], + "time": 9847.6 + }, + { + "action": "correctLetter", + "value": [32], + "time": 10032.7 + }, + { + "action": "submitCorrectWord", + "time": 10295.7 + }, + { + "action": "correctLetter", + "value": [111], + "time": 10295.7 + }, + { + "action": "correctLetter", + "value": [102], + "time": 10504 + }, + { + "action": "correctLetter", + "value": [32], + "time": 10656.2 + }, + { + "action": "submitCorrectWord", + "time": 10954.3 + }, + { + "action": "correctLetter", + "value": [102], + "time": 10954.3 + }, + { + "action": "correctLetter", + "value": [101], + "time": 11073.5 + }, + { + "action": "correctLetter", + "value": [97], + "time": 11229.7 + }, + { + "action": "correctLetter", + "value": [114], + "time": 11419 + }, + { + "action": "correctLetter", + "value": [46], + "time": 11539.5 + } + ], + "final": [ + { + "letters": [ + { + "char": [67], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [117], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [103], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [105], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [114], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [105], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [99], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [116], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [102], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [44], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [109], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [121], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [102], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [102], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [45], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [110], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [97], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [98], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [99], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [102], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [102], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [46], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + } + ], + "cursor": { + "wordIndex": 12, + "letterIndex": 5 + }, + "sounds": [ + "error", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click" + ], + "seeks": [ + { + "target": { + "wordIndex": 0, + "letterIndex": 0 + }, + "cursor": { + "wordIndex": 0, + "letterIndex": 0 + }, + "nextIndex": 0, + "words": [ + { + "letters": [ + { + "char": [67], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [117], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [103], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [105], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [105], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [99], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [102], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [44], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [109], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [121], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [102], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [102], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [45], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [98], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [99], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [102], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [102], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [46], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + } + ] + }, + { + "target": { + "wordIndex": 1, + "letterIndex": 2 + }, + "cursor": { + "wordIndex": 1, + "letterIndex": 2 + }, + "nextIndex": 13, + "words": [ + { + "letters": [ + { + "char": [67], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [117], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [103], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [105], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [105], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [99], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [102], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [44], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [109], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [121], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [102], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [102], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [45], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [98], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [99], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [102], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [102], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [46], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + } + ] + }, + { + "target": { + "wordIndex": 3, + "letterIndex": 0 + }, + "cursor": { + "wordIndex": 3, + "letterIndex": 0 + }, + "nextIndex": 27, + "words": [ + { + "letters": [ + { + "char": [67], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [117], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [103], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [105], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [114], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [105], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [99], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [102], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [44], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [109], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [121], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [102], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [102], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [45], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [98], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [99], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [102], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [102], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [46], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + } + ] + } + ] + }, + { + "zen": true, + "words": [ + [67, 111, 117, 114, 97, 103, 101, 32], + [105, 115, 32], + [114, 101, 115, 105, 115, 116, 97, 110, 99, 101, 32], + [116, 111, 32], + [102, 101, 97, 114, 44, 32], + [109, 97, 115, 116, 101, 114, 121, 32], + [111, 102, 32], + [102, 101, 97, 114, 32], + [45, 32], + [110, 111, 116, 32], + [97, 98, 115, 101, 110, 99, 101, 32], + [111, 102, 32], + [102, 101, 97, 114, 46] + ], + "steps": [ + { + "action": "incorrectLetter", + "value": [120], + "time": 0 + }, + { + "action": "setLetterIndex", + "value": 0, + "time": 164.4 + }, + { + "action": "correctLetter", + "value": [67], + "time": 319.1 + }, + { + "action": "correctLetter", + "value": [111], + "time": 411.7 + }, + { + "action": "correctLetter", + "value": [117], + "time": 615.1 + }, + { + "action": "correctLetter", + "value": [114], + "time": 820.2 + }, + { + "action": "correctLetter", + "value": [97], + "time": 910.2 + }, + { + "action": "correctLetter", + "value": [103], + "time": 1057.4 + }, + { + "action": "correctLetter", + "value": [101], + "time": 1222.6 + }, + { + "action": "correctLetter", + "value": [32], + "time": 1358.9 + }, + { + "action": "submitCorrectWord", + "time": 1640 + }, + { + "action": "correctLetter", + "value": [105], + "time": 1640 + }, + { + "action": "correctLetter", + "value": [115], + "time": 1759.8 + }, + { + "action": "correctLetter", + "value": [32], + "time": 1953.6 + }, + { + "action": "submitCorrectWord", + "time": 2289.2 + }, + { + "action": "correctLetter", + "value": [114], + "time": 2289.2 + }, + { + "action": "correctLetter", + "value": [101], + "time": 2494.1 + }, + { + "action": "correctLetter", + "value": [115], + "time": 2609.5 + }, + { + "action": "correctLetter", + "value": [105], + "time": 2725.1 + }, + { + "action": "correctLetter", + "value": [115], + "time": 2841.3 + }, + { + "action": "correctLetter", + "value": [116], + "time": 2979.1 + }, + { + "action": "correctLetter", + "value": [97], + "time": 3137 + }, + { + "action": "correctLetter", + "value": [110], + "time": 3297.3 + }, + { + "action": "correctLetter", + "value": [99], + "time": 3473.6 + }, + { + "action": "correctLetter", + "value": [101], + "time": 3595.9 + }, + { + "action": "correctLetter", + "value": [32], + "time": 3786.4 + }, + { + "action": "submitCorrectWord", + "time": 3996.1 + }, + { + "action": "correctLetter", + "value": [116], + "time": 3996.1 + }, + { + "action": "correctLetter", + "value": [111], + "time": 4165.7 + }, + { + "action": "correctLetter", + "value": [32], + "time": 4295.7 + }, + { + "action": "submitCorrectWord", + "time": 4501 + }, + { + "action": "correctLetter", + "value": [102], + "time": 4501 + }, + { + "action": "correctLetter", + "value": [101], + "time": 4623 + }, + { + "action": "correctLetter", + "value": [97], + "time": 4723.6 + }, + { + "action": "correctLetter", + "value": [114], + "time": 4821.9 + }, + { + "action": "correctLetter", + "value": [44], + "time": 4971.2 + }, + { + "action": "correctLetter", + "value": [32], + "time": 5123 + }, + { + "action": "submitCorrectWord", + "time": 5319.7 + }, + { + "action": "correctLetter", + "value": [109], + "time": 5319.7 + }, + { + "action": "correctLetter", + "value": [97], + "time": 5489.6 + }, + { + "action": "correctLetter", + "value": [115], + "time": 5613.9 + }, + { + "action": "correctLetter", + "value": [116], + "time": 5718.3 + }, + { + "action": "correctLetter", + "value": [101], + "time": 5879.4 + }, + { + "action": "correctLetter", + "value": [114], + "time": 6053.4 + }, + { + "action": "correctLetter", + "value": [121], + "time": 6162.2 + }, + { + "action": "correctLetter", + "value": [32], + "time": 6304 + }, + { + "action": "submitCorrectWord", + "time": 6545.3 + }, + { + "action": "correctLetter", + "value": [111], + "time": 6545.3 + }, + { + "action": "correctLetter", + "value": [102], + "time": 6737.9 + }, + { + "action": "correctLetter", + "value": [32], + "time": 6838.5 + }, + { + "action": "submitCorrectWord", + "time": 7077.4 + }, + { + "action": "correctLetter", + "value": [102], + "time": 7077.4 + }, + { + "action": "correctLetter", + "value": [101], + "time": 7227.1 + }, + { + "action": "correctLetter", + "value": [97], + "time": 7411.1 + }, + { + "action": "correctLetter", + "value": [114], + "time": 7532.9 + }, + { + "action": "correctLetter", + "value": [32], + "time": 7662.6 + }, + { + "action": "submitCorrectWord", + "time": 7882.1 + }, + { + "action": "correctLetter", + "value": [45], + "time": 7882.1 + }, + { + "action": "correctLetter", + "value": [32], + "time": 7994.4 + }, + { + "action": "submitCorrectWord", + "time": 8196 + }, + { + "action": "correctLetter", + "value": [110], + "time": 8196 + }, + { + "action": "correctLetter", + "value": [111], + "time": 8373.1 + }, + { + "action": "correctLetter", + "value": [116], + "time": 8538.3 + }, + { + "action": "correctLetter", + "value": [32], + "time": 8736.8 + }, + { + "action": "submitCorrectWord", + "time": 9021.5 + }, + { + "action": "correctLetter", + "value": [97], + "time": 9021.5 + }, + { + "action": "correctLetter", + "value": [98], + "time": 9224.5 + }, + { + "action": "correctLetter", + "value": [115], + "time": 9397.8 + }, + { + "action": "correctLetter", + "value": [101], + "time": 9534 + }, + { + "action": "correctLetter", + "value": [110], + "time": 9647.8 + }, + { + "action": "correctLetter", + "value": [99], + "time": 9749.1 + }, + { + "action": "correctLetter", + "value": [101], + "time": 9847.6 + }, + { + "action": "correctLetter", + "value": [32], + "time": 10032.7 + }, + { + "action": "submitCorrectWord", + "time": 10295.7 + }, + { + "action": "correctLetter", + "value": [111], + "time": 10295.7 + }, + { + "action": "correctLetter", + "value": [102], + "time": 10504 + }, + { + "action": "correctLetter", + "value": [32], + "time": 10656.2 + }, + { + "action": "submitCorrectWord", + "time": 10954.3 + }, + { + "action": "correctLetter", + "value": [102], + "time": 10954.3 + }, + { + "action": "correctLetter", + "value": [101], + "time": 11073.5 + }, + { + "action": "correctLetter", + "value": [97], + "time": 11229.7 + }, + { + "action": "correctLetter", + "value": [114], + "time": 11419 + }, + { + "action": "correctLetter", + "value": [46], + "time": 11539.5 + } + ], + "final": [ + { + "letters": [ + { + "char": [67], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [117], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [103], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [105], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [114], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [105], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [99], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [116], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [102], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [44], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [109], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [121], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [102], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [102], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [45], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [110], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [97], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [98], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [99], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [102], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [102], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [46], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + } + ], + "cursor": { + "wordIndex": 12, + "letterIndex": 5 + }, + "sounds": [ + "error", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click" + ], + "seeks": [ + { + "target": { + "wordIndex": 0, + "letterIndex": 0 + }, + "cursor": { + "wordIndex": 0, + "letterIndex": 0 + }, + "nextIndex": 0, + "words": [ + { + "letters": [ + { + "char": [67], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [117], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [103], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [105], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [105], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [99], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [102], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [44], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [109], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [121], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [102], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [102], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [45], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [98], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [99], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [102], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [102], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [46], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + } + ] + }, + { + "target": { + "wordIndex": 1, + "letterIndex": 2 + }, + "cursor": { + "wordIndex": 1, + "letterIndex": 2 + }, + "nextIndex": 13, + "words": [ + { + "letters": [ + { + "char": [67], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [117], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [103], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [105], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [105], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [99], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [102], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [44], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [109], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [121], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [102], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [102], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [45], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [98], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [99], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [102], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [102], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [46], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + } + ] + }, + { + "target": { + "wordIndex": 3, + "letterIndex": 0 + }, + "cursor": { + "wordIndex": 3, + "letterIndex": 0 + }, + "nextIndex": 27, + "words": [ + { + "letters": [ + { + "char": [67], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [117], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [103], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [105], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [114], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [105], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [99], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [102], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [44], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [109], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [121], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [102], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [102], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [45], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [98], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [99], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [102], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [102], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [46], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + } + ] + } + ] + } + ], + "practice": [ + { + "missed": "off", + "slow": false, + "result": null, + "notifications": ["Could not start a new custom test"] + }, + { + "missed": "off", + "slow": true, + "result": { + "text": [ + [67, 111, 117, 114, 97, 103, 101], + [67, 111, 117, 114, 97, 103, 101], + [114, 101, 115, 105, 115, 116, 97, 110, 99, 101] + ], + "sectionLimit": 10 + }, + "notifications": [] + }, + { + "missed": "words", + "slow": false, + "result": { + "text": [[67, 111, 117, 114, 97, 103, 101]], + "sectionLimit": 5 + }, + "notifications": [] + }, + { + "missed": "words", + "slow": true, + "result": { + "text": [ + [67, 111, 117, 114, 97, 103, 101], + [67, 111, 117, 114, 97, 103, 101], + [67, 111, 117, 114, 97, 103, 101], + [114, 101, 115, 105, 115, 116, 97, 110, 99, 101] + ], + "sectionLimit": 15 + }, + "notifications": [] + }, + { + "missed": "biwords", + "slow": false, + "result": { + "text": [[67, 111, 117, 114, 97, 103, 101]], + "sectionLimit": 5 + }, + "notifications": [] + }, + { + "missed": "biwords", + "slow": true, + "result": { + "text": [ + [67, 111, 117, 114, 97, 103, 101], + [67, 111, 117, 114, 97, 103, 101], + [67, 111, 117, 114, 97, 103, 101], + [114, 101, 115, 105, 115, 116, 97, 110, 99, 101] + ], + "sectionLimit": 15 + }, + "notifications": [] + } + ], + "validity": [ + { + "input": { + "result": { + "mode": "quote", + "mode2": "36", + "testDuration": 11.54, + "wpm": 71.75, + "rawWpm": 71.75, + "acc": 98.57 + }, + "bailedOut": false, + "failed": false, + "repeated": false, + "lbOptOut": false, + "customLimit": { + "mode": "word", + "value": 10 + } + }, + "reason": null + }, + { + "input": { + "result": { + "mode": "quote", + "mode2": "36", + "testDuration": 11.54, + "wpm": 71.75, + "rawWpm": 71.75, + "acc": 98.57 + }, + "bailedOut": false, + "failed": false, + "repeated": true, + "lbOptOut": false, + "customLimit": { + "mode": "word", + "value": 10 + } + }, + "reason": "repeated" + }, + { + "input": { + "result": { + "mode": "quote", + "mode2": "36", + "testDuration": 11.54, + "wpm": 71.75, + "rawWpm": 71.75, + "acc": 98.57 + }, + "bailedOut": false, + "failed": true, + "repeated": false, + "lbOptOut": false, + "customLimit": { + "mode": "word", + "value": 10 + } + }, + "reason": "failed" + }, + { + "input": { + "result": { + "mode": "quote", + "mode2": "36", + "testDuration": 11.54, + "wpm": 71.75, + "rawWpm": 71.75, + "acc": 98.57 + }, + "bailedOut": false, + "failed": true, + "repeated": true, + "lbOptOut": false, + "customLimit": { + "mode": "word", + "value": 10 + } + }, + "reason": "failed" + }, + { + "input": { + "result": { + "mode": "quote", + "mode2": "36", + "testDuration": 11.54, + "wpm": 71.75, + "rawWpm": 71.75, + "acc": 98.57 + }, + "bailedOut": true, + "failed": false, + "repeated": false, + "lbOptOut": false, + "customLimit": { + "mode": "word", + "value": 10 + } + }, + "reason": null + }, + { + "input": { + "result": { + "mode": "quote", + "mode2": "36", + "testDuration": 11.54, + "wpm": 71.75, + "rawWpm": 71.75, + "acc": 98.57 + }, + "bailedOut": true, + "failed": false, + "repeated": true, + "lbOptOut": false, + "customLimit": { + "mode": "word", + "value": 10 + } + }, + "reason": "repeated" + }, + { + "input": { + "result": { + "mode": "quote", + "mode2": "36", + "testDuration": 11.54, + "wpm": 71.75, + "rawWpm": 71.75, + "acc": 98.57 + }, + "bailedOut": true, + "failed": true, + "repeated": false, + "lbOptOut": false, + "customLimit": { + "mode": "word", + "value": 10 + } + }, + "reason": "failed" + }, + { + "input": { + "result": { + "mode": "quote", + "mode2": "36", + "testDuration": 11.54, + "wpm": 71.75, + "rawWpm": 71.75, + "acc": 98.57 + }, + "bailedOut": true, + "failed": true, + "repeated": true, + "lbOptOut": false, + "customLimit": { + "mode": "word", + "value": 10 + } + }, + "reason": "failed" + } + ] + }, + { + "name": "time-15", + "frames": [ + { + "time": -1, + "inputs": [], + "activeIndex": 0 + }, + { + "time": 0, + "inputs": [[105]], + "activeIndex": 0 + }, + { + "time": 3751.75, + "inputs": [ + [105, 110, 116, 111, 32], + [111, 118, 101, 114, 32], + [104, 97, 118, 101, 32], + [108, 111, 111, 107, 32], + [104] + ], + "activeIndex": 4 + }, + { + "time": 7503.5, + "inputs": [ + [105, 110, 116, 111, 32], + [111, 118, 101, 114, 32], + [104, 97, 118, 101, 32], + [108, 111, 111, 107, 32], + [104, 112, 104, 101, 114, 101, 32], + [110, 111, 116, 32], + [109, 101, 97, 110, 32], + [102, 111, 114, 32] + ], + "activeIndex": 8 + }, + { + "time": 15007, + "inputs": [ + [105, 110, 116, 111, 32], + [111, 118, 101, 114, 32], + [104, 97, 118, 101, 32], + [108, 111, 111, 107, 32], + [104, 112, 104, 101, 114, 101, 32], + [110, 111, 116, 32], + [109, 101, 97, 110, 32], + [102, 111, 114, 32], + [109, 111, 115, 116, 32], + [119, 97, 110, 116, 32], + [108, 101, 97, 100, 32], + [109, 117, 99, 104, 32], + [111, 102, 102, 32], + [116, 104, 105, 110, 107, 32], + [121, 101, 97, 114, 32] + ], + "activeIndex": 15 + } + ], + "replay": [ + { + "zen": false, + "words": [ + [105, 110, 116, 111, 32], + [111, 118, 101, 114, 32], + [104, 97, 118, 101, 32], + [108, 111, 111, 107, 32], + [104, 101, 114, 101, 32], + [110, 111, 116, 32], + [109, 101, 97, 110, 32], + [102, 111, 114, 32], + [109, 111, 115, 116, 32], + [119, 97, 110, 116, 32], + [108, 101, 97, 100, 32], + [109, 117, 99, 104, 32], + [111, 102, 102, 32], + [116, 104, 105, 110, 107, 32], + [121, 101, 97, 114, 32], + [116, 104, 114, 111, 117, 103, 104, 32], + [116, 111, 32], + [111, 102, 102, 32], + [104, 101, 97, 100, 32], + [104, 101, 114, 101, 32], + [97, 108, 115, 111, 32], + [108, 105, 102, 101, 32], + [101, 110, 100, 32], + [109, 97, 107, 101, 32], + [119, 111, 117, 108, 100, 32], + [114, 101, 97, 108, 32], + [102, 105, 114, 115, 116, 32], + [107, 110, 111, 119, 32], + [105, 110, 116, 111, 32], + [103, 105, 118, 101, 32], + [103, 101, 110, 101, 114, 97, 108, 32], + [104, 101, 108, 112, 32], + [111, 119, 110, 32], + [115, 116, 97, 116, 101, 32], + [111, 110, 32], + [102, 111, 108, 108, 111, 119, 32], + [102, 97, 99, 116, 32], + [119, 104, 105, 99, 104, 32], + [100, 101, 118, 101, 108, 111, 112, 32], + [109, 117, 115, 116, 32], + [97, 98, 111, 117, 116, 32], + [107, 101, 101, 112, 32], + [98, 117, 116, 32], + [100, 111, 119, 110, 32], + [97, 103, 97, 105, 110, 115, 116, 32], + [109, 97, 107, 101, 32], + [115, 105, 110, 99, 101, 32], + [116, 105, 109, 101, 32], + [98, 101, 103, 105, 110, 32], + [104, 111, 117, 115, 101, 32], + [97, 102, 116, 101, 114, 32], + [115, 111, 32], + [99, 97, 108, 108, 32], + [99, 104, 105, 108, 100, 32], + [99, 104, 97, 110, 103, 101, 32], + [119, 111, 114, 100, 32], + [119, 104, 101, 114, 101, 32], + [103, 101, 116, 32], + [117, 112, 32], + [100, 97, 121, 32], + [112, 111, 105, 110, 116, 32], + [109, 117, 115, 116, 32], + [104, 101, 108, 112, 32], + [116, 104, 114, 111, 117, 103, 104, 32], + [111, 117, 116, 32], + [104, 101, 32], + [109, 101, 97, 110, 32], + [102, 97, 99, 116, 32], + [112, 117, 98, 108, 105, 99, 32], + [106, 117, 115, 116, 32], + [116, 104, 101, 115, 101, 32], + [100, 117, 114, 105, 110, 103, 32], + [100, 101, 118, 101, 108, 111, 112, 32], + [100, 111, 119, 110, 32], + [97, 115, 107, 32], + [103, 101, 110, 101, 114, 97, 108, 32], + [115, 109, 97, 108, 108, 32], + [103, 114, 101, 97, 116, 32], + [98, 111, 116, 104, 32], + [111, 114, 100, 101, 114, 32], + [100, 111, 32], + [109, 97, 110, 32], + [99, 111, 109, 101, 32], + [105, 110, 99, 114, 101, 97, 115, 101, 32], + [115, 104, 111, 119, 32], + [116, 104, 101, 32], + [100, 111, 119, 110, 32], + [111, 110, 101, 32], + [100, 111, 32], + [99, 104, 97, 110, 103, 101, 32], + [119, 105, 116, 104, 32], + [116, 104, 101, 32], + [114, 105, 103, 104, 116, 32], + [99, 97, 110, 32], + [100, 111, 119, 110, 32], + [112, 114, 101, 115, 101, 110, 116, 32], + [110, 101, 119, 32], + [115, 101, 101, 109, 32], + [117, 110, 100, 101, 114, 32], + [121, 111, 117, 32], + [97, 110, 121, 32], + [108, 101, 97, 118, 101, 32], + [108, 97, 114, 103, 101, 32], + [108, 105, 110, 101, 32], + [117, 110, 100, 101, 114, 32], + [105, 110, 116, 101, 114, 101, 115, 116, 32], + [99, 104, 105, 108, 100, 32], + [107, 110, 111, 119, 32], + [119, 97, 121, 32], + [103, 101, 110, 101, 114, 97, 108, 32], + [115, 101, 116, 32], + [111, 108, 100, 32], + [98, 101, 99, 111, 109, 101, 32], + [105, 110, 116, 101, 114, 101, 115, 116, 32], + [108, 97, 115, 116, 32] + ], + "steps": [ + { + "action": "correctLetter", + "value": [105], + "time": 0 + }, + { + "action": "correctLetter", + "value": [110], + "time": 136.3 + }, + { + "action": "correctLetter", + "value": [116], + "time": 321 + }, + { + "action": "correctLetter", + "value": [111], + "time": 469 + }, + { + "action": "correctLetter", + "value": [32], + "time": 591.1 + }, + { + "action": "submitCorrectWord", + "time": 817.8 + }, + { + "action": "correctLetter", + "value": [111], + "time": 817.8 + }, + { + "action": "correctLetter", + "value": [118], + "time": 994.6 + }, + { + "action": "correctLetter", + "value": [101], + "time": 1178.7 + }, + { + "action": "correctLetter", + "value": [114], + "time": 1307.2 + }, + { + "action": "correctLetter", + "value": [32], + "time": 1428.4 + }, + { + "action": "submitCorrectWord", + "time": 1716.5 + }, + { + "action": "correctLetter", + "value": [104], + "time": 1716.5 + }, + { + "action": "correctLetter", + "value": [97], + "time": 1870.7 + }, + { + "action": "correctLetter", + "value": [118], + "time": 2067.9 + }, + { + "action": "correctLetter", + "value": [101], + "time": 2267.9 + }, + { + "action": "correctLetter", + "value": [32], + "time": 2406.1 + }, + { + "action": "submitCorrectWord", + "time": 2650.1 + }, + { + "action": "correctLetter", + "value": [108], + "time": 2650.1 + }, + { + "action": "correctLetter", + "value": [111], + "time": 2828.5 + }, + { + "action": "correctLetter", + "value": [111], + "time": 2915.7 + }, + { + "action": "correctLetter", + "value": [107], + "time": 3107.8 + }, + { + "action": "correctLetter", + "value": [32], + "time": 3282.8 + }, + { + "action": "submitCorrectWord", + "time": 3580.2 + }, + { + "action": "correctLetter", + "value": [104], + "time": 3580.2 + }, + { + "action": "incorrectLetter", + "value": [112], + "time": 3762.6 + }, + { + "action": "incorrectLetter", + "value": [104], + "time": 3920.4 + }, + { + "action": "correctLetter", + "value": [101], + "time": 4022.6 + }, + { + "action": "incorrectLetter", + "value": [114], + "time": 4179.9 + }, + { + "action": "incorrectLetter", + "value": [101], + "time": 4314.9 + }, + { + "action": "incorrectLetter", + "value": [32], + "time": 4466.8 + }, + { + "action": "submitErrorWord", + "time": 4754.7 + }, + { + "action": "correctLetter", + "value": [110], + "time": 4754.7 + }, + { + "action": "correctLetter", + "value": [111], + "time": 4925.1 + }, + { + "action": "correctLetter", + "value": [116], + "time": 5075.2 + }, + { + "action": "correctLetter", + "value": [32], + "time": 5267 + }, + { + "action": "submitCorrectWord", + "time": 5566.1 + }, + { + "action": "correctLetter", + "value": [109], + "time": 5566.1 + }, + { + "action": "correctLetter", + "value": [101], + "time": 5749.8 + }, + { + "action": "correctLetter", + "value": [97], + "time": 5847.7 + }, + { + "action": "correctLetter", + "value": [110], + "time": 6038.8 + }, + { + "action": "correctLetter", + "value": [32], + "time": 6197.3 + }, + { + "action": "submitCorrectWord", + "time": 6424.4 + }, + { + "action": "correctLetter", + "value": [102], + "time": 6424.4 + }, + { + "action": "correctLetter", + "value": [111], + "time": 6584.8 + }, + { + "action": "correctLetter", + "value": [114], + "time": 6766.2 + }, + { + "action": "correctLetter", + "value": [32], + "time": 6969.3 + }, + { + "action": "submitCorrectWord", + "time": 8871.2 + }, + { + "action": "correctLetter", + "value": [109], + "time": 8871.2 + }, + { + "action": "correctLetter", + "value": [111], + "time": 8969 + }, + { + "action": "correctLetter", + "value": [115], + "time": 9076.8 + }, + { + "action": "correctLetter", + "value": [116], + "time": 9261.5 + }, + { + "action": "correctLetter", + "value": [32], + "time": 9466.8 + }, + { + "action": "submitCorrectWord", + "time": 9839.6 + }, + { + "action": "correctLetter", + "value": [119], + "time": 9839.6 + }, + { + "action": "correctLetter", + "value": [97], + "time": 10033.7 + }, + { + "action": "correctLetter", + "value": [110], + "time": 10223.1 + }, + { + "action": "correctLetter", + "value": [116], + "time": 10387.9 + }, + { + "action": "correctLetter", + "value": [32], + "time": 10524.7 + }, + { + "action": "submitCorrectWord", + "time": 10746.7 + }, + { + "action": "correctLetter", + "value": [108], + "time": 10746.7 + }, + { + "action": "correctLetter", + "value": [101], + "time": 10900.6 + }, + { + "action": "correctLetter", + "value": [97], + "time": 11098.7 + }, + { + "action": "correctLetter", + "value": [100], + "time": 11290.7 + }, + { + "action": "correctLetter", + "value": [32], + "time": 11431.2 + }, + { + "action": "submitCorrectWord", + "time": 11674 + }, + { + "action": "correctLetter", + "value": [109], + "time": 11674 + }, + { + "action": "correctLetter", + "value": [117], + "time": 11778.1 + }, + { + "action": "correctLetter", + "value": [99], + "time": 11982.3 + }, + { + "action": "correctLetter", + "value": [104], + "time": 12096.9 + }, + { + "action": "correctLetter", + "value": [32], + "time": 12206.9 + }, + { + "action": "submitCorrectWord", + "time": 12495.3 + }, + { + "action": "correctLetter", + "value": [111], + "time": 12495.3 + }, + { + "action": "correctLetter", + "value": [102], + "time": 12666.7 + }, + { + "action": "correctLetter", + "value": [102], + "time": 12771.6 + }, + { + "action": "correctLetter", + "value": [32], + "time": 12876.2 + }, + { + "action": "submitCorrectWord", + "time": 13164.6 + }, + { + "action": "correctLetter", + "value": [116], + "time": 13164.6 + }, + { + "action": "correctLetter", + "value": [104], + "time": 13341.5 + }, + { + "action": "correctLetter", + "value": [105], + "time": 13545.5 + }, + { + "action": "correctLetter", + "value": [110], + "time": 13750.9 + }, + { + "action": "correctLetter", + "value": [107], + "time": 13886.8 + }, + { + "action": "correctLetter", + "value": [32], + "time": 14028.2 + }, + { + "action": "submitCorrectWord", + "time": 14264 + }, + { + "action": "correctLetter", + "value": [121], + "time": 14264 + }, + { + "action": "correctLetter", + "value": [101], + "time": 14354.4 + }, + { + "action": "correctLetter", + "value": [97], + "time": 14538.8 + }, + { + "action": "correctLetter", + "value": [114], + "time": 14682.2 + }, + { + "action": "correctLetter", + "value": [32], + "time": 14891.1 + } + ], + "final": [ + { + "letters": [ + { + "char": [105], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [118], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [104], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [118], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [108], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [107], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [104], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": true, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": true, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": true, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": true, + "extra": true + }, + { + "char": [32], + "correct": false, + "incorrect": true, + "extra": true + } + ], + "error": true + }, + { + "letters": [ + { + "char": [110], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [109], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [102], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [109], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [119], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [108], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [100], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [109], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [117], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [99], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [104], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [102], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [102], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [116], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [104], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [105], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [107], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [121], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + } + ], + "cursor": { + "wordIndex": 14, + "letterIndex": 5 + }, + "sounds": [ + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "error", + "error", + "click", + "error", + "error", + "error", + "error", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click" + ], + "seeks": [ + { + "target": { + "wordIndex": 0, + "letterIndex": 0 + }, + "cursor": { + "wordIndex": 0, + "letterIndex": 0 + }, + "nextIndex": 0, + "words": [ + { + "letters": [ + { + "char": [105], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [118], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [104], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [118], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [108], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [107], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [104], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [109], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [102], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [109], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [119], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [108], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [100], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [109], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [117], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [99], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [104], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [102], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [102], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [104], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [105], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [107], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [121], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + } + ] + }, + { + "target": { + "wordIndex": 1, + "letterIndex": 2 + }, + "cursor": { + "wordIndex": 1, + "letterIndex": 2 + }, + "nextIndex": 8, + "words": [ + { + "letters": [ + { + "char": [105], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [118], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [104], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [118], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [108], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [107], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [104], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [109], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [102], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [109], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [119], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [108], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [100], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [109], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [117], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [99], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [104], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [102], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [102], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [104], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [105], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [107], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [121], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + } + ] + }, + { + "target": { + "wordIndex": 3, + "letterIndex": 0 + }, + "cursor": { + "wordIndex": 3, + "letterIndex": 0 + }, + "nextIndex": 18, + "words": [ + { + "letters": [ + { + "char": [105], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [118], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [104], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [118], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [108], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [107], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [104], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [109], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [102], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [109], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [119], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [108], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [100], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [109], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [117], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [99], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [104], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [102], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [102], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [104], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [105], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [107], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [121], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + } + ] + } + ] + }, + { + "zen": true, + "words": [ + [105, 110, 116, 111, 32], + [111, 118, 101, 114, 32], + [104, 97, 118, 101, 32], + [108, 111, 111, 107, 32], + [104, 112, 104, 101, 114, 101, 32], + [110, 111, 116, 32], + [109, 101, 97, 110, 32], + [102, 111, 114, 32], + [109, 111, 115, 116, 32], + [119, 97, 110, 116, 32], + [108, 101, 97, 100, 32], + [109, 117, 99, 104, 32], + [111, 102, 102, 32], + [116, 104, 105, 110, 107, 32], + [121, 101, 97, 114, 32] + ], + "steps": [ + { + "action": "correctLetter", + "value": [105], + "time": 0 + }, + { + "action": "correctLetter", + "value": [110], + "time": 136.3 + }, + { + "action": "correctLetter", + "value": [116], + "time": 321 + }, + { + "action": "correctLetter", + "value": [111], + "time": 469 + }, + { + "action": "correctLetter", + "value": [32], + "time": 591.1 + }, + { + "action": "submitCorrectWord", + "time": 817.8 + }, + { + "action": "correctLetter", + "value": [111], + "time": 817.8 + }, + { + "action": "correctLetter", + "value": [118], + "time": 994.6 + }, + { + "action": "correctLetter", + "value": [101], + "time": 1178.7 + }, + { + "action": "correctLetter", + "value": [114], + "time": 1307.2 + }, + { + "action": "correctLetter", + "value": [32], + "time": 1428.4 + }, + { + "action": "submitCorrectWord", + "time": 1716.5 + }, + { + "action": "correctLetter", + "value": [104], + "time": 1716.5 + }, + { + "action": "correctLetter", + "value": [97], + "time": 1870.7 + }, + { + "action": "correctLetter", + "value": [118], + "time": 2067.9 + }, + { + "action": "correctLetter", + "value": [101], + "time": 2267.9 + }, + { + "action": "correctLetter", + "value": [32], + "time": 2406.1 + }, + { + "action": "submitCorrectWord", + "time": 2650.1 + }, + { + "action": "correctLetter", + "value": [108], + "time": 2650.1 + }, + { + "action": "correctLetter", + "value": [111], + "time": 2828.5 + }, + { + "action": "correctLetter", + "value": [111], + "time": 2915.7 + }, + { + "action": "correctLetter", + "value": [107], + "time": 3107.8 + }, + { + "action": "correctLetter", + "value": [32], + "time": 3282.8 + }, + { + "action": "submitCorrectWord", + "time": 3580.2 + }, + { + "action": "correctLetter", + "value": [104], + "time": 3580.2 + }, + { + "action": "incorrectLetter", + "value": [112], + "time": 3762.6 + }, + { + "action": "incorrectLetter", + "value": [104], + "time": 3920.4 + }, + { + "action": "correctLetter", + "value": [101], + "time": 4022.6 + }, + { + "action": "incorrectLetter", + "value": [114], + "time": 4179.9 + }, + { + "action": "incorrectLetter", + "value": [101], + "time": 4314.9 + }, + { + "action": "incorrectLetter", + "value": [32], + "time": 4466.8 + }, + { + "action": "submitCorrectWord", + "time": 4754.7 + }, + { + "action": "correctLetter", + "value": [110], + "time": 4754.7 + }, + { + "action": "correctLetter", + "value": [111], + "time": 4925.1 + }, + { + "action": "correctLetter", + "value": [116], + "time": 5075.2 + }, + { + "action": "correctLetter", + "value": [32], + "time": 5267 + }, + { + "action": "submitCorrectWord", + "time": 5566.1 + }, + { + "action": "correctLetter", + "value": [109], + "time": 5566.1 + }, + { + "action": "correctLetter", + "value": [101], + "time": 5749.8 + }, + { + "action": "correctLetter", + "value": [97], + "time": 5847.7 + }, + { + "action": "correctLetter", + "value": [110], + "time": 6038.8 + }, + { + "action": "correctLetter", + "value": [32], + "time": 6197.3 + }, + { + "action": "submitCorrectWord", + "time": 6424.4 + }, + { + "action": "correctLetter", + "value": [102], + "time": 6424.4 + }, + { + "action": "correctLetter", + "value": [111], + "time": 6584.8 + }, + { + "action": "correctLetter", + "value": [114], + "time": 6766.2 + }, + { + "action": "correctLetter", + "value": [32], + "time": 6969.3 + }, + { + "action": "submitCorrectWord", + "time": 8871.2 + }, + { + "action": "correctLetter", + "value": [109], + "time": 8871.2 + }, + { + "action": "correctLetter", + "value": [111], + "time": 8969 + }, + { + "action": "correctLetter", + "value": [115], + "time": 9076.8 + }, + { + "action": "correctLetter", + "value": [116], + "time": 9261.5 + }, + { + "action": "correctLetter", + "value": [32], + "time": 9466.8 + }, + { + "action": "submitCorrectWord", + "time": 9839.6 + }, + { + "action": "correctLetter", + "value": [119], + "time": 9839.6 + }, + { + "action": "correctLetter", + "value": [97], + "time": 10033.7 + }, + { + "action": "correctLetter", + "value": [110], + "time": 10223.1 + }, + { + "action": "correctLetter", + "value": [116], + "time": 10387.9 + }, + { + "action": "correctLetter", + "value": [32], + "time": 10524.7 + }, + { + "action": "submitCorrectWord", + "time": 10746.7 + }, + { + "action": "correctLetter", + "value": [108], + "time": 10746.7 + }, + { + "action": "correctLetter", + "value": [101], + "time": 10900.6 + }, + { + "action": "correctLetter", + "value": [97], + "time": 11098.7 + }, + { + "action": "correctLetter", + "value": [100], + "time": 11290.7 + }, + { + "action": "correctLetter", + "value": [32], + "time": 11431.2 + }, + { + "action": "submitCorrectWord", + "time": 11674 + }, + { + "action": "correctLetter", + "value": [109], + "time": 11674 + }, + { + "action": "correctLetter", + "value": [117], + "time": 11778.1 + }, + { + "action": "correctLetter", + "value": [99], + "time": 11982.3 + }, + { + "action": "correctLetter", + "value": [104], + "time": 12096.9 + }, + { + "action": "correctLetter", + "value": [32], + "time": 12206.9 + }, + { + "action": "submitCorrectWord", + "time": 12495.3 + }, + { + "action": "correctLetter", + "value": [111], + "time": 12495.3 + }, + { + "action": "correctLetter", + "value": [102], + "time": 12666.7 + }, + { + "action": "correctLetter", + "value": [102], + "time": 12771.6 + }, + { + "action": "correctLetter", + "value": [32], + "time": 12876.2 + }, + { + "action": "submitCorrectWord", + "time": 13164.6 + }, + { + "action": "correctLetter", + "value": [116], + "time": 13164.6 + }, + { + "action": "correctLetter", + "value": [104], + "time": 13341.5 + }, + { + "action": "correctLetter", + "value": [105], + "time": 13545.5 + }, + { + "action": "correctLetter", + "value": [110], + "time": 13750.9 + }, + { + "action": "correctLetter", + "value": [107], + "time": 13886.8 + }, + { + "action": "correctLetter", + "value": [32], + "time": 14028.2 + }, + { + "action": "submitCorrectWord", + "time": 14264 + }, + { + "action": "correctLetter", + "value": [121], + "time": 14264 + }, + { + "action": "correctLetter", + "value": [101], + "time": 14354.4 + }, + { + "action": "correctLetter", + "value": [97], + "time": 14538.8 + }, + { + "action": "correctLetter", + "value": [114], + "time": 14682.2 + }, + { + "action": "correctLetter", + "value": [32], + "time": 14891.1 + } + ], + "final": [ + { + "letters": [ + { + "char": [105], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [118], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [104], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [118], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [108], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [107], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [104], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [112], + "correct": false, + "incorrect": true, + "extra": false + }, + { + "char": [104], + "correct": false, + "incorrect": true, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": true, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": true, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": true, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [110], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [109], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [102], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [109], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [119], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [108], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [100], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [109], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [117], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [99], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [104], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [102], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [102], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [116], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [104], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [105], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [107], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [121], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + } + ], + "cursor": { + "wordIndex": 14, + "letterIndex": 5 + }, + "sounds": [ + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "error", + "error", + "click", + "error", + "error", + "error", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click" + ], + "seeks": [ + { + "target": { + "wordIndex": 0, + "letterIndex": 0 + }, + "cursor": { + "wordIndex": 0, + "letterIndex": 0 + }, + "nextIndex": 0, + "words": [ + { + "letters": [ + { + "char": [105], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [118], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [104], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [118], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [108], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [107], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [104], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [112], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [104], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [109], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [102], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [109], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [119], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [108], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [100], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [109], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [117], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [99], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [104], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [102], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [102], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [104], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [105], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [107], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [121], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + } + ] + }, + { + "target": { + "wordIndex": 1, + "letterIndex": 2 + }, + "cursor": { + "wordIndex": 1, + "letterIndex": 2 + }, + "nextIndex": 8, + "words": [ + { + "letters": [ + { + "char": [105], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [118], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [104], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [118], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [108], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [107], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [104], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [112], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [104], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [109], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [102], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [109], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [119], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [108], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [100], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [109], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [117], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [99], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [104], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [102], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [102], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [104], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [105], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [107], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [121], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + } + ] + }, + { + "target": { + "wordIndex": 3, + "letterIndex": 0 + }, + "cursor": { + "wordIndex": 3, + "letterIndex": 0 + }, + "nextIndex": 18, + "words": [ + { + "letters": [ + { + "char": [105], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [118], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [104], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [118], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [108], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [107], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [104], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [112], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [104], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [109], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [102], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [109], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [119], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [108], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [100], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [109], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [117], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [99], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [104], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [102], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [102], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [104], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [105], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [107], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [121], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + } + ] + } + ] + } + ], + "practice": [ + { + "missed": "off", + "slow": false, + "result": null, + "notifications": ["Could not start a new custom test"] + }, + { + "missed": "off", + "slow": true, + "result": { + "text": [ + [116, 104, 105, 110, 107], + [116, 104, 105, 110, 107], + [116, 104, 105, 110, 107], + [104, 97, 118, 101], + [104, 97, 118, 101], + [102, 111, 114] + ], + "sectionLimit": 15 + }, + "notifications": [] + }, + { + "missed": "words", + "slow": false, + "result": { + "text": [ + [104, 101, 114, 101], + [104, 101, 114, 101], + [104, 101, 114, 101], + [104, 101, 114, 101], + [104, 101, 114, 101] + ], + "sectionLimit": 5 + }, + "notifications": [] + }, + { + "missed": "words", + "slow": true, + "result": { + "text": [ + [104, 101, 114, 101], + [104, 101, 114, 101], + [104, 101, 114, 101], + [104, 101, 114, 101], + [104, 101, 114, 101], + [116, 104, 105, 110, 107], + [116, 104, 105, 110, 107], + [116, 104, 105, 110, 107], + [104, 97, 118, 101], + [104, 97, 118, 101], + [102, 111, 114] + ], + "sectionLimit": 20 + }, + "notifications": [] + }, + { + "missed": "biwords", + "slow": false, + "result": { + "text": [ + [108, 111, 111, 107, 32, 104, 101, 114, 101], + [108, 111, 111, 107, 32, 104, 101, 114, 101], + [108, 111, 111, 107, 32, 104, 101, 114, 101], + [108, 111, 111, 107, 32, 104, 101, 114, 101], + [108, 111, 111, 107, 32, 104, 101, 114, 101], + [104, 101, 97, 100, 32, 104, 101, 114, 101], + [104, 101, 97, 100, 32, 104, 101, 114, 101], + [104, 101, 97, 100, 32, 104, 101, 114, 101], + [104, 101, 97, 100, 32, 104, 101, 114, 101], + [104, 101, 97, 100, 32, 104, 101, 114, 101] + ], + "sectionLimit": 10 + }, + "notifications": [] + }, + { + "missed": "biwords", + "slow": true, + "result": { + "text": [ + [108, 111, 111, 107, 32, 104, 101, 114, 101], + [108, 111, 111, 107, 32, 104, 101, 114, 101], + [108, 111, 111, 107, 32, 104, 101, 114, 101], + [108, 111, 111, 107, 32, 104, 101, 114, 101], + [108, 111, 111, 107, 32, 104, 101, 114, 101], + [104, 101, 97, 100, 32, 104, 101, 114, 101], + [104, 101, 97, 100, 32, 104, 101, 114, 101], + [104, 101, 97, 100, 32, 104, 101, 114, 101], + [104, 101, 97, 100, 32, 104, 101, 114, 101], + [104, 101, 97, 100, 32, 104, 101, 114, 101], + [116, 104, 105, 110, 107], + [116, 104, 105, 110, 107], + [116, 104, 105, 110, 107], + [104, 97, 118, 101], + [104, 97, 118, 101], + [102, 111, 114] + ], + "sectionLimit": 25 + }, + "notifications": [] + } + ], + "validity": [ + { + "input": { + "result": { + "mode": "time", + "mode2": "15", + "testDuration": 15.01, + "wpm": 54.36, + "rawWpm": 59.96, + "acc": 93.33 + }, + "bailedOut": false, + "failed": false, + "repeated": false, + "lbOptOut": false, + "customLimit": { + "mode": "word", + "value": 10 + } + }, + "reason": null + }, + { + "input": { + "result": { + "mode": "time", + "mode2": "15", + "testDuration": 15.01, + "wpm": 54.36, + "rawWpm": 59.96, + "acc": 93.33 + }, + "bailedOut": false, + "failed": false, + "repeated": true, + "lbOptOut": false, + "customLimit": { + "mode": "word", + "value": 10 + } + }, + "reason": "repeated" + }, + { + "input": { + "result": { + "mode": "time", + "mode2": "15", + "testDuration": 15.01, + "wpm": 54.36, + "rawWpm": 59.96, + "acc": 93.33 + }, + "bailedOut": false, + "failed": true, + "repeated": false, + "lbOptOut": false, + "customLimit": { + "mode": "word", + "value": 10 + } + }, + "reason": "failed" + }, + { + "input": { + "result": { + "mode": "time", + "mode2": "15", + "testDuration": 15.01, + "wpm": 54.36, + "rawWpm": 59.96, + "acc": 93.33 + }, + "bailedOut": false, + "failed": true, + "repeated": true, + "lbOptOut": false, + "customLimit": { + "mode": "word", + "value": 10 + } + }, + "reason": "failed" + }, + { + "input": { + "result": { + "mode": "time", + "mode2": "15", + "testDuration": 15.01, + "wpm": 54.36, + "rawWpm": 59.96, + "acc": 93.33 + }, + "bailedOut": true, + "failed": false, + "repeated": false, + "lbOptOut": false, + "customLimit": { + "mode": "word", + "value": 10 + } + }, + "reason": null + }, + { + "input": { + "result": { + "mode": "time", + "mode2": "15", + "testDuration": 15.01, + "wpm": 54.36, + "rawWpm": 59.96, + "acc": 93.33 + }, + "bailedOut": true, + "failed": false, + "repeated": true, + "lbOptOut": false, + "customLimit": { + "mode": "word", + "value": 10 + } + }, + "reason": "repeated" + }, + { + "input": { + "result": { + "mode": "time", + "mode2": "15", + "testDuration": 15.01, + "wpm": 54.36, + "rawWpm": 59.96, + "acc": 93.33 + }, + "bailedOut": true, + "failed": true, + "repeated": false, + "lbOptOut": false, + "customLimit": { + "mode": "word", + "value": 10 + } + }, + "reason": "failed" + }, + { + "input": { + "result": { + "mode": "time", + "mode2": "15", + "testDuration": 15.01, + "wpm": 54.36, + "rawWpm": 59.96, + "acc": 93.33 + }, + "bailedOut": true, + "failed": true, + "repeated": true, + "lbOptOut": false, + "customLimit": { + "mode": "word", + "value": 10 + } + }, + "reason": "failed" + } + ] + }, + { + "name": "words-10-clean", + "frames": [ + { + "time": -1, + "inputs": [], + "activeIndex": 0 + }, + { + "time": 0, + "inputs": [[111]], + "activeIndex": 0 + }, + { + "time": 1199.9575, + "inputs": [ + [111, 102, 32], + [98, 121, 32], + [99, 111, 109, 101, 32], + [97] + ], + "activeIndex": 3 + }, + { + "time": 2399.915, + "inputs": [ + [111, 102, 32], + [98, 121, 32], + [99, 111, 109, 101, 32], + [97, 115, 32], + [99, 97, 110, 32], + [110, 101, 118, 101, 114] + ], + "activeIndex": 5 + }, + { + "time": 4799.83, + "inputs": [ + [111, 102, 32], + [98, 121, 32], + [99, 111, 109, 101, 32], + [97, 115, 32], + [99, 97, 110, 32], + [110, 101, 118, 101, 114, 32], + [116, 104, 101, 121, 32], + [98, 121, 32], + [115, 121, 115, 116, 101, 109, 32], + [109, 111, 115, 116] + ], + "activeIndex": 9 + } + ], + "replay": [ + { + "zen": false, + "words": [ + [111, 102, 32], + [98, 121, 32], + [99, 111, 109, 101, 32], + [97, 115, 32], + [99, 97, 110, 32], + [110, 101, 118, 101, 114, 32], + [116, 104, 101, 121, 32], + [98, 121, 32], + [115, 121, 115, 116, 101, 109, 32], + [109, 111, 115, 116] + ], + "steps": [ + { + "action": "correctLetter", + "value": [111], + "time": 0 + }, + { + "action": "correctLetter", + "value": [102], + "time": 88.7 + }, + { + "action": "correctLetter", + "value": [32], + "time": 202.9 + }, + { + "action": "submitCorrectWord", + "time": 375.1 + }, + { + "action": "correctLetter", + "value": [98], + "time": 375.1 + }, + { + "action": "correctLetter", + "value": [121], + "time": 462.4 + }, + { + "action": "correctLetter", + "value": [32], + "time": 550.1 + }, + { + "action": "submitCorrectWord", + "time": 715.6 + }, + { + "action": "correctLetter", + "value": [99], + "time": 715.6 + }, + { + "action": "correctLetter", + "value": [111], + "time": 791.5 + }, + { + "action": "correctLetter", + "value": [109], + "time": 880.3 + }, + { + "action": "correctLetter", + "value": [101], + "time": 965 + }, + { + "action": "correctLetter", + "value": [32], + "time": 1043.1 + }, + { + "action": "submitCorrectWord", + "time": 1174.7 + }, + { + "action": "correctLetter", + "value": [97], + "time": 1174.7 + }, + { + "action": "correctLetter", + "value": [115], + "time": 1267.7 + }, + { + "action": "correctLetter", + "value": [32], + "time": 1348.1 + }, + { + "action": "submitCorrectWord", + "time": 1518.8 + }, + { + "action": "correctLetter", + "value": [99], + "time": 1518.8 + }, + { + "action": "correctLetter", + "value": [97], + "time": 1620.4 + }, + { + "action": "correctLetter", + "value": [110], + "time": 1726.1 + }, + { + "action": "correctLetter", + "value": [32], + "time": 1831.1 + }, + { + "action": "submitCorrectWord", + "time": 2069.7 + }, + { + "action": "correctLetter", + "value": [110], + "time": 2069.7 + }, + { + "action": "correctLetter", + "value": [101], + "time": 2136.8 + }, + { + "action": "correctLetter", + "value": [118], + "time": 2218.8 + }, + { + "action": "correctLetter", + "value": [101], + "time": 2291.5 + }, + { + "action": "correctLetter", + "value": [114], + "time": 2385.3 + }, + { + "action": "correctLetter", + "value": [32], + "time": 2460.6 + }, + { + "action": "submitCorrectWord", + "time": 2615.7 + }, + { + "action": "correctLetter", + "value": [116], + "time": 2615.7 + }, + { + "action": "correctLetter", + "value": [104], + "time": 2688.5 + }, + { + "action": "correctLetter", + "value": [101], + "time": 2788.4 + }, + { + "action": "correctLetter", + "value": [121], + "time": 2866.9 + }, + { + "action": "correctLetter", + "value": [32], + "time": 2982.4 + }, + { + "action": "submitCorrectWord", + "time": 3243 + }, + { + "action": "correctLetter", + "value": [98], + "time": 3243 + }, + { + "action": "correctLetter", + "value": [121], + "time": 3353.3 + }, + { + "action": "correctLetter", + "value": [32], + "time": 3420.2 + }, + { + "action": "submitCorrectWord", + "time": 3636.7 + }, + { + "action": "correctLetter", + "value": [115], + "time": 3636.7 + }, + { + "action": "correctLetter", + "value": [121], + "time": 3750.4 + }, + { + "action": "correctLetter", + "value": [115], + "time": 3867.5 + }, + { + "action": "correctLetter", + "value": [116], + "time": 3933.8 + }, + { + "action": "correctLetter", + "value": [101], + "time": 4051.9 + }, + { + "action": "correctLetter", + "value": [109], + "time": 4172.3 + }, + { + "action": "correctLetter", + "value": [32], + "time": 4254.8 + }, + { + "action": "submitCorrectWord", + "time": 4399.8 + }, + { + "action": "correctLetter", + "value": [109], + "time": 4399.8 + }, + { + "action": "correctLetter", + "value": [111], + "time": 4498.5 + }, + { + "action": "correctLetter", + "value": [115], + "time": 4590.5 + }, + { + "action": "correctLetter", + "value": [116], + "time": 4697.1 + } + ], + "final": [ + { + "letters": [ + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [102], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [98], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [121], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [99], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [109], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [97], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [99], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [110], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [118], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [116], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [104], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [121], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [98], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [121], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [115], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [121], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [109], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [109], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + } + ], + "cursor": { + "wordIndex": 9, + "letterIndex": 4 + }, + "sounds": [ + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click" + ], + "seeks": [ + { + "target": { + "wordIndex": 0, + "letterIndex": 0 + }, + "cursor": { + "wordIndex": 0, + "letterIndex": 0 + }, + "nextIndex": 0, + "words": [ + { + "letters": [ + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [102], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [98], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [121], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [99], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [109], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [99], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [118], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [104], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [121], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [98], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [121], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [121], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [109], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [109], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + } + ] + }, + { + "target": { + "wordIndex": 1, + "letterIndex": 2 + }, + "cursor": { + "wordIndex": 1, + "letterIndex": 2 + }, + "nextIndex": 6, + "words": [ + { + "letters": [ + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [102], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [98], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [121], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [99], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [109], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [99], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [118], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [104], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [121], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [98], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [121], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [121], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [109], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [109], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + } + ] + }, + { + "target": { + "wordIndex": 3, + "letterIndex": 0 + }, + "cursor": { + "wordIndex": 3, + "letterIndex": 0 + }, + "nextIndex": 14, + "words": [ + { + "letters": [ + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [102], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [98], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [121], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [99], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [109], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [99], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [118], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [104], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [121], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [98], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [121], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [121], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [109], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [109], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + } + ] + } + ] + }, + { + "zen": true, + "words": [ + [111, 102, 32], + [98, 121, 32], + [99, 111, 109, 101, 32], + [97, 115, 32], + [99, 97, 110, 32], + [110, 101, 118, 101, 114, 32], + [116, 104, 101, 121, 32], + [98, 121, 32], + [115, 121, 115, 116, 101, 109, 32], + [109, 111, 115, 116] + ], + "steps": [ + { + "action": "correctLetter", + "value": [111], + "time": 0 + }, + { + "action": "correctLetter", + "value": [102], + "time": 88.7 + }, + { + "action": "correctLetter", + "value": [32], + "time": 202.9 + }, + { + "action": "submitCorrectWord", + "time": 375.1 + }, + { + "action": "correctLetter", + "value": [98], + "time": 375.1 + }, + { + "action": "correctLetter", + "value": [121], + "time": 462.4 + }, + { + "action": "correctLetter", + "value": [32], + "time": 550.1 + }, + { + "action": "submitCorrectWord", + "time": 715.6 + }, + { + "action": "correctLetter", + "value": [99], + "time": 715.6 + }, + { + "action": "correctLetter", + "value": [111], + "time": 791.5 + }, + { + "action": "correctLetter", + "value": [109], + "time": 880.3 + }, + { + "action": "correctLetter", + "value": [101], + "time": 965 + }, + { + "action": "correctLetter", + "value": [32], + "time": 1043.1 + }, + { + "action": "submitCorrectWord", + "time": 1174.7 + }, + { + "action": "correctLetter", + "value": [97], + "time": 1174.7 + }, + { + "action": "correctLetter", + "value": [115], + "time": 1267.7 + }, + { + "action": "correctLetter", + "value": [32], + "time": 1348.1 + }, + { + "action": "submitCorrectWord", + "time": 1518.8 + }, + { + "action": "correctLetter", + "value": [99], + "time": 1518.8 + }, + { + "action": "correctLetter", + "value": [97], + "time": 1620.4 + }, + { + "action": "correctLetter", + "value": [110], + "time": 1726.1 + }, + { + "action": "correctLetter", + "value": [32], + "time": 1831.1 + }, + { + "action": "submitCorrectWord", + "time": 2069.7 + }, + { + "action": "correctLetter", + "value": [110], + "time": 2069.7 + }, + { + "action": "correctLetter", + "value": [101], + "time": 2136.8 + }, + { + "action": "correctLetter", + "value": [118], + "time": 2218.8 + }, + { + "action": "correctLetter", + "value": [101], + "time": 2291.5 + }, + { + "action": "correctLetter", + "value": [114], + "time": 2385.3 + }, + { + "action": "correctLetter", + "value": [32], + "time": 2460.6 + }, + { + "action": "submitCorrectWord", + "time": 2615.7 + }, + { + "action": "correctLetter", + "value": [116], + "time": 2615.7 + }, + { + "action": "correctLetter", + "value": [104], + "time": 2688.5 + }, + { + "action": "correctLetter", + "value": [101], + "time": 2788.4 + }, + { + "action": "correctLetter", + "value": [121], + "time": 2866.9 + }, + { + "action": "correctLetter", + "value": [32], + "time": 2982.4 + }, + { + "action": "submitCorrectWord", + "time": 3243 + }, + { + "action": "correctLetter", + "value": [98], + "time": 3243 + }, + { + "action": "correctLetter", + "value": [121], + "time": 3353.3 + }, + { + "action": "correctLetter", + "value": [32], + "time": 3420.2 + }, + { + "action": "submitCorrectWord", + "time": 3636.7 + }, + { + "action": "correctLetter", + "value": [115], + "time": 3636.7 + }, + { + "action": "correctLetter", + "value": [121], + "time": 3750.4 + }, + { + "action": "correctLetter", + "value": [115], + "time": 3867.5 + }, + { + "action": "correctLetter", + "value": [116], + "time": 3933.8 + }, + { + "action": "correctLetter", + "value": [101], + "time": 4051.9 + }, + { + "action": "correctLetter", + "value": [109], + "time": 4172.3 + }, + { + "action": "correctLetter", + "value": [32], + "time": 4254.8 + }, + { + "action": "submitCorrectWord", + "time": 4399.8 + }, + { + "action": "correctLetter", + "value": [109], + "time": 4399.8 + }, + { + "action": "correctLetter", + "value": [111], + "time": 4498.5 + }, + { + "action": "correctLetter", + "value": [115], + "time": 4590.5 + }, + { + "action": "correctLetter", + "value": [116], + "time": 4697.1 + } + ], + "final": [ + { + "letters": [ + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [102], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [98], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [121], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [99], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [109], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [97], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [99], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [110], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [118], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [116], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [104], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [121], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [98], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [121], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [115], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [121], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [109], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [109], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + } + ], + "cursor": { + "wordIndex": 9, + "letterIndex": 4 + }, + "sounds": [ + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click" + ], + "seeks": [ + { + "target": { + "wordIndex": 0, + "letterIndex": 0 + }, + "cursor": { + "wordIndex": 0, + "letterIndex": 0 + }, + "nextIndex": 0, + "words": [ + { + "letters": [ + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [102], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [98], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [121], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [99], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [109], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [99], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [118], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [104], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [121], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [98], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [121], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [121], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [109], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [109], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + } + ] + }, + { + "target": { + "wordIndex": 1, + "letterIndex": 2 + }, + "cursor": { + "wordIndex": 1, + "letterIndex": 2 + }, + "nextIndex": 6, + "words": [ + { + "letters": [ + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [102], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [98], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [121], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [99], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [109], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [99], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [118], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [104], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [121], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [98], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [121], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [121], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [109], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [109], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + } + ] + }, + { + "target": { + "wordIndex": 3, + "letterIndex": 0 + }, + "cursor": { + "wordIndex": 3, + "letterIndex": 0 + }, + "nextIndex": 14, + "words": [ + { + "letters": [ + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [102], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [98], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [121], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [99], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [109], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [99], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [118], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [104], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [121], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [98], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [121], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [121], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [109], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [109], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + } + ] + } + ] + } + ], + "practice": [ + { + "missed": "off", + "slow": false, + "result": null, + "notifications": ["Could not start a new custom test"] + }, + { + "missed": "off", + "slow": true, + "result": { + "text": [ + [115, 121, 115, 116, 101, 109], + [115, 121, 115, 116, 101, 109], + [99, 97, 110] + ], + "sectionLimit": 10 + }, + "notifications": [] + }, + { + "missed": "words", + "slow": false, + "result": null, + "notifications": ["You haven't missed any words"] + }, + { + "missed": "words", + "slow": true, + "result": { + "text": [ + [115, 121, 115, 116, 101, 109], + [115, 121, 115, 116, 101, 109], + [99, 97, 110] + ], + "sectionLimit": 10 + }, + "notifications": [] + }, + { + "missed": "biwords", + "slow": false, + "result": null, + "notifications": ["You haven't missed any words"] + }, + { + "missed": "biwords", + "slow": true, + "result": { + "text": [ + [115, 121, 115, 116, 101, 109], + [115, 121, 115, 116, 101, 109], + [99, 97, 110] + ], + "sectionLimit": 10 + }, + "notifications": [] + } + ], + "validity": [ + { + "input": { + "result": { + "mode": "words", + "mode2": "10", + "testDuration": 4.7, + "wpm": 109.79, + "rawWpm": 109.79, + "acc": 100 + }, + "bailedOut": false, + "failed": false, + "repeated": false, + "lbOptOut": false, + "customLimit": { + "mode": "word", + "value": 10 + } + }, + "reason": null + }, + { + "input": { + "result": { + "mode": "words", + "mode2": "10", + "testDuration": 4.7, + "wpm": 109.79, + "rawWpm": 109.79, + "acc": 100 + }, + "bailedOut": false, + "failed": false, + "repeated": true, + "lbOptOut": false, + "customLimit": { + "mode": "word", + "value": 10 + } + }, + "reason": "repeated" + }, + { + "input": { + "result": { + "mode": "words", + "mode2": "10", + "testDuration": 4.7, + "wpm": 109.79, + "rawWpm": 109.79, + "acc": 100 + }, + "bailedOut": false, + "failed": true, + "repeated": false, + "lbOptOut": false, + "customLimit": { + "mode": "word", + "value": 10 + } + }, + "reason": "failed" + }, + { + "input": { + "result": { + "mode": "words", + "mode2": "10", + "testDuration": 4.7, + "wpm": 109.79, + "rawWpm": 109.79, + "acc": 100 + }, + "bailedOut": false, + "failed": true, + "repeated": true, + "lbOptOut": false, + "customLimit": { + "mode": "word", + "value": 10 + } + }, + "reason": "failed" + }, + { + "input": { + "result": { + "mode": "words", + "mode2": "10", + "testDuration": 4.7, + "wpm": 109.79, + "rawWpm": 109.79, + "acc": 100 + }, + "bailedOut": true, + "failed": false, + "repeated": false, + "lbOptOut": false, + "customLimit": { + "mode": "word", + "value": 10 + } + }, + "reason": null + }, + { + "input": { + "result": { + "mode": "words", + "mode2": "10", + "testDuration": 4.7, + "wpm": 109.79, + "rawWpm": 109.79, + "acc": 100 + }, + "bailedOut": true, + "failed": false, + "repeated": true, + "lbOptOut": false, + "customLimit": { + "mode": "word", + "value": 10 + } + }, + "reason": "repeated" + }, + { + "input": { + "result": { + "mode": "words", + "mode2": "10", + "testDuration": 4.7, + "wpm": 109.79, + "rawWpm": 109.79, + "acc": 100 + }, + "bailedOut": true, + "failed": true, + "repeated": false, + "lbOptOut": false, + "customLimit": { + "mode": "word", + "value": 10 + } + }, + "reason": "failed" + }, + { + "input": { + "result": { + "mode": "words", + "mode2": "10", + "testDuration": 4.7, + "wpm": 109.79, + "rawWpm": 109.79, + "acc": 100 + }, + "bailedOut": true, + "failed": true, + "repeated": true, + "lbOptOut": false, + "customLimit": { + "mode": "word", + "value": 10 + } + }, + "reason": "failed" + } + ] + }, + { + "name": "words-10-errors", + "frames": [ + { + "time": -1, + "inputs": [], + "activeIndex": 0 + }, + { + "time": 0, + "inputs": [[97]], + "activeIndex": 0 + }, + { + "time": 2572.2, + "inputs": [ + [97, 108, 108, 32], + [109, 97, 110, 32], + [119, 111, 117, 108, 100, 32] + ], + "activeIndex": 3 + }, + { + "time": 5144.4, + "inputs": [ + [97, 108, 108, 32], + [109, 97, 110, 32], + [119, 111, 117, 108, 100, 32], + [115, 99, 104, 111, 111, 108, 120, 120, 32], + [119, 104, 101, 114, 101, 32] + ], + "activeIndex": 5 + }, + { + "time": 10288.8, + "inputs": [ + [97, 108, 108, 32], + [109, 97, 110, 32], + [119, 111, 117, 108, 100, 32], + [115, 99, 104, 111, 111, 108, 120, 120, 32], + [119, 104, 101, 114, 101, 32], + [100, 97, 121, 32], + [100, 111, 32], + [110, 101, 118, 101, 114, 32], + [119, 101, 108, 108, 32], + [97, 103, 97, 105, 110, 115, 116] + ], + "activeIndex": 9 + } + ], + "replay": [ + { + "zen": false, + "words": [ + [97, 108, 108, 32], + [109, 97, 110, 32], + [119, 111, 117, 108, 100, 32], + [115, 99, 104, 111, 111, 108, 32], + [119, 104, 101, 114, 101, 32], + [100, 97, 121, 32], + [100, 111, 32], + [110, 101, 118, 101, 114, 32], + [119, 101, 108, 108, 32], + [97, 103, 97, 105, 110, 115, 116] + ], + "steps": [ + { + "action": "correctLetter", + "value": [97], + "time": 0 + }, + { + "action": "correctLetter", + "value": [108], + "time": 116 + }, + { + "action": "correctLetter", + "value": [108], + "time": 309.9 + }, + { + "action": "correctLetter", + "value": [32], + "time": 462.6 + }, + { + "action": "submitCorrectWord", + "time": 740.2 + }, + { + "action": "correctLetter", + "value": [109], + "time": 740.2 + }, + { + "action": "incorrectLetter", + "value": [113], + "time": 922.1 + }, + { + "action": "setLetterIndex", + "value": 1, + "time": 1064.3 + }, + { + "action": "correctLetter", + "value": [97], + "time": 1223.2 + }, + { + "action": "correctLetter", + "value": [110], + "time": 1394.3 + }, + { + "action": "correctLetter", + "value": [32], + "time": 1601.4 + }, + { + "action": "submitCorrectWord", + "time": 1851.2 + }, + { + "action": "correctLetter", + "value": [119], + "time": 1851.2 + }, + { + "action": "correctLetter", + "value": [111], + "time": 1958.6 + }, + { + "action": "correctLetter", + "value": [117], + "time": 2049.6 + }, + { + "action": "correctLetter", + "value": [108], + "time": 2195.3 + }, + { + "action": "correctLetter", + "value": [100], + "time": 2403.2 + }, + { + "action": "correctLetter", + "value": [32], + "time": 2549.2 + }, + { + "action": "submitCorrectWord", + "time": 2733.2 + }, + { + "action": "correctLetter", + "value": [115], + "time": 2733.2 + }, + { + "action": "correctLetter", + "value": [99], + "time": 2829.2 + }, + { + "action": "correctLetter", + "value": [104], + "time": 3038 + }, + { + "action": "correctLetter", + "value": [111], + "time": 3127.3 + }, + { + "action": "correctLetter", + "value": [111], + "time": 3229.3 + }, + { + "action": "correctLetter", + "value": [108], + "time": 3375 + }, + { + "action": "incorrectLetter", + "value": [120], + "time": 3554.3 + }, + { + "action": "incorrectLetter", + "value": [120], + "time": 3736.7 + }, + { + "action": "incorrectLetter", + "value": [32], + "time": 3923.1 + }, + { + "action": "submitErrorWord", + "time": 4227.4 + }, + { + "action": "correctLetter", + "value": [119], + "time": 4227.4 + }, + { + "action": "correctLetter", + "value": [104], + "time": 4402.6 + }, + { + "action": "correctLetter", + "value": [101], + "time": 4594.5 + }, + { + "action": "correctLetter", + "value": [114], + "time": 4746.3 + }, + { + "action": "correctLetter", + "value": [101], + "time": 4938.2 + }, + { + "action": "correctLetter", + "value": [32], + "time": 5095.1 + }, + { + "action": "submitCorrectWord", + "time": 5374.5 + }, + { + "action": "correctLetter", + "value": [100], + "time": 5374.5 + }, + { + "action": "correctLetter", + "value": [97], + "time": 5518.8 + }, + { + "action": "setLetterIndex", + "value": 0, + "time": 5606.5 + }, + { + "action": "correctLetter", + "value": [100], + "time": 5768.8 + }, + { + "action": "correctLetter", + "value": [97], + "time": 5954.8 + }, + { + "action": "correctLetter", + "value": [121], + "time": 6155.3 + }, + { + "action": "correctLetter", + "value": [32], + "time": 6267.5 + }, + { + "action": "submitCorrectWord", + "time": 6553.1 + }, + { + "action": "correctLetter", + "value": [100], + "time": 6553.1 + }, + { + "action": "incorrectLetter", + "value": [32], + "time": 6755.7 + }, + { + "action": "setLetterIndex", + "value": 1, + "time": 6880.7 + }, + { + "action": "correctLetter", + "value": [111], + "time": 7032.8 + }, + { + "action": "correctLetter", + "value": [32], + "time": 7158.6 + }, + { + "action": "submitCorrectWord", + "time": 7409.1 + }, + { + "action": "correctLetter", + "value": [110], + "time": 7409.1 + }, + { + "action": "correctLetter", + "value": [101], + "time": 7542 + }, + { + "action": "correctLetter", + "value": [118], + "time": 7680.4 + }, + { + "action": "correctLetter", + "value": [101], + "time": 7828.2 + }, + { + "action": "correctLetter", + "value": [114], + "time": 7949.3 + }, + { + "action": "correctLetter", + "value": [32], + "time": 8125.2 + }, + { + "action": "submitCorrectWord", + "time": 8358.5 + }, + { + "action": "correctLetter", + "value": [119], + "time": 8358.5 + }, + { + "action": "correctLetter", + "value": [101], + "time": 8523.7 + }, + { + "action": "correctLetter", + "value": [108], + "time": 8652.8 + }, + { + "action": "correctLetter", + "value": [108], + "time": 8755.9 + }, + { + "action": "correctLetter", + "value": [32], + "time": 8932.8 + }, + { + "action": "submitCorrectWord", + "time": 9259.6 + }, + { + "action": "correctLetter", + "value": [97], + "time": 9259.6 + }, + { + "action": "correctLetter", + "value": [103], + "time": 9418.3 + }, + { + "action": "correctLetter", + "value": [97], + "time": 9510.9 + }, + { + "action": "correctLetter", + "value": [105], + "time": 9664.2 + }, + { + "action": "correctLetter", + "value": [110], + "time": 9856.1 + }, + { + "action": "correctLetter", + "value": [115], + "time": 10006.7 + }, + { + "action": "correctLetter", + "value": [116], + "time": 10201.6 + } + ], + "final": [ + { + "letters": [ + { + "char": [97], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [109], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [119], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [117], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [100], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [115], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [99], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [104], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": true, + "extra": false + }, + { + "char": [120], + "correct": false, + "incorrect": true, + "extra": true + }, + { + "char": [32], + "correct": false, + "incorrect": true, + "extra": true + } + ], + "error": true + }, + { + "letters": [ + { + "char": [119], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [104], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [100], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [121], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [100], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [110], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [118], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [119], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [97], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [103], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [105], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + } + ], + "cursor": { + "wordIndex": 9, + "letterIndex": 7 + }, + "sounds": [ + "click", + "click", + "click", + "click", + "click", + "click", + "error", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "error", + "error", + "error", + "error", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "error", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click" + ], + "seeks": [ + { + "target": { + "wordIndex": 0, + "letterIndex": 0 + }, + "cursor": { + "wordIndex": 0, + "letterIndex": 0 + }, + "nextIndex": 0, + "words": [ + { + "letters": [ + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [109], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [119], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [117], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [100], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [99], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [104], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [119], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [104], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [100], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [121], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [100], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [118], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [119], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [103], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [105], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + } + ] + }, + { + "target": { + "wordIndex": 1, + "letterIndex": 2 + }, + "cursor": { + "wordIndex": 1, + "letterIndex": 2 + }, + "nextIndex": 7, + "words": [ + { + "letters": [ + { + "char": [97], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [109], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": true, + "extra": false + }, + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [119], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [117], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [100], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [99], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [104], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [119], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [104], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [100], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [121], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [100], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [118], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [119], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [103], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [105], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + } + ] + }, + { + "target": { + "wordIndex": 3, + "letterIndex": 0 + }, + "cursor": { + "wordIndex": 3, + "letterIndex": 0 + }, + "nextIndex": 19, + "words": [ + { + "letters": [ + { + "char": [97], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [109], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [119], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [117], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [100], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [99], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [104], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [119], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [104], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [100], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [121], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [100], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [118], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [119], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [103], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [105], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + } + ] + } + ] + }, + { + "zen": true, + "words": [ + [97, 108, 108, 32], + [109, 97, 110, 32], + [119, 111, 117, 108, 100, 32], + [115, 99, 104, 111, 111, 108, 120, 120, 32], + [119, 104, 101, 114, 101, 32], + [100, 97, 121, 32], + [100, 111, 32], + [110, 101, 118, 101, 114, 32], + [119, 101, 108, 108, 32], + [97, 103, 97, 105, 110, 115, 116] + ], + "steps": [ + { + "action": "correctLetter", + "value": [97], + "time": 0 + }, + { + "action": "correctLetter", + "value": [108], + "time": 116 + }, + { + "action": "correctLetter", + "value": [108], + "time": 309.9 + }, + { + "action": "correctLetter", + "value": [32], + "time": 462.6 + }, + { + "action": "submitCorrectWord", + "time": 740.2 + }, + { + "action": "correctLetter", + "value": [109], + "time": 740.2 + }, + { + "action": "incorrectLetter", + "value": [113], + "time": 922.1 + }, + { + "action": "setLetterIndex", + "value": 1, + "time": 1064.3 + }, + { + "action": "correctLetter", + "value": [97], + "time": 1223.2 + }, + { + "action": "correctLetter", + "value": [110], + "time": 1394.3 + }, + { + "action": "correctLetter", + "value": [32], + "time": 1601.4 + }, + { + "action": "submitCorrectWord", + "time": 1851.2 + }, + { + "action": "correctLetter", + "value": [119], + "time": 1851.2 + }, + { + "action": "correctLetter", + "value": [111], + "time": 1958.6 + }, + { + "action": "correctLetter", + "value": [117], + "time": 2049.6 + }, + { + "action": "correctLetter", + "value": [108], + "time": 2195.3 + }, + { + "action": "correctLetter", + "value": [100], + "time": 2403.2 + }, + { + "action": "correctLetter", + "value": [32], + "time": 2549.2 + }, + { + "action": "submitCorrectWord", + "time": 2733.2 + }, + { + "action": "correctLetter", + "value": [115], + "time": 2733.2 + }, + { + "action": "correctLetter", + "value": [99], + "time": 2829.2 + }, + { + "action": "correctLetter", + "value": [104], + "time": 3038 + }, + { + "action": "correctLetter", + "value": [111], + "time": 3127.3 + }, + { + "action": "correctLetter", + "value": [111], + "time": 3229.3 + }, + { + "action": "correctLetter", + "value": [108], + "time": 3375 + }, + { + "action": "incorrectLetter", + "value": [120], + "time": 3554.3 + }, + { + "action": "incorrectLetter", + "value": [120], + "time": 3736.7 + }, + { + "action": "incorrectLetter", + "value": [32], + "time": 3923.1 + }, + { + "action": "submitCorrectWord", + "time": 4227.4 + }, + { + "action": "correctLetter", + "value": [119], + "time": 4227.4 + }, + { + "action": "correctLetter", + "value": [104], + "time": 4402.6 + }, + { + "action": "correctLetter", + "value": [101], + "time": 4594.5 + }, + { + "action": "correctLetter", + "value": [114], + "time": 4746.3 + }, + { + "action": "correctLetter", + "value": [101], + "time": 4938.2 + }, + { + "action": "correctLetter", + "value": [32], + "time": 5095.1 + }, + { + "action": "submitCorrectWord", + "time": 5374.5 + }, + { + "action": "correctLetter", + "value": [100], + "time": 5374.5 + }, + { + "action": "correctLetter", + "value": [97], + "time": 5518.8 + }, + { + "action": "setLetterIndex", + "value": 0, + "time": 5606.5 + }, + { + "action": "correctLetter", + "value": [100], + "time": 5768.8 + }, + { + "action": "correctLetter", + "value": [97], + "time": 5954.8 + }, + { + "action": "correctLetter", + "value": [121], + "time": 6155.3 + }, + { + "action": "correctLetter", + "value": [32], + "time": 6267.5 + }, + { + "action": "submitCorrectWord", + "time": 6553.1 + }, + { + "action": "correctLetter", + "value": [100], + "time": 6553.1 + }, + { + "action": "incorrectLetter", + "value": [32], + "time": 6755.7 + }, + { + "action": "setLetterIndex", + "value": 1, + "time": 6880.7 + }, + { + "action": "correctLetter", + "value": [111], + "time": 7032.8 + }, + { + "action": "correctLetter", + "value": [32], + "time": 7158.6 + }, + { + "action": "submitCorrectWord", + "time": 7409.1 + }, + { + "action": "correctLetter", + "value": [110], + "time": 7409.1 + }, + { + "action": "correctLetter", + "value": [101], + "time": 7542 + }, + { + "action": "correctLetter", + "value": [118], + "time": 7680.4 + }, + { + "action": "correctLetter", + "value": [101], + "time": 7828.2 + }, + { + "action": "correctLetter", + "value": [114], + "time": 7949.3 + }, + { + "action": "correctLetter", + "value": [32], + "time": 8125.2 + }, + { + "action": "submitCorrectWord", + "time": 8358.5 + }, + { + "action": "correctLetter", + "value": [119], + "time": 8358.5 + }, + { + "action": "correctLetter", + "value": [101], + "time": 8523.7 + }, + { + "action": "correctLetter", + "value": [108], + "time": 8652.8 + }, + { + "action": "correctLetter", + "value": [108], + "time": 8755.9 + }, + { + "action": "correctLetter", + "value": [32], + "time": 8932.8 + }, + { + "action": "submitCorrectWord", + "time": 9259.6 + }, + { + "action": "correctLetter", + "value": [97], + "time": 9259.6 + }, + { + "action": "correctLetter", + "value": [103], + "time": 9418.3 + }, + { + "action": "correctLetter", + "value": [97], + "time": 9510.9 + }, + { + "action": "correctLetter", + "value": [105], + "time": 9664.2 + }, + { + "action": "correctLetter", + "value": [110], + "time": 9856.1 + }, + { + "action": "correctLetter", + "value": [115], + "time": 10006.7 + }, + { + "action": "correctLetter", + "value": [116], + "time": 10201.6 + } + ], + "final": [ + { + "letters": [ + { + "char": [97], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [109], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [119], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [117], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [100], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [115], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [99], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [104], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [120], + "correct": false, + "incorrect": true, + "extra": false + }, + { + "char": [120], + "correct": false, + "incorrect": true, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": true, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [119], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [104], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [100], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [121], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [100], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [110], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [118], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [119], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [97], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [103], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [105], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + } + ], + "cursor": { + "wordIndex": 9, + "letterIndex": 7 + }, + "sounds": [ + "click", + "click", + "click", + "click", + "click", + "click", + "error", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "error", + "error", + "error", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "error", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click" + ], + "seeks": [ + { + "target": { + "wordIndex": 0, + "letterIndex": 0 + }, + "cursor": { + "wordIndex": 0, + "letterIndex": 0 + }, + "nextIndex": 0, + "words": [ + { + "letters": [ + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [109], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [119], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [117], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [100], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [99], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [104], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [120], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [120], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [119], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [104], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [100], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [121], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [100], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [118], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [119], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [103], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [105], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + } + ] + }, + { + "target": { + "wordIndex": 1, + "letterIndex": 2 + }, + "cursor": { + "wordIndex": 1, + "letterIndex": 2 + }, + "nextIndex": 7, + "words": [ + { + "letters": [ + { + "char": [97], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [109], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": true, + "extra": false + }, + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [119], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [117], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [100], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [99], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [104], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [120], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [120], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [119], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [104], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [100], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [121], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [100], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [118], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [119], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [103], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [105], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + } + ] + }, + { + "target": { + "wordIndex": 3, + "letterIndex": 0 + }, + "cursor": { + "wordIndex": 3, + "letterIndex": 0 + }, + "nextIndex": 19, + "words": [ + { + "letters": [ + { + "char": [97], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [109], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [119], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [117], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [100], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [99], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [104], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [120], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [120], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [119], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [104], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [100], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [121], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [100], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [118], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [119], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [103], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [105], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + } + ] + } + ] + } + ], + "practice": [ + { + "missed": "off", + "slow": false, + "result": null, + "notifications": ["Could not start a new custom test"] + }, + { + "missed": "off", + "slow": true, + "result": { + "text": [ + [100, 97, 121], + [100, 97, 121], + [109, 97, 110] + ], + "sectionLimit": 10 + }, + "notifications": [] + }, + { + "missed": "words", + "slow": false, + "result": { + "text": [ + [115, 99, 104, 111, 111, 108], + [115, 99, 104, 111, 111, 108], + [115, 99, 104, 111, 111, 108], + [109, 97, 110], + [100, 111] + ], + "sectionLimit": 15 + }, + "notifications": [] + }, + { + "missed": "words", + "slow": true, + "result": { + "text": [ + [115, 99, 104, 111, 111, 108], + [115, 99, 104, 111, 111, 108], + [115, 99, 104, 111, 111, 108], + [109, 97, 110], + [100, 111], + [100, 97, 121], + [100, 97, 121], + [109, 97, 110] + ], + "sectionLimit": 25 + }, + "notifications": [] + }, + { + "missed": "biwords", + "slow": false, + "result": { + "text": [ + [119, 111, 117, 108, 100, 32, 115, 99, 104, 111, 111, 108], + [119, 111, 117, 108, 100, 32, 115, 99, 104, 111, 111, 108], + [119, 111, 117, 108, 100, 32, 115, 99, 104, 111, 111, 108], + [97, 108, 108, 32, 109, 97, 110], + [100, 97, 121, 32, 100, 111] + ], + "sectionLimit": 15 + }, + "notifications": [] + }, + { + "missed": "biwords", + "slow": true, + "result": { + "text": [ + [119, 111, 117, 108, 100, 32, 115, 99, 104, 111, 111, 108], + [119, 111, 117, 108, 100, 32, 115, 99, 104, 111, 111, 108], + [119, 111, 117, 108, 100, 32, 115, 99, 104, 111, 111, 108], + [97, 108, 108, 32, 109, 97, 110], + [100, 97, 121, 32, 100, 111], + [100, 97, 121], + [100, 97, 121], + [109, 97, 110] + ], + "sectionLimit": 25 + }, + "notifications": [] + } + ], + "validity": [ + { + "input": { + "result": { + "mode": "words", + "mode2": "10", + "testDuration": 10.2, + "wpm": 52.94, + "rawWpm": 63.53, + "acc": 91.38 + }, + "bailedOut": false, + "failed": false, + "repeated": false, + "lbOptOut": false, + "customLimit": { + "mode": "word", + "value": 10 + } + }, + "reason": null + }, + { + "input": { + "result": { + "mode": "words", + "mode2": "10", + "testDuration": 10.2, + "wpm": 52.94, + "rawWpm": 63.53, + "acc": 91.38 + }, + "bailedOut": false, + "failed": false, + "repeated": true, + "lbOptOut": false, + "customLimit": { + "mode": "word", + "value": 10 + } + }, + "reason": "repeated" + }, + { + "input": { + "result": { + "mode": "words", + "mode2": "10", + "testDuration": 10.2, + "wpm": 52.94, + "rawWpm": 63.53, + "acc": 91.38 + }, + "bailedOut": false, + "failed": true, + "repeated": false, + "lbOptOut": false, + "customLimit": { + "mode": "word", + "value": 10 + } + }, + "reason": "failed" + }, + { + "input": { + "result": { + "mode": "words", + "mode2": "10", + "testDuration": 10.2, + "wpm": 52.94, + "rawWpm": 63.53, + "acc": 91.38 + }, + "bailedOut": false, + "failed": true, + "repeated": true, + "lbOptOut": false, + "customLimit": { + "mode": "word", + "value": 10 + } + }, + "reason": "failed" + }, + { + "input": { + "result": { + "mode": "words", + "mode2": "10", + "testDuration": 10.2, + "wpm": 52.94, + "rawWpm": 63.53, + "acc": 91.38 + }, + "bailedOut": true, + "failed": false, + "repeated": false, + "lbOptOut": false, + "customLimit": { + "mode": "word", + "value": 10 + } + }, + "reason": null + }, + { + "input": { + "result": { + "mode": "words", + "mode2": "10", + "testDuration": 10.2, + "wpm": 52.94, + "rawWpm": 63.53, + "acc": 91.38 + }, + "bailedOut": true, + "failed": false, + "repeated": true, + "lbOptOut": false, + "customLimit": { + "mode": "word", + "value": 10 + } + }, + "reason": "repeated" + }, + { + "input": { + "result": { + "mode": "words", + "mode2": "10", + "testDuration": 10.2, + "wpm": 52.94, + "rawWpm": 63.53, + "acc": 91.38 + }, + "bailedOut": true, + "failed": true, + "repeated": false, + "lbOptOut": false, + "customLimit": { + "mode": "word", + "value": 10 + } + }, + "reason": "failed" + }, + { + "input": { + "result": { + "mode": "words", + "mode2": "10", + "testDuration": 10.2, + "wpm": 52.94, + "rawWpm": 63.53, + "acc": 91.38 + }, + "bailedOut": true, + "failed": true, + "repeated": true, + "lbOptOut": false, + "customLimit": { + "mode": "word", + "value": 10 + } + }, + "reason": "failed" + } + ] + }, + { + "name": "words-10-punctuation-numbers", + "frames": [ + { + "time": -1, + "inputs": [], + "activeIndex": 0 + }, + { + "time": 0, + "inputs": [[50]], + "activeIndex": 0 + }, + { + "time": 2082.525, + "inputs": [ + [50, 53, 32], + [102, 111, 114, 46, 32], + [66, 101, 102, 111, 114] + ], + "activeIndex": 2 + }, + { + "time": 4165.05, + "inputs": [ + [50, 53, 32], + [102, 111, 114, 46, 32], + [66, 101, 102, 111, 114, 101, 32], + [116, 117, 114, 110, 32], + [115, 99, 104, 111, 111, 108, 32] + ], + "activeIndex": 5 + }, + { + "time": 8330.1, + "inputs": [ + [50, 53, 32], + [102, 111, 114, 46, 32], + [66, 101, 102, 111, 114, 101, 32], + [116, 117, 114, 110, 32], + [115, 99, 104, 111, 111, 108, 32], + [108, 105, 110, 101, 32], + [51, 52, 32], + [101, 97, 114, 108, 121, 32], + [110, 111, 116, 44, 32], + [109, 111, 115, 116, 46] + ], + "activeIndex": 9 + } + ], + "replay": [ + { + "zen": false, + "words": [ + [50, 53, 32], + [102, 111, 114, 46, 32], + [66, 101, 102, 111, 114, 101, 32], + [116, 117, 114, 110, 32], + [115, 99, 104, 111, 111, 108, 32], + [108, 105, 110, 101, 32], + [51, 52, 32], + [101, 97, 114, 108, 121, 32], + [110, 111, 116, 44, 32], + [109, 111, 115, 116, 46] + ], + "steps": [ + { + "action": "correctLetter", + "value": [50], + "time": 0 + }, + { + "action": "correctLetter", + "value": [53], + "time": 118 + }, + { + "action": "correctLetter", + "value": [32], + "time": 278.8 + }, + { + "action": "submitCorrectWord", + "time": 568.2 + }, + { + "action": "correctLetter", + "value": [102], + "time": 568.2 + }, + { + "action": "correctLetter", + "value": [111], + "time": 718.9 + }, + { + "action": "correctLetter", + "value": [114], + "time": 869.9 + }, + { + "action": "correctLetter", + "value": [46], + "time": 1027.4 + }, + { + "action": "correctLetter", + "value": [32], + "time": 1131.8 + }, + { + "action": "submitCorrectWord", + "time": 1464.2 + }, + { + "action": "correctLetter", + "value": [66], + "time": 1464.2 + }, + { + "action": "correctLetter", + "value": [101], + "time": 1566.8 + }, + { + "action": "correctLetter", + "value": [102], + "time": 1727.5 + }, + { + "action": "correctLetter", + "value": [111], + "time": 1907.1 + }, + { + "action": "correctLetter", + "value": [114], + "time": 1999.9 + }, + { + "action": "correctLetter", + "value": [101], + "time": 2134.7 + }, + { + "action": "correctLetter", + "value": [32], + "time": 2263.6 + }, + { + "action": "submitCorrectWord", + "time": 2472.5 + }, + { + "action": "correctLetter", + "value": [116], + "time": 2472.5 + }, + { + "action": "correctLetter", + "value": [117], + "time": 2599.6 + }, + { + "action": "correctLetter", + "value": [114], + "time": 2798.1 + }, + { + "action": "correctLetter", + "value": [110], + "time": 2970.5 + }, + { + "action": "correctLetter", + "value": [32], + "time": 3134.8 + }, + { + "action": "submitCorrectWord", + "time": 3339.6 + }, + { + "action": "correctLetter", + "value": [115], + "time": 3339.6 + }, + { + "action": "correctLetter", + "value": [99], + "time": 3466.7 + }, + { + "action": "correctLetter", + "value": [104], + "time": 3624.3 + }, + { + "action": "correctLetter", + "value": [111], + "time": 3719.6 + }, + { + "action": "correctLetter", + "value": [111], + "time": 3907.3 + }, + { + "action": "correctLetter", + "value": [108], + "time": 4026 + }, + { + "action": "correctLetter", + "value": [32], + "time": 4124.6 + }, + { + "action": "submitCorrectWord", + "time": 4389.2 + }, + { + "action": "correctLetter", + "value": [108], + "time": 4389.2 + }, + { + "action": "correctLetter", + "value": [105], + "time": 4528.7 + }, + { + "action": "correctLetter", + "value": [110], + "time": 4629.9 + }, + { + "action": "correctLetter", + "value": [101], + "time": 4822 + }, + { + "action": "correctLetter", + "value": [32], + "time": 4915.5 + }, + { + "action": "submitCorrectWord", + "time": 5232 + }, + { + "action": "correctLetter", + "value": [51], + "time": 5232 + }, + { + "action": "correctLetter", + "value": [52], + "time": 5367.3 + }, + { + "action": "correctLetter", + "value": [32], + "time": 5458.8 + }, + { + "action": "submitCorrectWord", + "time": 5685 + }, + { + "action": "correctLetter", + "value": [101], + "time": 5685 + }, + { + "action": "correctLetter", + "value": [97], + "time": 5774.9 + }, + { + "action": "correctLetter", + "value": [114], + "time": 5908.8 + }, + { + "action": "correctLetter", + "value": [108], + "time": 6118.6 + }, + { + "action": "correctLetter", + "value": [121], + "time": 6309.5 + }, + { + "action": "correctLetter", + "value": [32], + "time": 6480.4 + }, + { + "action": "submitCorrectWord", + "time": 6764.6 + }, + { + "action": "correctLetter", + "value": [110], + "time": 6764.6 + }, + { + "action": "correctLetter", + "value": [111], + "time": 6924.1 + }, + { + "action": "correctLetter", + "value": [116], + "time": 7053.6 + }, + { + "action": "correctLetter", + "value": [44], + "time": 7182.7 + }, + { + "action": "correctLetter", + "value": [32], + "time": 7306.5 + }, + { + "action": "submitCorrectWord", + "time": 7589.2 + }, + { + "action": "correctLetter", + "value": [109], + "time": 7589.2 + }, + { + "action": "correctLetter", + "value": [111], + "time": 7765.5 + }, + { + "action": "correctLetter", + "value": [115], + "time": 7942.1 + }, + { + "action": "correctLetter", + "value": [116], + "time": 8077.4 + }, + { + "action": "correctLetter", + "value": [46], + "time": 8286.1 + } + ], + "final": [ + { + "letters": [ + { + "char": [50], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [53], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [102], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [46], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [66], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [102], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [116], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [117], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [115], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [99], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [104], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [108], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [105], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [51], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [52], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [121], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [110], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [44], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [109], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [46], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + } + ], + "cursor": { + "wordIndex": 9, + "letterIndex": 5 + }, + "sounds": [ + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click" + ], + "seeks": [ + { + "target": { + "wordIndex": 0, + "letterIndex": 0 + }, + "cursor": { + "wordIndex": 0, + "letterIndex": 0 + }, + "nextIndex": 0, + "words": [ + { + "letters": [ + { + "char": [50], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [53], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [102], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [46], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [66], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [102], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [117], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [99], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [104], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [108], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [105], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [51], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [52], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [121], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [44], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [109], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [46], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + } + ] + }, + { + "target": { + "wordIndex": 1, + "letterIndex": 2 + }, + "cursor": { + "wordIndex": 1, + "letterIndex": 2 + }, + "nextIndex": 6, + "words": [ + { + "letters": [ + { + "char": [50], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [53], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [102], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [46], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [66], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [102], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [117], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [99], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [104], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [108], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [105], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [51], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [52], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [121], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [44], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [109], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [46], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + } + ] + }, + { + "target": { + "wordIndex": 3, + "letterIndex": 0 + }, + "cursor": { + "wordIndex": 3, + "letterIndex": 0 + }, + "nextIndex": 18, + "words": [ + { + "letters": [ + { + "char": [50], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [53], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [102], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [46], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [66], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [102], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [117], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [99], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [104], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [108], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [105], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [51], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [52], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [121], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [44], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [109], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [46], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + } + ] + } + ] + }, + { + "zen": true, + "words": [ + [50, 53, 32], + [102, 111, 114, 46, 32], + [66, 101, 102, 111, 114, 101, 32], + [116, 117, 114, 110, 32], + [115, 99, 104, 111, 111, 108, 32], + [108, 105, 110, 101, 32], + [51, 52, 32], + [101, 97, 114, 108, 121, 32], + [110, 111, 116, 44, 32], + [109, 111, 115, 116, 46] + ], + "steps": [ + { + "action": "correctLetter", + "value": [50], + "time": 0 + }, + { + "action": "correctLetter", + "value": [53], + "time": 118 + }, + { + "action": "correctLetter", + "value": [32], + "time": 278.8 + }, + { + "action": "submitCorrectWord", + "time": 568.2 + }, + { + "action": "correctLetter", + "value": [102], + "time": 568.2 + }, + { + "action": "correctLetter", + "value": [111], + "time": 718.9 + }, + { + "action": "correctLetter", + "value": [114], + "time": 869.9 + }, + { + "action": "correctLetter", + "value": [46], + "time": 1027.4 + }, + { + "action": "correctLetter", + "value": [32], + "time": 1131.8 + }, + { + "action": "submitCorrectWord", + "time": 1464.2 + }, + { + "action": "correctLetter", + "value": [66], + "time": 1464.2 + }, + { + "action": "correctLetter", + "value": [101], + "time": 1566.8 + }, + { + "action": "correctLetter", + "value": [102], + "time": 1727.5 + }, + { + "action": "correctLetter", + "value": [111], + "time": 1907.1 + }, + { + "action": "correctLetter", + "value": [114], + "time": 1999.9 + }, + { + "action": "correctLetter", + "value": [101], + "time": 2134.7 + }, + { + "action": "correctLetter", + "value": [32], + "time": 2263.6 + }, + { + "action": "submitCorrectWord", + "time": 2472.5 + }, + { + "action": "correctLetter", + "value": [116], + "time": 2472.5 + }, + { + "action": "correctLetter", + "value": [117], + "time": 2599.6 + }, + { + "action": "correctLetter", + "value": [114], + "time": 2798.1 + }, + { + "action": "correctLetter", + "value": [110], + "time": 2970.5 + }, + { + "action": "correctLetter", + "value": [32], + "time": 3134.8 + }, + { + "action": "submitCorrectWord", + "time": 3339.6 + }, + { + "action": "correctLetter", + "value": [115], + "time": 3339.6 + }, + { + "action": "correctLetter", + "value": [99], + "time": 3466.7 + }, + { + "action": "correctLetter", + "value": [104], + "time": 3624.3 + }, + { + "action": "correctLetter", + "value": [111], + "time": 3719.6 + }, + { + "action": "correctLetter", + "value": [111], + "time": 3907.3 + }, + { + "action": "correctLetter", + "value": [108], + "time": 4026 + }, + { + "action": "correctLetter", + "value": [32], + "time": 4124.6 + }, + { + "action": "submitCorrectWord", + "time": 4389.2 + }, + { + "action": "correctLetter", + "value": [108], + "time": 4389.2 + }, + { + "action": "correctLetter", + "value": [105], + "time": 4528.7 + }, + { + "action": "correctLetter", + "value": [110], + "time": 4629.9 + }, + { + "action": "correctLetter", + "value": [101], + "time": 4822 + }, + { + "action": "correctLetter", + "value": [32], + "time": 4915.5 + }, + { + "action": "submitCorrectWord", + "time": 5232 + }, + { + "action": "correctLetter", + "value": [51], + "time": 5232 + }, + { + "action": "correctLetter", + "value": [52], + "time": 5367.3 + }, + { + "action": "correctLetter", + "value": [32], + "time": 5458.8 + }, + { + "action": "submitCorrectWord", + "time": 5685 + }, + { + "action": "correctLetter", + "value": [101], + "time": 5685 + }, + { + "action": "correctLetter", + "value": [97], + "time": 5774.9 + }, + { + "action": "correctLetter", + "value": [114], + "time": 5908.8 + }, + { + "action": "correctLetter", + "value": [108], + "time": 6118.6 + }, + { + "action": "correctLetter", + "value": [121], + "time": 6309.5 + }, + { + "action": "correctLetter", + "value": [32], + "time": 6480.4 + }, + { + "action": "submitCorrectWord", + "time": 6764.6 + }, + { + "action": "correctLetter", + "value": [110], + "time": 6764.6 + }, + { + "action": "correctLetter", + "value": [111], + "time": 6924.1 + }, + { + "action": "correctLetter", + "value": [116], + "time": 7053.6 + }, + { + "action": "correctLetter", + "value": [44], + "time": 7182.7 + }, + { + "action": "correctLetter", + "value": [32], + "time": 7306.5 + }, + { + "action": "submitCorrectWord", + "time": 7589.2 + }, + { + "action": "correctLetter", + "value": [109], + "time": 7589.2 + }, + { + "action": "correctLetter", + "value": [111], + "time": 7765.5 + }, + { + "action": "correctLetter", + "value": [115], + "time": 7942.1 + }, + { + "action": "correctLetter", + "value": [116], + "time": 8077.4 + }, + { + "action": "correctLetter", + "value": [46], + "time": 8286.1 + } + ], + "final": [ + { + "letters": [ + { + "char": [50], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [53], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [102], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [46], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [66], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [102], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [116], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [117], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [115], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [99], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [104], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [108], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [105], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [51], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [52], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [121], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [110], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [44], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [109], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [46], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + } + ], + "cursor": { + "wordIndex": 9, + "letterIndex": 5 + }, + "sounds": [ + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click" + ], + "seeks": [ + { + "target": { + "wordIndex": 0, + "letterIndex": 0 + }, + "cursor": { + "wordIndex": 0, + "letterIndex": 0 + }, + "nextIndex": 0, + "words": [ + { + "letters": [ + { + "char": [50], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [53], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [102], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [46], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [66], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [102], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [117], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [99], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [104], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [108], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [105], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [51], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [52], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [121], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [44], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [109], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [46], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + } + ] + }, + { + "target": { + "wordIndex": 1, + "letterIndex": 2 + }, + "cursor": { + "wordIndex": 1, + "letterIndex": 2 + }, + "nextIndex": 6, + "words": [ + { + "letters": [ + { + "char": [50], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [53], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [102], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [46], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [66], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [102], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [117], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [99], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [104], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [108], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [105], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [51], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [52], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [121], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [44], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [109], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [46], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + } + ] + }, + { + "target": { + "wordIndex": 3, + "letterIndex": 0 + }, + "cursor": { + "wordIndex": 3, + "letterIndex": 0 + }, + "nextIndex": 18, + "words": [ + { + "letters": [ + { + "char": [50], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [53], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [102], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [46], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [66], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [102], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [117], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [99], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [104], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [108], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [105], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [51], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [52], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [121], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [44], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [109], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [46], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + } + ] + } + ] + } + ], + "practice": [ + { + "missed": "off", + "slow": false, + "result": null, + "notifications": ["Could not start a new custom test"] + }, + { + "missed": "off", + "slow": true, + "result": { + "text": [ + [116, 117, 114, 110], + [116, 117, 114, 110], + [101, 97, 114, 108, 121] + ], + "sectionLimit": 10 + }, + "notifications": [] + }, + { + "missed": "words", + "slow": false, + "result": null, + "notifications": ["You haven't missed any words"] + }, + { + "missed": "words", + "slow": true, + "result": { + "text": [ + [116, 117, 114, 110], + [116, 117, 114, 110], + [101, 97, 114, 108, 121] + ], + "sectionLimit": 10 + }, + "notifications": [] + }, + { + "missed": "biwords", + "slow": false, + "result": null, + "notifications": ["You haven't missed any words"] + }, + { + "missed": "biwords", + "slow": true, + "result": { + "text": [ + [116, 117, 114, 110], + [116, 117, 114, 110], + [101, 97, 114, 108, 121] + ], + "sectionLimit": 10 + }, + "notifications": [] + } + ], + "validity": [ + { + "input": { + "result": { + "mode": "words", + "mode2": "10", + "testDuration": 8.29, + "wpm": 73.82, + "rawWpm": 73.82, + "acc": 100 + }, + "bailedOut": false, + "failed": false, + "repeated": false, + "lbOptOut": false, + "customLimit": { + "mode": "word", + "value": 10 + } + }, + "reason": null + }, + { + "input": { + "result": { + "mode": "words", + "mode2": "10", + "testDuration": 8.29, + "wpm": 73.82, + "rawWpm": 73.82, + "acc": 100 + }, + "bailedOut": false, + "failed": false, + "repeated": true, + "lbOptOut": false, + "customLimit": { + "mode": "word", + "value": 10 + } + }, + "reason": "repeated" + }, + { + "input": { + "result": { + "mode": "words", + "mode2": "10", + "testDuration": 8.29, + "wpm": 73.82, + "rawWpm": 73.82, + "acc": 100 + }, + "bailedOut": false, + "failed": true, + "repeated": false, + "lbOptOut": false, + "customLimit": { + "mode": "word", + "value": 10 + } + }, + "reason": "failed" + }, + { + "input": { + "result": { + "mode": "words", + "mode2": "10", + "testDuration": 8.29, + "wpm": 73.82, + "rawWpm": 73.82, + "acc": 100 + }, + "bailedOut": false, + "failed": true, + "repeated": true, + "lbOptOut": false, + "customLimit": { + "mode": "word", + "value": 10 + } + }, + "reason": "failed" + }, + { + "input": { + "result": { + "mode": "words", + "mode2": "10", + "testDuration": 8.29, + "wpm": 73.82, + "rawWpm": 73.82, + "acc": 100 + }, + "bailedOut": true, + "failed": false, + "repeated": false, + "lbOptOut": false, + "customLimit": { + "mode": "word", + "value": 10 + } + }, + "reason": null + }, + { + "input": { + "result": { + "mode": "words", + "mode2": "10", + "testDuration": 8.29, + "wpm": 73.82, + "rawWpm": 73.82, + "acc": 100 + }, + "bailedOut": true, + "failed": false, + "repeated": true, + "lbOptOut": false, + "customLimit": { + "mode": "word", + "value": 10 + } + }, + "reason": "repeated" + }, + { + "input": { + "result": { + "mode": "words", + "mode2": "10", + "testDuration": 8.29, + "wpm": 73.82, + "rawWpm": 73.82, + "acc": 100 + }, + "bailedOut": true, + "failed": true, + "repeated": false, + "lbOptOut": false, + "customLimit": { + "mode": "word", + "value": 10 + } + }, + "reason": "failed" + }, + { + "input": { + "result": { + "mode": "words", + "mode2": "10", + "testDuration": 8.29, + "wpm": 73.82, + "rawWpm": 73.82, + "acc": 100 + }, + "bailedOut": true, + "failed": true, + "repeated": true, + "lbOptOut": false, + "customLimit": { + "mode": "word", + "value": 10 + } + }, + "reason": "failed" + } + ] + }, + { + "name": "words-10-stop-on-letter", + "frames": [ + { + "time": -1, + "inputs": [], + "activeIndex": 0 + }, + { + "time": 0, + "inputs": [[119]], + "activeIndex": 0 + }, + { + "time": 2480.925, + "inputs": [ + [119, 105, 108, 108, 32], + [112, 101, 111, 112, 108, 101, 32], + [98, 101] + ], + "activeIndex": 2 + }, + { + "time": 4961.85, + "inputs": [ + [119, 105, 108, 108, 32], + [112, 101, 111, 112, 108, 101, 32], + [98, 101, 99, 111, 109, 101, 32], + [111, 112, 101, 110, 32], + [115, 117, 99, 104] + ], + "activeIndex": 4 + }, + { + "time": 9923.7, + "inputs": [ + [119, 105, 108, 108, 32], + [112, 101, 111, 112, 108, 101, 32], + [98, 101, 99, 111, 109, 101, 32], + [111, 112, 101, 110, 32], + [115, 117, 99, 104, 32], + [104, 111, 108, 100, 32], + [119, 104, 97, 116, 32], + [111, 110, 101, 32], + [97, 116, 32], + [112, 111, 115, 115, 105, 98, 108, 101] + ], + "activeIndex": 9 + } + ], + "replay": [ + { + "zen": false, + "words": [ + [119, 105, 108, 108, 32], + [112, 101, 111, 112, 108, 101, 32], + [98, 101, 99, 111, 109, 101, 32], + [111, 112, 101, 110, 32], + [115, 117, 99, 104, 32], + [104, 111, 108, 100, 32], + [119, 104, 97, 116, 32], + [111, 110, 101, 32], + [97, 116, 32], + [112, 111, 115, 115, 105, 98, 108, 101] + ], + "steps": [ + { + "action": "correctLetter", + "value": [119], + "time": 0 + }, + { + "action": "correctLetter", + "value": [105], + "time": 198.9 + }, + { + "action": "correctLetter", + "value": [108], + "time": 352.3 + }, + { + "action": "correctLetter", + "value": [108], + "time": 498.9 + }, + { + "action": "correctLetter", + "value": [32], + "time": 655.2 + }, + { + "action": "submitCorrectWord", + "time": 905.6 + }, + { + "action": "correctLetter", + "value": [112], + "time": 905.6 + }, + { + "action": "correctLetter", + "value": [101], + "time": 1323.2 + }, + { + "action": "correctLetter", + "value": [111], + "time": 1450.1 + }, + { + "action": "correctLetter", + "value": [112], + "time": 1571.8 + }, + { + "action": "correctLetter", + "value": [108], + "time": 1685.1 + }, + { + "action": "correctLetter", + "value": [101], + "time": 1793.4 + }, + { + "action": "correctLetter", + "value": [32], + "time": 1949.7 + }, + { + "action": "submitCorrectWord", + "time": 2185.2 + }, + { + "action": "correctLetter", + "value": [98], + "time": 2185.2 + }, + { + "action": "correctLetter", + "value": [101], + "time": 2337.6 + }, + { + "action": "correctLetter", + "value": [99], + "time": 2512 + }, + { + "action": "correctLetter", + "value": [111], + "time": 2677 + }, + { + "action": "correctLetter", + "value": [109], + "time": 2867.4 + }, + { + "action": "correctLetter", + "value": [101], + "time": 2977 + }, + { + "action": "correctLetter", + "value": [32], + "time": 3119.2 + }, + { + "action": "submitCorrectWord", + "time": 3267.9 + }, + { + "action": "correctLetter", + "value": [111], + "time": 3267.9 + }, + { + "action": "correctLetter", + "value": [112], + "time": 3400.8 + }, + { + "action": "correctLetter", + "value": [101], + "time": 3547.8 + }, + { + "action": "correctLetter", + "value": [110], + "time": 3720.5 + }, + { + "action": "correctLetter", + "value": [32], + "time": 3850.3 + }, + { + "action": "submitCorrectWord", + "time": 4050 + }, + { + "action": "correctLetter", + "value": [115], + "time": 4050 + }, + { + "action": "correctLetter", + "value": [117], + "time": 4489.5 + }, + { + "action": "correctLetter", + "value": [99], + "time": 4665.3 + }, + { + "action": "correctLetter", + "value": [104], + "time": 4820 + }, + { + "action": "correctLetter", + "value": [32], + "time": 4988 + }, + { + "action": "submitCorrectWord", + "time": 5242 + }, + { + "action": "correctLetter", + "value": [104], + "time": 5242 + }, + { + "action": "correctLetter", + "value": [111], + "time": 5412.1 + }, + { + "action": "correctLetter", + "value": [108], + "time": 5615.9 + }, + { + "action": "correctLetter", + "value": [100], + "time": 5778.1 + }, + { + "action": "correctLetter", + "value": [32], + "time": 5984.4 + }, + { + "action": "submitCorrectWord", + "time": 6282.5 + }, + { + "action": "correctLetter", + "value": [119], + "time": 6282.5 + }, + { + "action": "correctLetter", + "value": [104], + "time": 6435.2 + }, + { + "action": "correctLetter", + "value": [97], + "time": 6611.8 + }, + { + "action": "correctLetter", + "value": [116], + "time": 6791.6 + }, + { + "action": "correctLetter", + "value": [32], + "time": 6946.2 + }, + { + "action": "submitCorrectWord", + "time": 7231.5 + }, + { + "action": "correctLetter", + "value": [111], + "time": 7231.5 + }, + { + "action": "correctLetter", + "value": [110], + "time": 7629.6 + }, + { + "action": "correctLetter", + "value": [101], + "time": 7833.4 + }, + { + "action": "correctLetter", + "value": [32], + "time": 7997.4 + }, + { + "action": "submitCorrectWord", + "time": 8211.9 + }, + { + "action": "correctLetter", + "value": [97], + "time": 8211.9 + }, + { + "action": "correctLetter", + "value": [116], + "time": 8387.5 + }, + { + "action": "correctLetter", + "value": [32], + "time": 8511 + }, + { + "action": "submitCorrectWord", + "time": 8838.7 + }, + { + "action": "correctLetter", + "value": [112], + "time": 8838.7 + }, + { + "action": "correctLetter", + "value": [111], + "time": 9002.8 + }, + { + "action": "correctLetter", + "value": [115], + "time": 9105.9 + }, + { + "action": "correctLetter", + "value": [115], + "time": 9256 + }, + { + "action": "correctLetter", + "value": [105], + "time": 9365.8 + }, + { + "action": "correctLetter", + "value": [98], + "time": 9484.4 + }, + { + "action": "correctLetter", + "value": [108], + "time": 9678.1 + }, + { + "action": "correctLetter", + "value": [101], + "time": 9846.4 + } + ], + "final": [ + { + "letters": [ + { + "char": [119], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [105], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [112], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [112], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [98], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [99], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [109], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [112], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [115], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [117], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [99], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [104], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [104], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [100], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [119], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [104], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [97], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [112], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [105], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [98], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + } + ], + "cursor": { + "wordIndex": 9, + "letterIndex": 8 + }, + "sounds": [ + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click" + ], + "seeks": [ + { + "target": { + "wordIndex": 0, + "letterIndex": 0 + }, + "cursor": { + "wordIndex": 0, + "letterIndex": 0 + }, + "nextIndex": 0, + "words": [ + { + "letters": [ + { + "char": [119], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [105], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [112], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [112], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [98], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [99], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [109], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [112], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [117], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [99], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [104], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [104], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [100], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [119], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [104], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [112], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [105], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [98], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + } + ] + }, + { + "target": { + "wordIndex": 1, + "letterIndex": 2 + }, + "cursor": { + "wordIndex": 1, + "letterIndex": 2 + }, + "nextIndex": 8, + "words": [ + { + "letters": [ + { + "char": [119], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [105], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [112], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [112], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [98], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [99], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [109], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [112], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [117], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [99], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [104], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [104], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [100], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [119], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [104], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [112], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [105], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [98], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + } + ] + }, + { + "target": { + "wordIndex": 3, + "letterIndex": 0 + }, + "cursor": { + "wordIndex": 3, + "letterIndex": 0 + }, + "nextIndex": 22, + "words": [ + { + "letters": [ + { + "char": [119], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [105], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [112], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [112], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [98], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [99], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [109], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [112], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [117], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [99], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [104], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [104], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [100], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [119], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [104], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [112], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [105], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [98], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + } + ] + } + ] + }, + { + "zen": true, + "words": [ + [119, 105, 108, 108, 32], + [112, 101, 111, 112, 108, 101, 32], + [98, 101, 99, 111, 109, 101, 32], + [111, 112, 101, 110, 32], + [115, 117, 99, 104, 32], + [104, 111, 108, 100, 32], + [119, 104, 97, 116, 32], + [111, 110, 101, 32], + [97, 116, 32], + [112, 111, 115, 115, 105, 98, 108, 101] + ], + "steps": [ + { + "action": "correctLetter", + "value": [119], + "time": 0 + }, + { + "action": "correctLetter", + "value": [105], + "time": 198.9 + }, + { + "action": "correctLetter", + "value": [108], + "time": 352.3 + }, + { + "action": "correctLetter", + "value": [108], + "time": 498.9 + }, + { + "action": "correctLetter", + "value": [32], + "time": 655.2 + }, + { + "action": "submitCorrectWord", + "time": 905.6 + }, + { + "action": "correctLetter", + "value": [112], + "time": 905.6 + }, + { + "action": "correctLetter", + "value": [101], + "time": 1323.2 + }, + { + "action": "correctLetter", + "value": [111], + "time": 1450.1 + }, + { + "action": "correctLetter", + "value": [112], + "time": 1571.8 + }, + { + "action": "correctLetter", + "value": [108], + "time": 1685.1 + }, + { + "action": "correctLetter", + "value": [101], + "time": 1793.4 + }, + { + "action": "correctLetter", + "value": [32], + "time": 1949.7 + }, + { + "action": "submitCorrectWord", + "time": 2185.2 + }, + { + "action": "correctLetter", + "value": [98], + "time": 2185.2 + }, + { + "action": "correctLetter", + "value": [101], + "time": 2337.6 + }, + { + "action": "correctLetter", + "value": [99], + "time": 2512 + }, + { + "action": "correctLetter", + "value": [111], + "time": 2677 + }, + { + "action": "correctLetter", + "value": [109], + "time": 2867.4 + }, + { + "action": "correctLetter", + "value": [101], + "time": 2977 + }, + { + "action": "correctLetter", + "value": [32], + "time": 3119.2 + }, + { + "action": "submitCorrectWord", + "time": 3267.9 + }, + { + "action": "correctLetter", + "value": [111], + "time": 3267.9 + }, + { + "action": "correctLetter", + "value": [112], + "time": 3400.8 + }, + { + "action": "correctLetter", + "value": [101], + "time": 3547.8 + }, + { + "action": "correctLetter", + "value": [110], + "time": 3720.5 + }, + { + "action": "correctLetter", + "value": [32], + "time": 3850.3 + }, + { + "action": "submitCorrectWord", + "time": 4050 + }, + { + "action": "correctLetter", + "value": [115], + "time": 4050 + }, + { + "action": "correctLetter", + "value": [117], + "time": 4489.5 + }, + { + "action": "correctLetter", + "value": [99], + "time": 4665.3 + }, + { + "action": "correctLetter", + "value": [104], + "time": 4820 + }, + { + "action": "correctLetter", + "value": [32], + "time": 4988 + }, + { + "action": "submitCorrectWord", + "time": 5242 + }, + { + "action": "correctLetter", + "value": [104], + "time": 5242 + }, + { + "action": "correctLetter", + "value": [111], + "time": 5412.1 + }, + { + "action": "correctLetter", + "value": [108], + "time": 5615.9 + }, + { + "action": "correctLetter", + "value": [100], + "time": 5778.1 + }, + { + "action": "correctLetter", + "value": [32], + "time": 5984.4 + }, + { + "action": "submitCorrectWord", + "time": 6282.5 + }, + { + "action": "correctLetter", + "value": [119], + "time": 6282.5 + }, + { + "action": "correctLetter", + "value": [104], + "time": 6435.2 + }, + { + "action": "correctLetter", + "value": [97], + "time": 6611.8 + }, + { + "action": "correctLetter", + "value": [116], + "time": 6791.6 + }, + { + "action": "correctLetter", + "value": [32], + "time": 6946.2 + }, + { + "action": "submitCorrectWord", + "time": 7231.5 + }, + { + "action": "correctLetter", + "value": [111], + "time": 7231.5 + }, + { + "action": "correctLetter", + "value": [110], + "time": 7629.6 + }, + { + "action": "correctLetter", + "value": [101], + "time": 7833.4 + }, + { + "action": "correctLetter", + "value": [32], + "time": 7997.4 + }, + { + "action": "submitCorrectWord", + "time": 8211.9 + }, + { + "action": "correctLetter", + "value": [97], + "time": 8211.9 + }, + { + "action": "correctLetter", + "value": [116], + "time": 8387.5 + }, + { + "action": "correctLetter", + "value": [32], + "time": 8511 + }, + { + "action": "submitCorrectWord", + "time": 8838.7 + }, + { + "action": "correctLetter", + "value": [112], + "time": 8838.7 + }, + { + "action": "correctLetter", + "value": [111], + "time": 9002.8 + }, + { + "action": "correctLetter", + "value": [115], + "time": 9105.9 + }, + { + "action": "correctLetter", + "value": [115], + "time": 9256 + }, + { + "action": "correctLetter", + "value": [105], + "time": 9365.8 + }, + { + "action": "correctLetter", + "value": [98], + "time": 9484.4 + }, + { + "action": "correctLetter", + "value": [108], + "time": 9678.1 + }, + { + "action": "correctLetter", + "value": [101], + "time": 9846.4 + } + ], + "final": [ + { + "letters": [ + { + "char": [119], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [105], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [112], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [112], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [98], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [99], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [109], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [112], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [115], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [117], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [99], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [104], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [104], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [100], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [119], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [104], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [97], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [112], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [105], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [98], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + } + ], + "cursor": { + "wordIndex": 9, + "letterIndex": 8 + }, + "sounds": [ + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click" + ], + "seeks": [ + { + "target": { + "wordIndex": 0, + "letterIndex": 0 + }, + "cursor": { + "wordIndex": 0, + "letterIndex": 0 + }, + "nextIndex": 0, + "words": [ + { + "letters": [ + { + "char": [119], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [105], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [112], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [112], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [98], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [99], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [109], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [112], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [117], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [99], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [104], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [104], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [100], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [119], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [104], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [112], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [105], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [98], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + } + ] + }, + { + "target": { + "wordIndex": 1, + "letterIndex": 2 + }, + "cursor": { + "wordIndex": 1, + "letterIndex": 2 + }, + "nextIndex": 8, + "words": [ + { + "letters": [ + { + "char": [119], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [105], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [112], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [112], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [98], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [99], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [109], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [112], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [117], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [99], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [104], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [104], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [100], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [119], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [104], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [112], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [105], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [98], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + } + ] + }, + { + "target": { + "wordIndex": 3, + "letterIndex": 0 + }, + "cursor": { + "wordIndex": 3, + "letterIndex": 0 + }, + "nextIndex": 22, + "words": [ + { + "letters": [ + { + "char": [119], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [105], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [112], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [112], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [98], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [99], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [109], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [112], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [117], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [99], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [104], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [104], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [100], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [119], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [104], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [97], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [112], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [105], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [98], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + } + ] + } + ] + } + ], + "practice": [ + { + "missed": "off", + "slow": false, + "result": null, + "notifications": ["Could not start a new custom test"] + }, + { + "missed": "off", + "slow": true, + "result": { + "text": [ + [111, 110, 101], + [111, 110, 101], + [115, 117, 99, 104] + ], + "sectionLimit": 10 + }, + "notifications": [] + }, + { + "missed": "words", + "slow": false, + "result": { + "text": [ + [112, 101, 111, 112, 108, 101], + [112, 101, 111, 112, 108, 101], + [115, 117, 99, 104], + [115, 117, 99, 104], + [111, 110, 101], + [111, 110, 101] + ], + "sectionLimit": 15 + }, + "notifications": [] + }, + { + "missed": "words", + "slow": true, + "result": { + "text": [ + [112, 101, 111, 112, 108, 101], + [112, 101, 111, 112, 108, 101], + [115, 117, 99, 104], + [115, 117, 99, 104], + [111, 110, 101], + [111, 110, 101], + [111, 110, 101], + [111, 110, 101], + [115, 117, 99, 104] + ], + "sectionLimit": 25 + }, + "notifications": [] + }, + { + "missed": "biwords", + "slow": false, + "result": { + "text": [ + [119, 105, 108, 108, 32, 112, 101, 111, 112, 108, 101], + [119, 105, 108, 108, 32, 112, 101, 111, 112, 108, 101], + [111, 112, 101, 110, 32, 115, 117, 99, 104], + [111, 112, 101, 110, 32, 115, 117, 99, 104], + [119, 104, 97, 116, 32, 111, 110, 101], + [119, 104, 97, 116, 32, 111, 110, 101] + ], + "sectionLimit": 15 + }, + "notifications": [] + }, + { + "missed": "biwords", + "slow": true, + "result": { + "text": [ + [119, 105, 108, 108, 32, 112, 101, 111, 112, 108, 101], + [119, 105, 108, 108, 32, 112, 101, 111, 112, 108, 101], + [111, 112, 101, 110, 32, 115, 117, 99, 104], + [111, 112, 101, 110, 32, 115, 117, 99, 104], + [119, 104, 97, 116, 32, 111, 110, 101], + [119, 104, 97, 116, 32, 111, 110, 101], + [111, 110, 101], + [111, 110, 101], + [115, 117, 99, 104] + ], + "sectionLimit": 25 + }, + "notifications": [] + } + ], + "validity": [ + { + "input": { + "result": { + "mode": "words", + "mode2": "10", + "testDuration": 9.85, + "wpm": 65.79, + "rawWpm": 65.79, + "acc": 90 + }, + "bailedOut": false, + "failed": false, + "repeated": false, + "lbOptOut": false, + "customLimit": { + "mode": "word", + "value": 10 + } + }, + "reason": null + }, + { + "input": { + "result": { + "mode": "words", + "mode2": "10", + "testDuration": 9.85, + "wpm": 65.79, + "rawWpm": 65.79, + "acc": 90 + }, + "bailedOut": false, + "failed": false, + "repeated": true, + "lbOptOut": false, + "customLimit": { + "mode": "word", + "value": 10 + } + }, + "reason": "repeated" + }, + { + "input": { + "result": { + "mode": "words", + "mode2": "10", + "testDuration": 9.85, + "wpm": 65.79, + "rawWpm": 65.79, + "acc": 90 + }, + "bailedOut": false, + "failed": true, + "repeated": false, + "lbOptOut": false, + "customLimit": { + "mode": "word", + "value": 10 + } + }, + "reason": "failed" + }, + { + "input": { + "result": { + "mode": "words", + "mode2": "10", + "testDuration": 9.85, + "wpm": 65.79, + "rawWpm": 65.79, + "acc": 90 + }, + "bailedOut": false, + "failed": true, + "repeated": true, + "lbOptOut": false, + "customLimit": { + "mode": "word", + "value": 10 + } + }, + "reason": "failed" + }, + { + "input": { + "result": { + "mode": "words", + "mode2": "10", + "testDuration": 9.85, + "wpm": 65.79, + "rawWpm": 65.79, + "acc": 90 + }, + "bailedOut": true, + "failed": false, + "repeated": false, + "lbOptOut": false, + "customLimit": { + "mode": "word", + "value": 10 + } + }, + "reason": null + }, + { + "input": { + "result": { + "mode": "words", + "mode2": "10", + "testDuration": 9.85, + "wpm": 65.79, + "rawWpm": 65.79, + "acc": 90 + }, + "bailedOut": true, + "failed": false, + "repeated": true, + "lbOptOut": false, + "customLimit": { + "mode": "word", + "value": 10 + } + }, + "reason": "repeated" + }, + { + "input": { + "result": { + "mode": "words", + "mode2": "10", + "testDuration": 9.85, + "wpm": 65.79, + "rawWpm": 65.79, + "acc": 90 + }, + "bailedOut": true, + "failed": true, + "repeated": false, + "lbOptOut": false, + "customLimit": { + "mode": "word", + "value": 10 + } + }, + "reason": "failed" + }, + { + "input": { + "result": { + "mode": "words", + "mode2": "10", + "testDuration": 9.85, + "wpm": 65.79, + "rawWpm": 65.79, + "acc": 90 + }, + "bailedOut": true, + "failed": true, + "repeated": true, + "lbOptOut": false, + "customLimit": { + "mode": "word", + "value": 10 + } + }, + "reason": "failed" + } + ] + }, + { + "name": "zen", + "frames": [ + { + "time": -1, + "inputs": [], + "activeIndex": 0 + }, + { + "time": 0, + "inputs": [[104]], + "activeIndex": 0 + }, + { + "time": 1145.7, + "inputs": [ + [104, 101, 108, 108, 111, 32], + [116, 104, 101] + ], + "activeIndex": 1 + }, + { + "time": 2291.4, + "inputs": [ + [104, 101, 108, 108, 111, 32], + [116, 104, 101, 114, 101, 32], + [122, 101, 110, 10] + ], + "activeIndex": 3 + }, + { + "time": 4582.8, + "inputs": [ + [104, 101, 108, 108, 111, 32], + [116, 104, 101, 114, 101, 32], + [122, 101, 110, 10], + [109, 111, 100, 101, 32], + [114, 111, 99, 107, 115] + ], + "activeIndex": 4 + } + ], + "replay": [ + { + "zen": false, + "words": [], + "steps": [ + { + "action": "correctLetter", + "value": [104], + "time": 0 + }, + { + "action": "correctLetter", + "value": [101], + "time": 106.6 + }, + { + "action": "correctLetter", + "value": [108], + "time": 225.1 + }, + { + "action": "correctLetter", + "value": [108], + "time": 326.1 + }, + { + "action": "correctLetter", + "value": [111], + "time": 491.8 + }, + { + "action": "correctLetter", + "value": [32], + "time": 674.2 + }, + { + "action": "submitErrorWord", + "time": 853.2 + }, + { + "action": "correctLetter", + "value": [116], + "time": 853.2 + }, + { + "action": "correctLetter", + "value": [104], + "time": 1002.7 + }, + { + "action": "correctLetter", + "value": [101], + "time": 1144.7 + }, + { + "action": "correctLetter", + "value": [114], + "time": 1323.3 + }, + { + "action": "correctLetter", + "value": [101], + "time": 1531.3 + }, + { + "action": "correctLetter", + "value": [32], + "time": 1662.7 + }, + { + "action": "submitErrorWord", + "time": 1785.1 + }, + { + "action": "correctLetter", + "value": [122], + "time": 1785.1 + }, + { + "action": "correctLetter", + "value": [101], + "time": 1938.5 + }, + { + "action": "correctLetter", + "value": [110], + "time": 2083.9 + }, + { + "action": "correctLetter", + "value": [10], + "time": 2233.7 + }, + { + "action": "submitErrorWord", + "time": 2415.4 + }, + { + "action": "correctLetter", + "value": [109], + "time": 2415.4 + }, + { + "action": "correctLetter", + "value": [111], + "time": 2601.4 + }, + { + "action": "correctLetter", + "value": [101], + "time": 2730.9 + }, + { + "action": "correctLetter", + "value": [100], + "time": 2827.9 + }, + { + "action": "setLetterIndex", + "value": 3, + "time": 2948.5 + }, + { + "action": "setLetterIndex", + "value": 2, + "time": 3055.7 + }, + { + "action": "correctLetter", + "value": [100], + "time": 3251.7 + }, + { + "action": "correctLetter", + "value": [101], + "time": 3381.3 + }, + { + "action": "correctLetter", + "value": [32], + "time": 3551.1 + }, + { + "action": "submitErrorWord", + "time": 3644.4 + }, + { + "action": "correctLetter", + "value": [114], + "time": 3644.4 + }, + { + "action": "correctLetter", + "value": [111], + "time": 3854.4 + }, + { + "action": "correctLetter", + "value": [99], + "time": 4001.1 + }, + { + "action": "correctLetter", + "value": [107], + "time": 4176 + }, + { + "action": "correctLetter", + "value": [115], + "time": 4381.6 + } + ], + "final": [], + "cursor": { + "wordIndex": 0, + "letterIndex": 0 + }, + "sounds": [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null + ], + "seeks": [ + { + "target": { + "wordIndex": 0, + "letterIndex": 0 + }, + "cursor": { + "wordIndex": 0, + "letterIndex": 0 + }, + "nextIndex": 0, + "words": [] + }, + { + "target": { + "wordIndex": 1, + "letterIndex": 2 + }, + "cursor": { + "wordIndex": 0, + "letterIndex": 0 + }, + "nextIndex": 34, + "words": [] + }, + { + "target": { + "wordIndex": 3, + "letterIndex": 0 + }, + "cursor": { + "wordIndex": 0, + "letterIndex": 0 + }, + "nextIndex": 34, + "words": [] + } + ] + }, + { + "zen": true, + "words": [ + [104, 101, 108, 108, 111, 32], + [116, 104, 101, 114, 101, 32], + [122, 101, 110, 10], + [109, 111, 100, 101, 32], + [114, 111, 99, 107, 115] + ], + "steps": [ + { + "action": "correctLetter", + "value": [104], + "time": 0 + }, + { + "action": "correctLetter", + "value": [101], + "time": 106.6 + }, + { + "action": "correctLetter", + "value": [108], + "time": 225.1 + }, + { + "action": "correctLetter", + "value": [108], + "time": 326.1 + }, + { + "action": "correctLetter", + "value": [111], + "time": 491.8 + }, + { + "action": "correctLetter", + "value": [32], + "time": 674.2 + }, + { + "action": "submitCorrectWord", + "time": 853.2 + }, + { + "action": "correctLetter", + "value": [116], + "time": 853.2 + }, + { + "action": "correctLetter", + "value": [104], + "time": 1002.7 + }, + { + "action": "correctLetter", + "value": [101], + "time": 1144.7 + }, + { + "action": "correctLetter", + "value": [114], + "time": 1323.3 + }, + { + "action": "correctLetter", + "value": [101], + "time": 1531.3 + }, + { + "action": "correctLetter", + "value": [32], + "time": 1662.7 + }, + { + "action": "submitCorrectWord", + "time": 1785.1 + }, + { + "action": "correctLetter", + "value": [122], + "time": 1785.1 + }, + { + "action": "correctLetter", + "value": [101], + "time": 1938.5 + }, + { + "action": "correctLetter", + "value": [110], + "time": 2083.9 + }, + { + "action": "correctLetter", + "value": [10], + "time": 2233.7 + }, + { + "action": "submitCorrectWord", + "time": 2415.4 + }, + { + "action": "correctLetter", + "value": [109], + "time": 2415.4 + }, + { + "action": "correctLetter", + "value": [111], + "time": 2601.4 + }, + { + "action": "correctLetter", + "value": [101], + "time": 2730.9 + }, + { + "action": "correctLetter", + "value": [100], + "time": 2827.9 + }, + { + "action": "setLetterIndex", + "value": 3, + "time": 2948.5 + }, + { + "action": "setLetterIndex", + "value": 2, + "time": 3055.7 + }, + { + "action": "correctLetter", + "value": [100], + "time": 3251.7 + }, + { + "action": "correctLetter", + "value": [101], + "time": 3381.3 + }, + { + "action": "correctLetter", + "value": [32], + "time": 3551.1 + }, + { + "action": "submitCorrectWord", + "time": 3644.4 + }, + { + "action": "correctLetter", + "value": [114], + "time": 3644.4 + }, + { + "action": "correctLetter", + "value": [111], + "time": 3854.4 + }, + { + "action": "correctLetter", + "value": [99], + "time": 4001.1 + }, + { + "action": "correctLetter", + "value": [107], + "time": 4176 + }, + { + "action": "correctLetter", + "value": [115], + "time": 4381.6 + } + ], + "final": [ + { + "letters": [ + { + "char": [104], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [116], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [104], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [122], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [10], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [109], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [100], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [114], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [99], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [107], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + } + ], + "cursor": { + "wordIndex": 4, + "letterIndex": 5 + }, + "sounds": [ + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click", + "click" + ], + "seeks": [ + { + "target": { + "wordIndex": 0, + "letterIndex": 0 + }, + "cursor": { + "wordIndex": 0, + "letterIndex": 0 + }, + "nextIndex": 0, + "words": [ + { + "letters": [ + { + "char": [104], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [116], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [104], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [122], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [10], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [109], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [100], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [99], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [107], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + } + ] + }, + { + "target": { + "wordIndex": 1, + "letterIndex": 2 + }, + "cursor": { + "wordIndex": 1, + "letterIndex": 2 + }, + "nextIndex": 9, + "words": [ + { + "letters": [ + { + "char": [104], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [116], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [104], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [122], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [10], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [109], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [100], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [99], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [107], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + } + ] + }, + { + "target": { + "wordIndex": 3, + "letterIndex": 0 + }, + "cursor": { + "wordIndex": 3, + "letterIndex": 0 + }, + "nextIndex": 19, + "words": [ + { + "letters": [ + { + "char": [104], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [108], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [116], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [104], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [114], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [122], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [110], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [10], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [109], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [100], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [114], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [111], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [99], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [107], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [115], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + } + ] + } + ] + } + ], + "practice": [ + { + "missed": "off", + "slow": false, + "result": null, + "notifications": [] + }, + { + "missed": "off", + "slow": true, + "result": null, + "notifications": [] + }, + { + "missed": "words", + "slow": false, + "result": null, + "notifications": [] + }, + { + "missed": "words", + "slow": true, + "result": null, + "notifications": [] + }, + { + "missed": "biwords", + "slow": false, + "result": null, + "notifications": [] + }, + { + "missed": "biwords", + "slow": true, + "result": null, + "notifications": [] + } + ], + "validity": [ + { + "input": { + "result": { + "mode": "zen", + "mode2": "zen", + "testDuration": 4.56, + "wpm": 68.42, + "rawWpm": 68.42, + "acc": 100 + }, + "bailedOut": false, + "failed": false, + "repeated": false, + "lbOptOut": false, + "customLimit": { + "mode": "word", + "value": 10 + } + }, + "reason": "too short" + }, + { + "input": { + "result": { + "mode": "zen", + "mode2": "zen", + "testDuration": 4.56, + "wpm": 68.42, + "rawWpm": 68.42, + "acc": 100 + }, + "bailedOut": false, + "failed": false, + "repeated": true, + "lbOptOut": false, + "customLimit": { + "mode": "word", + "value": 10 + } + }, + "reason": "too short" + }, + { + "input": { + "result": { + "mode": "zen", + "mode2": "zen", + "testDuration": 4.56, + "wpm": 68.42, + "rawWpm": 68.42, + "acc": 100 + }, + "bailedOut": false, + "failed": true, + "repeated": false, + "lbOptOut": false, + "customLimit": { + "mode": "word", + "value": 10 + } + }, + "reason": "failed" + }, + { + "input": { + "result": { + "mode": "zen", + "mode2": "zen", + "testDuration": 4.56, + "wpm": 68.42, + "rawWpm": 68.42, + "acc": 100 + }, + "bailedOut": false, + "failed": true, + "repeated": true, + "lbOptOut": false, + "customLimit": { + "mode": "word", + "value": 10 + } + }, + "reason": "failed" + }, + { + "input": { + "result": { + "mode": "zen", + "mode2": "zen", + "testDuration": 4.56, + "wpm": 68.42, + "rawWpm": 68.42, + "acc": 100 + }, + "bailedOut": true, + "failed": false, + "repeated": false, + "lbOptOut": false, + "customLimit": { + "mode": "word", + "value": 10 + } + }, + "reason": "too short" + }, + { + "input": { + "result": { + "mode": "zen", + "mode2": "zen", + "testDuration": 4.56, + "wpm": 68.42, + "rawWpm": 68.42, + "acc": 100 + }, + "bailedOut": true, + "failed": false, + "repeated": true, + "lbOptOut": false, + "customLimit": { + "mode": "word", + "value": 10 + } + }, + "reason": "too short" + }, + { + "input": { + "result": { + "mode": "zen", + "mode2": "zen", + "testDuration": 4.56, + "wpm": 68.42, + "rawWpm": 68.42, + "acc": 100 + }, + "bailedOut": true, + "failed": true, + "repeated": false, + "lbOptOut": false, + "customLimit": { + "mode": "word", + "value": 10 + } + }, + "reason": "failed" + }, + { + "input": { + "result": { + "mode": "zen", + "mode2": "zen", + "testDuration": 4.56, + "wpm": 68.42, + "rawWpm": 68.42, + "acc": 100 + }, + "bailedOut": true, + "failed": true, + "repeated": true, + "lbOptOut": false, + "customLimit": { + "mode": "word", + "value": 10 + } + }, + "reason": "failed" + } + ] + } +] From 3c8961672394118b42d1409e4a582c4bf912d9f4 Mon Sep 17 00:00:00 2001 From: Lakshmi Tanmay Date: Fri, 23 Oct 2026 04:17:51 +0530 Subject: [PATCH 32/93] Port full results, histories, validity, practice and historical replay --- crates/typing-engine/src/analysis.rs | 120 +++++++++ crates/typing-engine/src/lib.rs | 4 + crates/typing-engine/src/practice.rs | 118 +++++++++ crates/typing-engine/src/replay.rs | 307 +++++++++++++++++++++++ crates/typing-engine/src/results.rs | 175 +++++++++++++ crates/typing-engine/src/stats.rs | 209 ++++++++++++++- crates/typing-engine/tests/analysis.rs | 107 ++++++++ crates/typing-engine/tests/statistics.rs | 65 +++++ docs/rust-engine/progress.md | 4 + 9 files changed, 1104 insertions(+), 5 deletions(-) create mode 100644 crates/typing-engine/src/analysis.rs create mode 100644 crates/typing-engine/src/practice.rs create mode 100644 crates/typing-engine/src/replay.rs create mode 100644 crates/typing-engine/src/results.rs create mode 100644 crates/typing-engine/tests/analysis.rs diff --git a/crates/typing-engine/src/analysis.rs b/crates/typing-engine/src/analysis.rs new file mode 100644 index 000000000..f7369edc7 --- /dev/null +++ b/crates/typing-engine/src/analysis.rs @@ -0,0 +1,120 @@ +use crate::{ + Text, practice, replay, results, + stats::{self, Log}, +}; +use serde::{Deserialize, Serialize}; +use serde_json::Value; +use ts_rs::TS; +#[derive(Deserialize, TS)] +#[serde(tag = "op", rename_all = "camelCase")] +pub enum Query { + Stats { + now: f64, + }, + Completed { + context: Box, + }, + Validity { + input: results::Validity, + }, + Replay { + zen: bool, + }, + Frame { + time: f64, + }, + Practice { + words: Vec, + missed: String, + slow: bool, + now: f64, + }, +} +#[derive(Serialize, TS)] +#[serde(tag = "op", rename_all = "camelCase")] +pub enum Response { + Stats { + stats: Box, + }, + Completed { + result: Value, + }, + Validity { + reason: Option, + afk: bool, + }, + Replay { + words: Vec, + steps: Vec, + duration: f64, + }, + Frame { + frame: replay::Frame, + }, + Practice { + selection: practice::Selection, + }, +} +#[derive(Serialize, TS)] +#[serde(rename_all = "camelCase")] +pub struct Overview { + pub summary: Value, + pub input_history: Vec, + pub corrected_words_history: Vec, + pub missed_words: Vec<(Text, usize)>, + pub word_burst_history: Vec, + pub raw_history: Vec, + pub keypresses_per_second: Vec, + pub incomplete_test_seconds: f64, + pub boundaries: Vec, + pub lagged_boundaries: Vec, + pub boundary_labels: Vec, + pub date_duration_ms: f64, + pub word_indexes_per_second: Vec>, +} +pub fn analyze(log: &Log, query: Query) -> Response { + match query { + Query::Stats { now } => Response::Stats { + stats: Box::new(Overview { + summary: stats::summary(log), + input_history: stats::input_history(log), + corrected_words_history: stats::corrected_history(log), + missed_words: stats::missed_words(log), + word_burst_history: stats::word_burst_history(log, now), + raw_history: stats::speed_history(log, true), + keypresses_per_second: stats::keypresses_per_second(log), + incomplete_test_seconds: stats::incomplete_seconds(log), + boundaries: stats::boundaries(log), + lagged_boundaries: stats::lagged_boundaries(log), + boundary_labels: stats::boundary_labels(log, true), + date_duration_ms: results::date_duration(log), + word_indexes_per_second: (0..stats::boundaries(log).len()) + .map(|index| stats::word_indexes_for_second(log, index)) + .collect(), + }), + }, + Query::Completed { context } => Response::Completed { + result: results::completed(log, &context), + }, + Query::Validity { input } => Response::Validity { + reason: results::invalid_reason(log, &input).map(String::from), + afk: results::is_afk(log, input.bailed_out), + }, + Query::Replay { zen } => Response::Replay { + words: replay::words(log, zen), + steps: replay::steps(log, zen), + duration: replay::duration(log), + }, + Query::Frame { time } => Response::Frame { + frame: replay::frame(log, time), + }, + Query::Practice { + words, + missed, + slow, + now, + } => Response::Practice { + selection: practice::select(log, &words, &missed, slow, now), + }, + } +} diff --git a/crates/typing-engine/src/lib.rs b/crates/typing-engine/src/lib.rs index 6b5d95438..ba683b9fb 100644 --- a/crates/typing-engine/src/lib.rs +++ b/crates/typing-engine/src/lib.rs @@ -1,11 +1,15 @@ //! Platform-independent typing rules. Hosts provide content, observations and time. +pub mod analysis; mod generated_funboxes; pub mod generation; pub mod language_rules; pub mod pace; +pub mod practice; pub mod punctuation; pub mod quotes; pub mod random; +pub mod replay; +pub mod results; pub mod session; pub mod stats; pub mod text; diff --git a/crates/typing-engine/src/practice.rs b/crates/typing-engine/src/practice.rs new file mode 100644 index 000000000..9029082b9 --- /dev/null +++ b/crates/typing-engine/src/practice.rs @@ -0,0 +1,118 @@ +use crate::{ + Text, js_round, + stats::{self, Log}, +}; +use serde::{Deserialize, Serialize}; +use ts_rs::TS; +#[derive(Serialize, Deserialize, TS)] +#[serde(rename_all = "camelCase")] +pub struct PracticeWords { + pub text: Vec, + pub section_limit: usize, +} +#[derive(Serialize, TS)] +pub struct Selection { + pub result: Option, + pub notifications: Vec, +} +pub fn select(log: &Log, words: &[Text], missed: &str, slow: bool, now: f64) -> Selection { + let mut selection = Selection { + result: None, + notifications: vec![], + }; + if log.context.mode == "zen" { + return selection; + } + let limit = if (missed == "words" && !slow) || (missed == "off" && slow) { + 20 + } else { + 10 + }; + let missed_words = stats::missed_words(log); + let mut missed_items = if missed == "words" { + missed_words.clone() + } else { + vec![] + }; + missed_items.sort_by_key(|a| std::cmp::Reverse(a.1)); + missed_items.truncate(limit); + let mut biwords = vec![]; + if missed == "biwords" { + for (index, word) in words.iter().enumerate() { + if let Some((_, count)) = missed_words.iter().find(|(text, _)| text == word) { + biwords.push(( + word.clone(), + index + .checked_sub(1) + .and_then(|index| words.get(index)) + .cloned() + .unwrap_or_default(), + *count, + )); + } + } + biwords.sort_by_key(|a| std::cmp::Reverse(a.2)); + biwords.truncate(limit); + } + if ((missed == "words" && missed_items.is_empty()) + || (missed == "biwords" && biwords.is_empty())) + && !slow + { + selection + .notifications + .push("You haven't missed any words".into()); + return selection; + } + let mut slow_items = vec![]; + if slow { + // Array.slice(0,-1) on an empty history still slices the input words. + let history_len = stats::input_history(log).len(); + let length = if history_len == 0 { + words.len().saturating_sub(1) + } else { + (history_len - 1).min(words.len()) + }; + let bursts = stats::word_burst_history(log, now); + slow_items = words + .iter() + .take(length) + .enumerate() + .map(|(index, word)| (word.clone(), bursts.get(index).copied().unwrap_or(0.0))) + .collect(); + slow_items.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(std::cmp::Ordering::Equal)); + slow_items.truncate(limit.min(js_round(length as f64 * 0.2) as usize)); + if slow_items.is_empty() { + selection + .notifications + .push("Test too short to classify slow words.".into()); + } + } + if missed_items.is_empty() && biwords.is_empty() && slow_items.is_empty() { + selection + .notifications + .push("Could not start a new custom test".into()); + return selection; + } + let mut text = vec![]; + for (word, count) in &missed_items { + text.extend(std::iter::repeat_n(word.clone(), *count)); + } + for (word, previous, count) in &biwords { + let mut word = word.clone(); + if !previous.is_empty() { + let mut joined = previous.clone(); + joined.0.push(32); + joined.0.extend(word.0); + word = joined; + } + text.extend(std::iter::repeat_n(word, *count)); + } + for (index, (word, _)) in slow_items.iter().enumerate() { + text.extend(std::iter::repeat_n(word.clone(), slow_items.len() - index)); + } + selection.result = Some(PracticeWords { + text, + section_limit: (slow_items.len() + missed_items.len() + biwords.len()) * 5, + }); + selection +} diff --git a/crates/typing-engine/src/replay.rs b/crates/typing-engine/src/replay.rs new file mode 100644 index 000000000..3cdb4248a --- /dev/null +++ b/crates/typing-engine/src/replay.rs @@ -0,0 +1,307 @@ +//! Historical playback preserves legacy UTF-16 event positions. Prompt letters +//! use JS Array.from code-point iteration, including its indexing mismatch. +use crate::{ + Text, + stats::{Log, input, text, word_index}, +}; +use serde::{Deserialize, Serialize}; +use ts_rs::TS; + +#[derive(Clone, Debug, Serialize, Deserialize, TS)] +#[serde(rename_all = "camelCase")] +pub struct Frame { + pub inputs: Vec>, + pub active_index: usize, +} +pub fn frame(log: &Log, time: f64) -> Frame { + let mut frame = Frame { + inputs: vec![], + active_index: 0, + }; + for event in &log.events { + if event.kind != "input" || event.test_ms > time { + continue; + } + let Some(index) = word_index(event) else { + continue; + }; + frame.inputs.resize(frame.inputs.len().max(index + 1), None); + frame.inputs[index] = Some(text(&event.data["inputValue"])); + if event.data["clearedNextWord"] == true { + frame.inputs.resize(frame.inputs.len().max(index + 2), None); + frame.inputs[index + 1] = Some(Text::default()); + } + frame.active_index = index; + if event.data["commitsWord"] == true + && event.data["lastWord"] != true + && event.data["inputStopped"] != true + { + frame.active_index += 1; + } + } + frame +} +pub fn duration(log: &Log) -> f64 { + log.events + .iter() + .fold(0.0f64, |value, event| value.max(event.test_ms)) +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, TS)] +#[serde(rename_all = "camelCase")] +pub enum Action { + CorrectLetter, + IncorrectLetter, + BackWord, + SubmitCorrectWord, + SubmitErrorWord, + SetLetterIndex, +} +#[derive(Clone, Debug, Serialize, Deserialize, TS)] +#[serde(untagged)] +pub enum StepValue { + Text(Text), + Index(usize), +} +#[derive(Clone, Debug, Serialize, Deserialize, TS)] +pub struct Step { + pub action: Action, + #[serde(skip_serializing_if = "Option::is_none")] + #[ts(optional)] + pub value: Option, + pub time: f64, +} +pub fn words(log: &Log, zen: bool) -> Vec { + if zen { + crate::stats::input_history(log) + } else { + log.context.target_words.clone() + } +} +pub fn steps(log: &Log, zen: bool) -> Vec { + let mut steps = vec![]; + let mut previous = None; + for event in &log.events { + if event.kind != "input" { + continue; + } + let Some(index) = word_index(event) else { + continue; + }; + if let Some(previous) = previous + && previous != index + { + let action = if index > previous { + let events: Vec<_> = log + .events + .iter() + .filter(|e| word_index(e) == Some(previous)) + .collect(); + let typed = input(&events); + if zen || log.context.target_words.get(previous) == Some(&typed) { + Action::SubmitCorrectWord + } else { + Action::SubmitErrorWord + } + } else { + Action::BackWord + }; + steps.push(Step { + action, + time: event.test_ms, + value: None, + }); + } + if event.data["inputType"] == "insertText" + || event.data["inputType"] == "insertCompositionText" + { + if event.data["inputStopped"] != true { + steps.push(Step { + action: if event.data["correct"] == true { + Action::CorrectLetter + } else { + Action::IncorrectLetter + }, + value: Some(StepValue::Text(text(&event.data["data"]))), + time: event.test_ms, + }); + } + } else if (event.data["inputType"] == "deleteContentBackward" + || event.data["inputType"] == "deleteWordBackward") + && !previous.is_some_and(|previous| index < previous) + { + let length = if event.data.get("inputValue").is_some() { + text(&event.data["inputValue"]).len() + } else { + event.data["charIndex"].as_u64().unwrap_or(0) as usize + }; + steps.push(Step { + action: Action::SetLetterIndex, + value: Some(StepValue::Index(length)), + time: event.test_ms, + }); + } + previous = Some(index); + } + steps +} +#[derive(Clone, Debug, Serialize, Deserialize, TS)] +pub struct Letter { + #[serde(rename = "char")] + pub character: Text, + pub correct: bool, + pub incorrect: bool, + pub extra: bool, +} +#[derive(Clone, Debug, Serialize, Deserialize, TS)] +pub struct Word { + pub letters: Vec, + pub error: bool, +} +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize, TS)] +#[serde(rename_all = "camelCase")] +pub struct Cursor { + pub word_index: i32, + pub letter_index: usize, +} +pub fn prompt(words: &[Text], steps: &[Step]) -> Vec { + let count = steps.iter().fold(0i32, |count, step| match step.action { + Action::BackWord => count - 1, + Action::SubmitCorrectWord | Action::SubmitErrorWord => count + 1, + _ => count, + }); + words + .iter() + .take((count + 1).max(0) as usize) + .map(|word| { + let mut letters = vec![]; + let mut index = 0; + while index < word.len() { + let width = if (0xd800..=0xdbff).contains(&word.0[index]) + && word + .0 + .get(index + 1) + .is_some_and(|unit| (0xdc00..=0xdfff).contains(unit)) + { + 2 + } else { + 1 + }; + letters.push(Letter { + character: Text(word.0[index..index + width].to_vec()), + correct: false, + incorrect: false, + extra: false, + }); + index += width; + } + Word { + letters, + error: false, + } + }) + .collect() +} +pub fn apply(words: &mut [Word], cursor: &mut Cursor, step: &Step) -> Option<&'static str> { + let word = words.get_mut(usize::try_from(cursor.word_index).ok()?)?; + match step.action { + Action::CorrectLetter => { + if let Some(letter) = word.letters.get_mut(cursor.letter_index) { + letter.correct = true; + } + cursor.letter_index += 1; + Some("click") + } + Action::IncorrectLetter => { + if cursor.letter_index >= word.letters.len() { + let character = match &step.value { + Some(StepValue::Text(text)) => text.clone(), + Some(StepValue::Index(index)) => Text::from(index.to_string().as_str()), + None => Text::default(), + }; + word.letters.push(Letter { + character, + correct: false, + incorrect: false, + extra: true, + }); + } + if let Some(letter) = word.letters.get_mut(cursor.letter_index) { + letter.incorrect = true; + } + cursor.letter_index += 1; + Some("error") + } + Action::SetLetterIndex => { + let Some(StepValue::Index(index)) = step.value else { + return None; + }; + cursor.letter_index = index; + let mut position = 0; + word.letters.retain_mut(|letter| { + let before = position < index; + position += 1; + if before { + return true; + } + if letter.extra { + return false; + } + letter.correct = false; + letter.incorrect = false; + true + }); + Some("click") + } + Action::SubmitCorrectWord | Action::SubmitErrorWord => { + if step.action == Action::SubmitErrorWord { + word.error = true; + } + cursor.word_index += 1; + cursor.letter_index = 0; + Some(if step.action == Action::SubmitErrorWord { + "error" + } else { + "click" + }) + } + Action::BackWord => { + cursor.word_index -= 1; + let Some(word) = usize::try_from(cursor.word_index) + .ok() + .and_then(|index| words.get_mut(index)) + else { + return Some("click"); + }; + cursor.letter_index = word.letters.len(); + while cursor.letter_index > 0 + && word + .letters + .get(cursor.letter_index - 1) + .is_some_and(|letter| !letter.correct && !letter.incorrect && !letter.extra) + { + cursor.letter_index -= 1; + } + word.error = false; + Some("click") + } + } +} +#[derive(Serialize, TS)] +#[serde(rename_all = "camelCase")] +pub struct Seek { + pub cursor: Cursor, + pub next_index: usize, +} +pub fn seek(words: &mut [Word], steps: &[Step], target: Cursor) -> Seek { + let mut cursor = Cursor::default(); + let mut next_index = 0; + for (index, step) in steps.iter().enumerate() { + if cursor.word_index < target.word_index + || (cursor.word_index == target.word_index && cursor.letter_index < target.letter_index) + { + apply(words, &mut cursor, step); + next_index = index + 1; + } + } + Seek { cursor, next_index } +} diff --git a/crates/typing-engine/src/results.rs b/crates/typing-engine/src/results.rs new file mode 100644 index 000000000..55a07ea55 --- /dev/null +++ b/crates/typing-engine/src/results.rs @@ -0,0 +1,175 @@ +use crate::{ + Config, round2, + stats::{self, Log}, +}; +use serde::{Deserialize, Serialize}; +use serde_json::{Value, json}; +use ts_rs::TS; + +#[derive(Clone, Debug, Deserialize, Serialize, TS)] +pub struct QuoteRef { + pub id: u32, + pub group: usize, +} +#[derive(Clone, Debug, Deserialize, Serialize, TS)] +#[serde(rename_all = "camelCase")] +pub struct CompletedContext { + pub client: Option, + pub offline: Option, + pub config: Config, + pub current_quote: Option, + pub custom_text: Option, + pub tags: Vec, + pub bailed_out: bool, + pub restart_count: u32, + pub incomplete_tests: Vec, + pub incomplete_seconds: f64, + pub timestamp: f64, +} +pub fn mode2(config: &Config, quote: Option<&QuoteRef>) -> String { + match config.mode.as_str() { + "time" => config.time.to_string(), + "words" => config.words.to_string(), + "quote" => quote.map_or_else(|| "-1".into(), |quote| quote.id.to_string()), + other => other.into(), + } +} +pub fn completed(log: &Log, context: &CompletedContext) -> Value { + let config = &context.config; + let mut value = stats::summary(log); + let object = value.as_object_mut().expect("summary object"); + let metadata = json!({ + "client": context.client.as_deref().unwrap_or("web"), + "language": if config.mode == "quote" { crate::quotes::remove_language_size(&config.language) } else { config.language.clone() }, + "tags": context.tags, "punctuation": config.punctuation, "numbers": config.numbers, + "lazyMode": config.lazy_mode, "timestamp": context.timestamp, "mode": config.mode, + "mode2": mode2(config,context.current_quote.as_ref()), "bailedOut": context.bailed_out, + "funbox": config.funbox, "difficulty": config.difficulty, "blindMode": config.blind_mode, + "stopOnLetter": config.stop_on_error == "letter", "restartCount": context.restart_count, + "incompleteTests": context.incomplete_tests, + "incompleteTestSeconds": if context.incomplete_seconds < 0.0 { 0.0 } else { round2(context.incomplete_seconds) } + }); + object.extend(metadata.as_object().expect("metadata object").clone()); + if let Some(offline) = context.offline { + object.insert("offline".into(), json!(offline)); + } + if config.mode == "custom" + && let Some(custom) = &context.custom_text + { + object.insert("customText".into(), custom.clone()); + } + if config.mode == "quote" { + object.insert( + "quoteLength".into(), + json!( + context + .current_quote + .as_ref() + .map_or(-1, |quote| quote.group as i64) + ), + ); + } + value +} +#[derive(Clone, Debug, Deserialize, Serialize, TS)] +#[serde(rename_all = "camelCase")] +pub struct ValidityResult { + pub mode: String, + pub mode2: String, + pub test_duration: f64, + pub wpm: f64, + pub raw_wpm: f64, + pub acc: f64, +} +#[derive(Clone, Debug, Deserialize, Serialize, TS)] +pub struct Limit { + pub mode: String, + pub value: f64, +} +#[derive(Clone, Debug, Deserialize, Serialize, TS)] +#[serde(rename_all = "camelCase")] +pub struct Validity { + pub result: ValidityResult, + pub bailed_out: bool, + pub failed: bool, + pub repeated: bool, + pub lb_opt_out: bool, + pub custom_limit: Limit, +} +pub fn date_duration(log: &Log) -> f64 { + let find = |kind: &str| { + log.events + .iter() + .find(|event| event.kind == "timer" && event.data["event"] == kind) + .and_then(|event| event.data["date"].as_f64()) + }; + match (find("start"), find("end")) { + (Some(start), Some(end)) => end - start, + _ => 0.0, + } +} +pub fn is_afk(log: &Log, bailed_out: bool) -> bool { + !bailed_out + && stats::keypresses_per_second(log) + .iter() + .rev() + .take(5) + .all(|count| *count == 0) +} +pub fn invalid_reason(log: &Log, input: &Validity) -> Option<&'static str> { + let result = &input.result; + let mode = result.mode.as_str(); + // JS parseInt consumes an initial signed integer and ignores a suffix. + let mut chars = result.mode2.trim_start().chars().peekable(); + let mut number = String::new(); + if chars.peek().is_some_and(|c| *c == '-' || *c == '+') { + number.push(chars.next().unwrap()); + } + number.extend(chars.take_while(char::is_ascii_digit)); + let mode2 = number.parse::().unwrap_or(f64::NAN); + let date = date_duration(log) / 1000.0; + if mode == "time" + && !input.bailed_out + && (result.test_duration < date - 0.1 || result.test_duration > date + 0.1) + && result.test_duration <= 120.0 + { + return Some("inconsistent duration"); + } + if input.failed { + return Some("failed"); + } + if result.test_duration < 1.0 + || (mode == "time" && mode2 < 15.0 && mode2 > 0.0) + || (mode == "time" && mode2 == 0.0 && result.test_duration < 15.0) + || (mode == "words" && mode2 < 10.0 && mode2 > 0.0) + || (mode == "words" && mode2 == 0.0 && result.test_duration < 15.0) + || (mode == "custom" + && ((["word", "section"].contains(&input.custom_limit.mode.as_str()) + && input.custom_limit.value < 10.0) + || (input.custom_limit.mode == "time" && input.custom_limit.value < 15.0))) + || (mode == "zen" && result.test_duration < 15.0) + { + return Some("too short"); + } + if is_afk(log, input.bailed_out) { + return Some("afk"); + } + if input.repeated { + return Some("repeated"); + } + let invalid_speed = |speed: f64| { + speed < 0.0 + || (speed > 350.0 && mode != "words" && result.mode2 != "10") + || (speed > 420.0 && mode == "words" && result.mode2 == "10") + }; + if invalid_speed(result.wpm) { + return Some("wpm"); + } + if invalid_speed(result.raw_wpm) { + return Some("raw"); + } + if result.acc < if input.lb_opt_out { 50.0 } else { 75.0 } || result.acc > 100.0 { + return Some("accuracy"); + } + None +} diff --git a/crates/typing-engine/src/stats.rs b/crates/typing-engine/src/stats.rs index 425ce7f94..bca645bc6 100644 --- a/crates/typing-engine/src/stats.rs +++ b/crates/typing-engine/src/stats.rs @@ -6,8 +6,9 @@ use crate::{ }; use serde::{Deserialize, Serialize}; use serde_json::{Value, json}; +use ts_rs::TS; -#[derive(Clone, Debug, Deserialize, Serialize)] +#[derive(Clone, Debug, Deserialize, Serialize, TS)] #[serde(rename_all = "camelCase")] pub struct Context { pub target_words: Vec, @@ -20,7 +21,7 @@ pub struct Context { pub custom_text_limit_mode: Option, pub custom_text_limit_value: Option, } -#[derive(Clone, Debug, Deserialize, Serialize)] +#[derive(Clone, Debug, Deserialize, Serialize, TS)] pub struct Log { pub version: u32, pub events: Vec, @@ -59,7 +60,7 @@ pub fn input(events: &[&Event]) -> Text { } value } -fn per_word(log: &Log, limit: Option) -> Vec<(usize, Vec<&Event>)> { +pub(crate) fn per_word(log: &Log, limit: Option) -> Vec<(usize, Vec<&Event>)> { let mut words: Vec<(usize, Vec<&Event>)> = vec![]; for event in &log.events { if limit.is_some_and(|limit| event.test_ms > limit) { @@ -95,7 +96,7 @@ pub fn timed(log: &Log) -> bool { && (log.context.custom_text_limit_mode.as_deref() == Some("time") || log.context.custom_text_limit_value == Some(0.0))) } -#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize)] +#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, TS)] #[serde(rename_all = "camelCase")] pub struct Chars { pub all_correct: usize, @@ -382,11 +383,16 @@ pub fn summary(log: &Log) -> Value { let mut held: Vec<(String, f64, usize)> = vec![]; let mut overlap = 0.0; let mut overlap_start = None; + let start = log + .events + .iter() + .find(|event| event.kind == "timer" && event.data["event"] == "start") + .map(|event| event.test_ms); let first = log .events .iter() .find(|event| event.kind == "keydown") - .map(|event| event.test_ms.max(0.0)) + .map(|event| (event.test_ms - start.unwrap_or(event.test_ms)).max(0.0)) .unwrap_or(0.0); for event in &log.events { let code = event.data["code"].as_str().unwrap_or_default().to_owned(); @@ -423,3 +429,196 @@ pub fn summary(log: &Log) -> Value { "consistency":consistency(&burst),"wpmConsistency":consistency(&speed),"keyConsistency":consistency(&spacing[..spacing.len().saturating_sub(1)]), "chartData":{"wpm":speed,"burst":burst,"err":errors},"keySpacing":spacing,"keyDuration":durations,"keyOverlap":round2(overlap)}) } + +pub fn input_history(log: &Log) -> Vec { + let words = per_word(log, None); + words + .iter() + .map(|(index, events)| { + let Some(last) = events.last() else { + return Text::default(); + }; + let abandoned = words + .iter() + .find(|(previous, _)| *previous + 1 == *index) + .is_some_and(|(_, previous)| { + previous.iter().any(|event| { + event.test_ms > last.test_ms && event.data["clearedNextWord"] == true + }) + }); + if abandoned { + Text::default() + } else { + input(events) + } + }) + .collect() +} +pub fn missed_words(log: &Log) -> Vec<(Text, usize)> { + let mut words: Vec<(Text, usize)> = vec![]; + for event in &log.events { + if event.kind != "input" + || event.data["inputType"] != "insertText" + || event.data["correct"] == true + { + continue; + } + let Some(word) = word_index(event).and_then(|index| log.context.target_words.get(index)) + else { + continue; + }; + let mut word = word.clone(); + if matches!(word.0.last(), Some(32 | 10)) { + word.0.pop(); + } + if let Some((_, count)) = words.iter_mut().find(|(text, _)| text == &word) { + *count += 1; + } else { + words.push((word, 1)); + } + } + // JS Object.keys: canonical uint32 integer keys precede insertion order. + words.sort_by_key(|(text, _)| { + let value = text.display_lossy(); + value + .parse::() + .ok() + .filter(|n| *n < u32::MAX && n.to_string() == value) + .map_or((true, 0), |n| (false, n)) + }); + words +} +pub fn corrected_history(log: &Log) -> Vec { + per_word(log, None) + .into_iter() + .map(|(_, events)| { + let mut current: Vec> = vec![]; + let mut corrected: Vec> = vec![]; + let mut cursor = 0; + for event in events { + if event.kind != "input" { + continue; + } + if event.data["inputType"] == "insertText" + || event.data["inputType"] == "insertCompositionText" + { + if event.data["inputStopped"] == true { + continue; + } + current.resize(current.len().max(cursor + 1), None); + current[cursor] = Some(text(&event.data["data"])); + cursor += 1; + } else if event.data["inputType"] == "deleteContentBackward" + || event.data["inputType"] == "deleteWordBackward" + { + let all = event.data["inputType"] == "deleteWordBackward"; + while cursor > 0 { + cursor -= 1; + corrected.resize(corrected.len().max(cursor + 1), None); + corrected[cursor] = current + .get(cursor) + .cloned() + .flatten() + .or(Some(Text::default())); + if !all { + break; + } + } + } + } + Text( + current + .iter() + .enumerate() + .flat_map(|(index, value)| { + corrected + .get(index) + .and_then(Option::as_ref) + .or(value.as_ref()) + .map_or(vec![], |text| text.0.clone()) + }) + .collect(), + ) + }) + .collect() +} +pub fn word_burst_history(log: &Log, now: f64) -> Vec { + (0..per_word(log, None).len()) + .map(|index| word_burst(log, index, now)) + .collect() +} +pub fn word_indexes_for_second(log: &Log, second: usize) -> Vec { + let boundaries = boundaries(log); + let Some(boundary) = boundaries.get(second) else { + return vec![]; + }; + let previous = second + .checked_sub(1) + .and_then(|index| boundaries.get(index)); + let mut words = vec![]; + for event in &log.events { + if previous.is_some_and(|previous| event.test_ms <= *previous) { + continue; + } + if event.test_ms > *boundary { + break; + } + if let Some(index) = word_index(event) + && !words.contains(&index) + { + words.push(index); + } + } + words +} + +pub fn lagged_boundaries(log: &Log) -> Vec { + let mut boundaries: Vec<_> = log + .events + .iter() + .filter(|event| event.kind == "timer" && event.data["event"] == "step") + .map(|event| event.test_ms) + .collect(); + let has_end = log + .events + .iter() + .any(|event| event.kind == "timer" && event.data["event"] == "end"); + let end = adjusted_end(log); + if has_end && (log.context.mode == "zen" || log.context.bailed_out) { + while boundaries.last().is_some_and(|boundary| *boundary > end) { + boundaries.pop(); + } + } + if has_end && !timed(log) && js_round(round2(end / 1000.0) % 1.0) >= 0.5 { + boundaries.push(end); + } + boundaries +} +pub fn boundary_labels(log: &Log, show_lag: bool) -> Vec { + let boundaries = boundaries(log); + boundaries + .iter() + .enumerate() + .map(|(index, boundary)| { + if index + 1 == boundaries.len() + && (boundary - (index + 1) as f64 * 1000.0).abs() > 100.0 + { + return format!("{:.2}", round2(boundary / 1000.0)); + } + let previous = index.checked_sub(1).map_or(0.0, |index| boundaries[index]); + if show_lag + && log.events.iter().any(|event| { + event.kind == "timer" + && event.data["event"] == "step" + && event.data["catchup"] == true + && event.test_ms > previous + && event.test_ms <= *boundary + }) + { + "LAG".into() + } else { + (index + 1).to_string() + } + }) + .collect() +} diff --git a/crates/typing-engine/tests/analysis.rs b/crates/typing-engine/tests/analysis.rs new file mode 100644 index 000000000..c4e4d70d9 --- /dev/null +++ b/crates/typing-engine/tests/analysis.rs @@ -0,0 +1,107 @@ +use serde_json::{Value, json}; +use typing_engine::{Text, replay, stats::Log}; +fn equal(a: &Value, b: &Value, path: &str) { + match (a, b) { + (Value::Number(a), Value::Number(b)) => assert_eq!(a.as_f64(), b.as_f64(), "{path}"), + (Value::Array(a), Value::Array(b)) => { + assert_eq!(a.len(), b.len(), "{path}"); + for (i, (a, b)) in a.iter().zip(b).enumerate() { + equal(a, b, &format!("{path}[{i}]")); + } + } + (Value::Object(a), Value::Object(b)) => { + assert_eq!(a.len(), b.len(), "{path}"); + for (key, a) in a { + equal(a, &b[key], &format!("{path}.{key}")); + } + } + _ => assert_eq!(a, b, "{path}"), + } +} +#[test] +fn replay_seeking_practice_and_validity_match_frozen_typescript_oracle() { + let root = + std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("../../packages/typing-fixtures"); + let references: Vec = + serde_json::from_slice(&std::fs::read(root.join("oracle/analysis.json")).unwrap()).unwrap(); + for reference in references { + let name = reference["name"].as_str().unwrap(); + let fixture: Value = serde_json::from_slice( + &std::fs::read(root.join(format!("reference/keystrokes/{name}.json"))).unwrap(), + ) + .unwrap(); + let mut log = fixture["eventLog"].clone(); + log["context"]["targetWords"] = json!( + log["context"]["targetWords"] + .as_array() + .unwrap() + .iter() + .map(|word| Text::from(word.as_str().unwrap())) + .collect::>() + ); + let log: Log = serde_json::from_value(log).unwrap(); + for frame in reference["frames"].as_array().unwrap() { + let actual = replay::frame(&log, frame["time"].as_f64().unwrap()); + equal(&json!(actual.inputs), &frame["inputs"], name); + equal(&json!(actual.active_index), &frame["activeIndex"], name); + } + for expected in reference["replay"].as_array().unwrap() { + let words = replay::words(&log, expected["zen"].as_bool().unwrap()); + let steps = replay::steps(&log, expected["zen"].as_bool().unwrap()); + equal(&json!(words), &expected["words"], name); + equal(&json!(steps), &expected["steps"], name); + let mut prompt = replay::prompt(&words, &steps); + let mut cursor = replay::Cursor::default(); + let sounds: Vec<_> = steps + .iter() + .map(|step| replay::apply(&mut prompt, &mut cursor, step)) + .collect(); + equal(&json!(sounds), &expected["sounds"], name); + equal(&json!(prompt), &expected["final"], name); + equal(&json!(cursor), &expected["cursor"], name); + for expected in expected["seeks"].as_array().unwrap() { + let mut prompt = replay::prompt(&words, &steps); + let target = serde_json::from_value(expected["target"].clone()).unwrap(); + let actual = replay::seek(&mut prompt, &steps, target); + equal(&json!(actual.cursor), &expected["cursor"], name); + equal(&json!(actual.next_index), &expected["nextIndex"], name); + equal(&json!(prompt), &expected["words"], name); + } + } + let words: Vec<_> = log + .context + .target_words + .iter() + .map(|word| { + let mut word = word.clone(); + if matches!(word.0.last(), Some(32 | 10)) { + word.0.pop(); + } + word + }) + .collect(); + for expected in reference["practice"].as_array().unwrap() { + let actual = typing_engine::practice::select( + &log, + &words, + expected["missed"].as_str().unwrap(), + expected["slow"].as_bool().unwrap(), + 0.0, + ); + equal(&json!(actual.result), &expected["result"], name); + equal( + &json!(actual.notifications), + &expected["notifications"], + name, + ); + } + for expected in reference["validity"].as_array().unwrap() { + let input = serde_json::from_value(expected["input"].clone()).unwrap(); + equal( + &json!(typing_engine::results::invalid_reason(&log, &input)), + &expected["reason"], + name, + ); + } + } +} diff --git a/crates/typing-engine/tests/statistics.rs b/crates/typing-engine/tests/statistics.rs index 1782afcf1..984fcc383 100644 --- a/crates/typing-engine/tests/statistics.rs +++ b/crates/typing-engine/tests/statistics.rs @@ -43,6 +43,71 @@ fn summary_fields_match_all_unchanged_browser_results_exactly() { .collect::>() ); let log: Log = serde_json::from_value(log).unwrap(); + + let snapshot: Value = serde_json::from_slice( + &std::fs::read( + path.parent() + .unwrap() + .join("__snapshots__") + .join(path.file_name().unwrap()), + ) + .unwrap(), + ) + .unwrap(); + let derived = &snapshot["derived"]; + let decode = + |words: Vec| json!(words.iter().map(Text::display_lossy).collect::>()); + equal( + &decode(typing_engine::stats::input_history(&log)), + &derived["inputHistory"], + "inputHistory", + ); + equal( + &decode(typing_engine::stats::corrected_history(&log)), + &derived["correctedWordsHistory"], + "correctedHistory", + ); + let missed: serde_json::Map = typing_engine::stats::missed_words(&log) + .into_iter() + .map(|(word, count)| (word.display_lossy(), json!(count))) + .collect(); + equal(&json!(missed), &derived["missedWords"], "missedWords"); + equal( + &json!(typing_engine::stats::word_burst_history(&log, 0.0)), + &derived["wordBurstHistory"], + "wordBurstHistory", + ); + equal( + &json!(typing_engine::stats::speed_history(&log, true)), + &derived["rawHistory"], + "rawHistory", + ); + equal( + &json!(typing_engine::stats::keypresses_per_second(&log)), + &derived["keypressesPerSecond"], + "keypressesPerSecond", + ); + equal( + &json!(typing_engine::stats::incomplete_seconds(&log)), + &derived["incompleteTestSeconds"], + "incompleteSeconds", + ); + + let recorded = &fixture["completedEvent"]; + let context = json!({ + "config": fixture["config"], "client": null, "offline": null, + "currentQuote": if recorded["mode"] == "quote" { json!({"id": recorded["mode2"].as_str().unwrap().parse::().unwrap(), "group": recorded["quoteLength"]}) } else { Value::Null }, + "customText": recorded["customText"], "tags": recorded["tags"], "bailedOut": recorded["bailedOut"], + "restartCount": recorded["restartCount"], "incompleteTests": recorded["incompleteTests"], + "incompleteSeconds": recorded["incompleteTestSeconds"], "timestamp": recorded["timestamp"] + }); + let context = serde_json::from_value(context).unwrap(); + equal( + &typing_engine::results::completed(&log, &context), + recorded, + &format!("{}.completed", path.display()), + ); + let actual = summary(&log); for (key, actual) in actual.as_object().unwrap() { equal( diff --git a/docs/rust-engine/progress.md b/docs/rust-engine/progress.md index 20a0bbc25..40d0392e4 100644 --- a/docs/rust-engine/progress.md +++ b/docs/rust-engine/progress.md @@ -79,3 +79,7 @@ do not rerun completed checks without a new reason. - Current Cloudflare production build passes. Real-browser `engine-proof.migration.ts`: 2 tests pass (19 command traces and 61 generator snapshots). Generated binding types now cover content preparation and generator outputs. - Expanded Wasm: 1,123,350 bytes raw (over 1 MiB budget), ~292.77 kB gzip (under budget). Optimization required; size gate explicitly not passed. + +- Pinned Binaryen 132.0.0 optimization reduces expanded Wasm to 660,285 bytes raw / 261,217 bytes gzip. Both payload limits pass; optimized browser rerun still required. +- Session and generation now share one Rust Config/default implementation. +- All full CompletedEvents and historical derived arrays match original recordings. New one-time analysis capture checks replay frames, steps, final prompt/sounds, seeking, all practice combinations and validity flags against unchanged TypeScript. Native analysis regression passes for all eight recordings. From ab3483d6b158ec2c6435bb5236dbbf21b33fba82 Mon Sep 17 00:00:00 2001 From: Lakshmi Tanmay Date: Fri, 23 Oct 2026 04:20:29 +0530 Subject: [PATCH 33/93] Expose analysis bindings and verify historical results through browser Wasm --- crates/typing-engine-wasm/src/lib.rs | 9 ++ .../typing-engine/src/bin/export-bindings.rs | 5 + docs/rust-engine/progress.md | 3 + frontend/e2e/engine-proof.migration.ts | 113 ++++++++++++++++++ frontend/src/routes/engine-proof.tsx | 30 +++++ packages/typing-engine/src/browser.client.ts | 16 +++ 6 files changed, 176 insertions(+) diff --git a/crates/typing-engine-wasm/src/lib.rs b/crates/typing-engine-wasm/src/lib.rs index 575bfcb1d..70b26c533 100644 --- a/crates/typing-engine-wasm/src/lib.rs +++ b/crates/typing-engine-wasm/src/lib.rs @@ -131,3 +131,12 @@ impl WasmContent { self.content = None; } } + +#[wasm_bindgen] +pub fn analyze(log: &str, query: &str) -> Result { + let log = serde_json::from_str(log)?; + let query = serde_json::from_str(query)?; + Ok(serde_json::to_string(&typing_engine::analysis::analyze( + &log, query, + ))?) +} diff --git a/crates/typing-engine/src/bin/export-bindings.rs b/crates/typing-engine/src/bin/export-bindings.rs index d1ba1f025..c069bc4f4 100644 --- a/crates/typing-engine/src/bin/export-bindings.rs +++ b/crates/typing-engine/src/bin/export-bindings.rs @@ -12,6 +12,11 @@ fn main() -> Result<(), Box> { Config::export_all(&config)?; State::export_all(&config)?; Update::export_all(&config)?; + typing_engine::stats::Log::export_all(&config)?; + typing_engine::analysis::Query::export_all(&config)?; + typing_engine::analysis::Response::export_all(&config)?; + typing_engine::replay::Word::export_all(&config)?; + typing_engine::replay::Seek::export_all(&config)?; typing_engine::generation::Prepare::export_all(&config)?; typing_engine::generation::Generated::export_all(&config)?; typing_engine::generation::NextWord::export_all(&config)?; diff --git a/docs/rust-engine/progress.md b/docs/rust-engine/progress.md index 40d0392e4..62a0e67fe 100644 --- a/docs/rust-engine/progress.md +++ b/docs/rust-engine/progress.md @@ -83,3 +83,6 @@ do not rerun completed checks without a new reason. - Pinned Binaryen 132.0.0 optimization reduces expanded Wasm to 660,285 bytes raw / 261,217 bytes gzip. Both payload limits pass; optimized browser rerun still required. - Session and generation now share one Rust Config/default implementation. - All full CompletedEvents and historical derived arrays match original recordings. New one-time analysis capture checks replay frames, steps, final prompt/sounds, seeking, all practice combinations and validity flags against unchanged TypeScript. Native analysis regression passes for all eight recordings. + +- Optimized real Cloudflare browser proof now passes 3 tests: all 19 command traces, all 61 generators, and all eight historical result/replay/practice references. Native full tests and Clippy pass. +- Adding full analysis exports increases optimized Wasm to 812,970 bytes raw / 321,266 bytes gzip (level 9). Raw passes; gzip exceeds 307,200-byte budget. Additional optimization is required. `--converge` and `--gufa` experiments did not resolve the excess; no budget was relaxed. diff --git a/frontend/e2e/engine-proof.migration.ts b/frontend/e2e/engine-proof.migration.ts index 70161d2c8..01e92c598 100644 --- a/frontend/e2e/engine-proof.migration.ts +++ b/frontend/e2e/engine-proof.migration.ts @@ -230,3 +230,116 @@ test("all 61 unchanged generator snapshots match browser Wasm", async ({ expect(actual[index], fixture.name).toEqual(expected[index]); } }); + +test("historical results, replay and practice agree with frozen references in Wasm", async ({ + page, +}) => { + const references = JSON.parse( + await readFile(resolve(root, "oracle/analysis.json"), "utf8"), + ) as { + name: string; + frames: Record[]; + replay: { zen: boolean; words: unknown; steps: unknown }[]; + practice: { + missed: string; + slow: boolean; + result: unknown; + notifications: string[]; + }[]; + validity: { input: unknown; reason: string | null }[]; + }[]; + const units = (text: string): number[] => + Array.from({ length: text.length }, (_, i) => text.charCodeAt(i)); + const inputs = []; + const expected = []; + for (const reference of references) { + const fixture = JSON.parse( + await readFile( + resolve(root, `reference/keystrokes/${reference.name}.json`), + "utf8", + ), + ) as { + config: Record; + eventLog: { + context: { targetWords: string[] }; + events: { testMs: number }[]; + }; + completedEvent: Record; + }; + const recorded = fixture.completedEvent; + const context = { + config: fixture.config, + currentQuote: + recorded["mode"] === "quote" + ? { id: Number(recorded["mode2"]), group: recorded["quoteLength"] } + : null, + customText: recorded["customText"], + tags: recorded["tags"], + bailedOut: recorded["bailedOut"], + restartCount: recorded["restartCount"], + incompleteTests: recorded["incompleteTests"], + incompleteSeconds: recorded["incompleteTestSeconds"], + timestamp: recorded["timestamp"], + }; + const log = { + ...fixture.eventLog, + context: { + ...fixture.eventLog.context, + targetWords: fixture.eventLog.context.targetWords.map(units), + }, + }; + const queries: unknown[] = [{ op: "completed", context }]; + const outputs: unknown[] = [{ op: "completed", result: recorded }]; + for (const frame of reference.frames) { + const { time, ...state } = frame; + queries.push({ op: "frame", time }); + outputs.push({ op: "frame", frame: state }); + } + for (const replay of reference.replay) { + queries.push({ op: "replay", zen: replay.zen }); + outputs.push({ + op: "replay", + words: replay.words, + steps: replay.steps, + duration: Math.max( + 0, + ...fixture.eventLog.events.map((event) => event.testMs), + ), + }); + } + const words = fixture.eventLog.context.targetWords.map((word) => + units(word.replace(/[ \n]$/, "")), + ); + for (const practice of reference.practice) { + queries.push({ + op: "practice", + words, + missed: practice.missed, + slow: practice.slow, + now: 0, + }); + outputs.push({ + op: "practice", + selection: { + result: practice.result, + notifications: practice.notifications, + }, + }); + } + inputs.push({ log, queries }); + expected.push(outputs); + } + await page.goto("/engine-proof"); + await page + .getByRole("textbox", { name: "Command fixture" }) + .fill(JSON.stringify(inputs)); + await page.getByRole("button", { name: "Run analysis" }).click(); + await expect(page.getByTestId("engine-proof-result")).not.toBeEmpty(); + await expect(page.getByTestId("engine-proof-error")).toBeEmpty(); + const actual = JSON.parse( + await page.getByTestId("engine-proof-result").innerText(), + ) as unknown[]; + for (const [index, reference] of references.entries()) { + expect(actual[index], reference.name).toEqual(expected[index]); + } +}); diff --git a/frontend/src/routes/engine-proof.tsx b/frontend/src/routes/engine-proof.tsx index 30c5d64fe..2be0a2438 100644 --- a/frontend/src/routes/engine-proof.tsx +++ b/frontend/src/routes/engine-proof.tsx @@ -4,6 +4,8 @@ import type { Config, Text, Prepare, + Log, + Query, } from "@oxytype/typing-engine/browser.client"; import { createFileRoute } from "@tanstack/solid-router"; @@ -152,6 +154,26 @@ function EngineProof() { setFailure(error instanceof Error ? error.message : String(error)); } }; + const runAnalysis = async (): Promise => { + setResult(""); + setFailure(""); + try { + const inputs = JSON.parse(fixture()) as { log: Log; queries: Query[] }[]; + const { loadEngine, analyzeLog } = + await import("@oxytype/typing-engine/browser.client"); + await loadEngine(); + if (disposed) return; + setResult( + JSON.stringify( + inputs.map(({ log, queries }) => + queries.map((query) => analyzeLog(log, query)), + ), + ), + ); + } catch (error) { + setFailure(error instanceof Error ? error.message : String(error)); + } + }; return ( Run generation +
{result()}
{failure()}
{benchmark()}
diff --git a/packages/typing-engine/src/browser.client.ts b/packages/typing-engine/src/browser.client.ts index 6cf68679d..2830e7d84 100644 --- a/packages/typing-engine/src/browser.client.ts +++ b/packages/typing-engine/src/browser.client.ts @@ -1,5 +1,6 @@ import initialize, { binding_version, + analyze, WasmSession, WasmContent, } from "../generated/engine.js"; @@ -13,6 +14,11 @@ import type { Prepare } from "../generated/types/Prepare"; import type { Generated } from "../generated/types/Generated"; import type { NextWord } from "../generated/types/NextWord"; import type { Quote } from "../generated/types/Quote"; +import type { Query } from "../generated/types/Query"; +import type { Response } from "../generated/types/Response"; +import type { Log } from "../generated/types/Log"; +export type { Query, Response, Log }; + export type { Command, Config, @@ -175,3 +181,13 @@ export class BrowserContent { handle.free(); } } + +/** Derivation only: transport IDs, hashes and network policy stay with the host. */ +export function analyzeLog( + log: Log, + query: Q, +): Extract { + return JSON.parse( + analyze(JSON.stringify(log), JSON.stringify(query)), + ) as Extract; +} From 6e3373cfad3e63e4a02552a1e2bb12a8e6169593 Mon Sep 17 00:00:00 2001 From: Lakshmi Tanmay Date: Fri, 23 Oct 2026 04:22:32 +0530 Subject: [PATCH 34/93] Preserve exhaustive Morse compatibility with a smaller immutable lookup --- Cargo.lock | 16 - crates/typing-engine/Cargo.toml | 1 - crates/typing-engine/data/morse.rs | 495 ++++++++++++++++++ crates/typing-engine/src/transforms.rs | 26 +- crates/typing-engine/tests/transforms.rs | 24 + docs/rust-engine/decisions.md | 2 +- docs/rust-engine/progress.md | 5 +- .../scripts/capture-morse-oracle.ts | 33 ++ .../typing-fixtures/oracle/morse-unicode.json | 7 + 9 files changed, 575 insertions(+), 34 deletions(-) create mode 100644 crates/typing-engine/data/morse.rs create mode 100644 packages/typing-core/scripts/capture-morse-oracle.ts create mode 100644 packages/typing-fixtures/oracle/morse-unicode.json diff --git a/Cargo.lock b/Cargo.lock index 5e868d204..c1c7beee1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -238,12 +238,6 @@ dependencies = [ "syn 3.0.9", ] -[[package]] -name = "tinyvec" -version = "1.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd3ca314f692efd6c868f8408f53fe444634a845f96c028b97d35f6a1f79f0ee" - [[package]] name = "ts-rs" version = "12.0.1" @@ -274,7 +268,6 @@ dependencies = [ "serde", "serde_json", "ts-rs", - "unicode-normalization", ] [[package]] @@ -294,15 +287,6 @@ version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2c754d6c33795a1c324727428e5a7dedb5b06195f9890bdbcba760d3e246563" -[[package]] -name = "unicode-normalization" -version = "0.1.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" -dependencies = [ - "tinyvec", -] - [[package]] name = "wasm-bindgen" version = "0.2.129" diff --git a/crates/typing-engine/Cargo.toml b/crates/typing-engine/Cargo.toml index d16b0b11d..363ba8801 100644 --- a/crates/typing-engine/Cargo.toml +++ b/crates/typing-engine/Cargo.toml @@ -10,7 +10,6 @@ repository.workspace = true serde.workspace = true serde_json.workspace = true ts-rs.workspace = true -unicode-normalization = "=0.1.24" [lints] workspace = true diff --git a/crates/typing-engine/data/morse.rs b/crates/typing-engine/data/morse.rs new file mode 100644 index 000000000..4d0ceabca --- /dev/null +++ b/crates/typing-engine/data/morse.rs @@ -0,0 +1,495 @@ +// Mechanically captured from the frozen TypeScript getMorse, Unicode 17.0. +&[ + (192, ".-/"), + (193, ".-/"), + (194, ".-/"), + (195, ".-/"), + (196, ".-/"), + (197, ".-/"), + (199, "-.-./"), + (200, "./"), + (201, "./"), + (202, "./"), + (203, "./"), + (204, "../"), + (205, "../"), + (206, "../"), + (207, "../"), + (209, "-./"), + (210, "---/"), + (211, "---/"), + (212, "---/"), + (213, "---/"), + (214, "---/"), + (217, "..-/"), + (218, "..-/"), + (219, "..-/"), + (220, "..-/"), + (221, "-.--/"), + (224, ".-/"), + (225, ".-/"), + (226, ".-/"), + (227, ".-/"), + (228, ".-/"), + (229, ".-/"), + (231, "-.-./"), + (232, "./"), + (233, "./"), + (234, "./"), + (235, "./"), + (236, "../"), + (237, "../"), + (238, "../"), + (239, "../"), + (241, "-./"), + (242, "---/"), + (243, "---/"), + (244, "---/"), + (245, "---/"), + (246, "---/"), + (249, "..-/"), + (250, "..-/"), + (251, "..-/"), + (252, "..-/"), + (253, "-.--/"), + (255, "-.--/"), + (256, ".-/"), + (257, ".-/"), + (258, ".-/"), + (259, ".-/"), + (260, ".-/"), + (261, ".-/"), + (262, "-.-./"), + (263, "-.-./"), + (264, "-.-./"), + (265, "-.-./"), + (266, "-.-./"), + (267, "-.-./"), + (268, "-.-./"), + (269, "-.-./"), + (270, "-../"), + (271, "-../"), + (274, "./"), + (275, "./"), + (276, "./"), + (277, "./"), + (278, "./"), + (279, "./"), + (280, "./"), + (281, "./"), + (282, "./"), + (283, "./"), + (284, "--./"), + (285, "--./"), + (286, "--./"), + (287, "--./"), + (288, "--./"), + (289, "--./"), + (290, "--./"), + (291, "--./"), + (292, "..../"), + (293, "..../"), + (296, "../"), + (297, "../"), + (298, "../"), + (299, "../"), + (300, "../"), + (301, "../"), + (302, "../"), + (303, "../"), + (304, "../"), + (308, ".---/"), + (309, ".---/"), + (310, "-.-/"), + (311, "-.-/"), + (313, ".-../"), + (314, ".-../"), + (315, ".-../"), + (316, ".-../"), + (317, ".-../"), + (318, ".-../"), + (323, "-./"), + (324, "-./"), + (325, "-./"), + (326, "-./"), + (327, "-./"), + (328, "-./"), + (332, "---/"), + (333, "---/"), + (334, "---/"), + (335, "---/"), + (336, "---/"), + (337, "---/"), + (340, ".-./"), + (341, ".-./"), + (342, ".-./"), + (343, ".-./"), + (344, ".-./"), + (345, ".-./"), + (346, ".../"), + (347, ".../"), + (348, ".../"), + (349, ".../"), + (350, ".../"), + (351, ".../"), + (352, ".../"), + (353, ".../"), + (354, "-/"), + (355, "-/"), + (356, "-/"), + (357, "-/"), + (360, "..-/"), + (361, "..-/"), + (362, "..-/"), + (363, "..-/"), + (364, "..-/"), + (365, "..-/"), + (366, "..-/"), + (367, "..-/"), + (368, "..-/"), + (369, "..-/"), + (370, "..-/"), + (371, "..-/"), + (372, ".--/"), + (373, ".--/"), + (374, "-.--/"), + (375, "-.--/"), + (376, "-.--/"), + (377, "--../"), + (378, "--../"), + (379, "--../"), + (380, "--../"), + (381, "--../"), + (382, "--../"), + (416, "---/"), + (417, "---/"), + (431, "..-/"), + (432, "..-/"), + (461, ".-/"), + (462, ".-/"), + (463, "../"), + (464, "../"), + (465, "---/"), + (466, "---/"), + (467, "..-/"), + (468, "..-/"), + (469, "..-/"), + (470, "..-/"), + (471, "..-/"), + (472, "..-/"), + (473, "..-/"), + (474, "..-/"), + (475, "..-/"), + (476, "..-/"), + (478, ".-/"), + (479, ".-/"), + (480, ".-/"), + (481, ".-/"), + (486, "--./"), + (487, "--./"), + (488, "-.-/"), + (489, "-.-/"), + (490, "---/"), + (491, "---/"), + (492, "---/"), + (493, "---/"), + (496, ".---/"), + (500, "--./"), + (501, "--./"), + (504, "-./"), + (505, "-./"), + (506, ".-/"), + (507, ".-/"), + (512, ".-/"), + (513, ".-/"), + (514, ".-/"), + (515, ".-/"), + (516, "./"), + (517, "./"), + (518, "./"), + (519, "./"), + (520, "../"), + (521, "../"), + (522, "../"), + (523, "../"), + (524, "---/"), + (525, "---/"), + (526, "---/"), + (527, "---/"), + (528, ".-./"), + (529, ".-./"), + (530, ".-./"), + (531, ".-./"), + (532, "..-/"), + (533, "..-/"), + (534, "..-/"), + (535, "..-/"), + (536, ".../"), + (537, ".../"), + (538, "-/"), + (539, "-/"), + (542, "..../"), + (543, "..../"), + (550, ".-/"), + (551, ".-/"), + (552, "./"), + (553, "./"), + (554, "---/"), + (555, "---/"), + (556, "---/"), + (557, "---/"), + (558, "---/"), + (559, "---/"), + (560, "---/"), + (561, "---/"), + (562, "-.--/"), + (563, "-.--/"), + (894, "-.-.-./"), + (7680, ".-/"), + (7681, ".-/"), + (7682, "-.../"), + (7683, "-.../"), + (7684, "-.../"), + (7685, "-.../"), + (7686, "-.../"), + (7687, "-.../"), + (7688, "-.-./"), + (7689, "-.-./"), + (7690, "-../"), + (7691, "-../"), + (7692, "-../"), + (7693, "-../"), + (7694, "-../"), + (7695, "-../"), + (7696, "-../"), + (7697, "-../"), + (7698, "-../"), + (7699, "-../"), + (7700, "./"), + (7701, "./"), + (7702, "./"), + (7703, "./"), + (7704, "./"), + (7705, "./"), + (7706, "./"), + (7707, "./"), + (7708, "./"), + (7709, "./"), + (7710, "..-./"), + (7711, "..-./"), + (7712, "--./"), + (7713, "--./"), + (7714, "..../"), + (7715, "..../"), + (7716, "..../"), + (7717, "..../"), + (7718, "..../"), + (7719, "..../"), + (7720, "..../"), + (7721, "..../"), + (7722, "..../"), + (7723, "..../"), + (7724, "../"), + (7725, "../"), + (7726, "../"), + (7727, "../"), + (7728, "-.-/"), + (7729, "-.-/"), + (7730, "-.-/"), + (7731, "-.-/"), + (7732, "-.-/"), + (7733, "-.-/"), + (7734, ".-../"), + (7735, ".-../"), + (7736, ".-../"), + (7737, ".-../"), + (7738, ".-../"), + (7739, ".-../"), + (7740, ".-../"), + (7741, ".-../"), + (7742, "--/"), + (7743, "--/"), + (7744, "--/"), + (7745, "--/"), + (7746, "--/"), + (7747, "--/"), + (7748, "-./"), + (7749, "-./"), + (7750, "-./"), + (7751, "-./"), + (7752, "-./"), + (7753, "-./"), + (7754, "-./"), + (7755, "-./"), + (7756, "---/"), + (7757, "---/"), + (7758, "---/"), + (7759, "---/"), + (7760, "---/"), + (7761, "---/"), + (7762, "---/"), + (7763, "---/"), + (7764, ".--./"), + (7765, ".--./"), + (7766, ".--./"), + (7767, ".--./"), + (7768, ".-./"), + (7769, ".-./"), + (7770, ".-./"), + (7771, ".-./"), + (7772, ".-./"), + (7773, ".-./"), + (7774, ".-./"), + (7775, ".-./"), + (7776, ".../"), + (7777, ".../"), + (7778, ".../"), + (7779, ".../"), + (7780, ".../"), + (7781, ".../"), + (7782, ".../"), + (7783, ".../"), + (7784, ".../"), + (7785, ".../"), + (7786, "-/"), + (7787, "-/"), + (7788, "-/"), + (7789, "-/"), + (7790, "-/"), + (7791, "-/"), + (7792, "-/"), + (7793, "-/"), + (7794, "..-/"), + (7795, "..-/"), + (7796, "..-/"), + (7797, "..-/"), + (7798, "..-/"), + (7799, "..-/"), + (7800, "..-/"), + (7801, "..-/"), + (7802, "..-/"), + (7803, "..-/"), + (7804, "...-/"), + (7805, "...-/"), + (7806, "...-/"), + (7807, "...-/"), + (7808, ".--/"), + (7809, ".--/"), + (7810, ".--/"), + (7811, ".--/"), + (7812, ".--/"), + (7813, ".--/"), + (7814, ".--/"), + (7815, ".--/"), + (7816, ".--/"), + (7817, ".--/"), + (7818, "-..-/"), + (7819, "-..-/"), + (7820, "-..-/"), + (7821, "-..-/"), + (7822, "-.--/"), + (7823, "-.--/"), + (7824, "--../"), + (7825, "--../"), + (7826, "--../"), + (7827, "--../"), + (7828, "--../"), + (7829, "--../"), + (7830, "..../"), + (7831, "-/"), + (7832, ".--/"), + (7833, "-.--/"), + (7840, ".-/"), + (7841, ".-/"), + (7842, ".-/"), + (7843, ".-/"), + (7844, ".-/"), + (7845, ".-/"), + (7846, ".-/"), + (7847, ".-/"), + (7848, ".-/"), + (7849, ".-/"), + (7850, ".-/"), + (7851, ".-/"), + (7852, ".-/"), + (7853, ".-/"), + (7854, ".-/"), + (7855, ".-/"), + (7856, ".-/"), + (7857, ".-/"), + (7858, ".-/"), + (7859, ".-/"), + (7860, ".-/"), + (7861, ".-/"), + (7862, ".-/"), + (7863, ".-/"), + (7864, "./"), + (7865, "./"), + (7866, "./"), + (7867, "./"), + (7868, "./"), + (7869, "./"), + (7870, "./"), + (7871, "./"), + (7872, "./"), + (7873, "./"), + (7874, "./"), + (7875, "./"), + (7876, "./"), + (7877, "./"), + (7878, "./"), + (7879, "./"), + (7880, "../"), + (7881, "../"), + (7882, "../"), + (7883, "../"), + (7884, "---/"), + (7885, "---/"), + (7886, "---/"), + (7887, "---/"), + (7888, "---/"), + (7889, "---/"), + (7890, "---/"), + (7891, "---/"), + (7892, "---/"), + (7893, "---/"), + (7894, "---/"), + (7895, "---/"), + (7896, "---/"), + (7897, "---/"), + (7898, "---/"), + (7899, "---/"), + (7900, "---/"), + (7901, "---/"), + (7902, "---/"), + (7903, "---/"), + (7904, "---/"), + (7905, "---/"), + (7906, "---/"), + (7907, "---/"), + (7908, "..-/"), + (7909, "..-/"), + (7910, "..-/"), + (7911, "..-/"), + (7912, "..-/"), + (7913, "..-/"), + (7914, "..-/"), + (7915, "..-/"), + (7916, "..-/"), + (7917, "..-/"), + (7918, "..-/"), + (7919, "..-/"), + (7920, "..-/"), + (7921, "..-/"), + (7922, "-.--/"), + (7923, "-.--/"), + (7924, "-.--/"), + (7925, "-.--/"), + (7926, "-.--/"), + (7927, "-.--/"), + (7928, "-.--/"), + (7929, "-.--/"), + (8490, "-.-/"), + (8491, ".-/"), + (8800, "-...-/"), +] diff --git a/crates/typing-engine/src/transforms.rs b/crates/typing-engine/src/transforms.rs index 26a00073d..0efa94906 100644 --- a/crates/typing-engine/src/transforms.rs +++ b/crates/typing-engine/src/transforms.rs @@ -196,7 +196,6 @@ pub fn alter( } fn morse(word: Text) -> Text { - use unicode_normalization::UnicodeNormalization; const MAP: &[(&str, &str)] = &[ ("a", ".-"), ("b", "-..."), @@ -253,21 +252,18 @@ fn morse(word: Text) -> Text { ("!", "-.-.--"), ("@", ".--.-."), ]; - // A surrogate cannot map to Morse; replacement also cannot map. Preserve - // JS's original UTF-16 loop bound even if lowercasing expands the text. - let normalized = Text::from( - word.display_lossy() - .nfd() - .filter(|c| !('\u{0300}'..='\u{036f}').contains(c)) - .collect::() - .as_str(), - ); - let lower = case(&normalized, false); + const EXTENDED: &[(u32, &str)] = include!("../data/morse.rs"); let mut output = String::new(); - for unit in lower.0.iter().take(normalized.len()) { - if let Some((_, code)) = MAP.iter().find(|(key, _)| key.encode_utf16().eq([*unit])) { - output.push_str(code); - output.push('/'); + for character in char::decode_utf16(word.0.iter().copied()).flatten() { + let code = u32::from(character); + if code <= 127 { + let lower = character.to_ascii_lowercase(); + if let Some((_, morse)) = MAP.iter().find(|(key, _)| key.chars().eq([lower])) { + output.push_str(morse); + output.push('/'); + } + } else if let Ok(index) = EXTENDED.binary_search_by_key(&code, |(code, _)| *code) { + output.push_str(EXTENDED[index].1); } } Text::from(output.as_str()) diff --git a/crates/typing-engine/tests/transforms.rs b/crates/typing-engine/tests/transforms.rs index eccbcc9ed..abc93de15 100644 --- a/crates/typing-engine/tests/transforms.rs +++ b/crates/typing-engine/tests/transforms.rs @@ -27,3 +27,27 @@ fn funbox_transforms_preserve_frozen_unicode_and_control_character_behavior() { assert_eq!(actual, case.expected, "{} {:?}", case.name, case.word); } } + +#[test] +fn morse_matches_javascript_for_every_scalar_and_isolated_utf16_unit() { + use typing_engine::{Text, random::Random, transforms::alter}; + let mut hash = 14695981039346656037_u64; + let mut count = 0; + let mut record = |input: Text| { + let output = alter("morse", input, 0, 1, &mut Random::seeded(0)).unwrap(); + for unit in std::iter::once(output.len() as u16).chain(output.0) { + hash = (hash ^ u64::from(unit)).wrapping_mul(1099511628211); + count += 1; + } + }; + for unit in 0..=u16::MAX { + record(Text(vec![unit])); + } + for code in 0..=0x10ffff { + if let Some(character) = char::from_u32(code) { + record(Text(character.to_string().encode_utf16().collect())); + } + } + assert_eq!(count, 1182044); + assert_eq!(hash, 0xcbd60b424acd4b15); +} diff --git a/docs/rust-engine/decisions.md b/docs/rust-engine/decisions.md index 9a133fc22..43ff5a83e 100644 --- a/docs/rust-engine/decisions.md +++ b/docs/rust-engine/decisions.md @@ -21,4 +21,4 @@ Quote queues belong to an explicit `Quotes` instance; loading the same language Pseudolang retains UTF-16 prefix and JS property enumeration semantics. Its legacy unbounded loop is bounded at 65,536 draws with an explicit error; no random fallback. Individual source words and generation requests are bounded at 1,000,000 UTF-16 units/words. Hosts must surface failures; never silently alter generated content. -`accents.json` and `british-english.json` are mechanical copies of existing GPL-3.0 project transformation tables, not regenerated reference snapshots. `unicode-normalization` supplies canonical NFD for Morse; only U+0300–036F marks are removed, preserving the original rule. +`accents.json` and `british-english.json` are mechanical copies of existing GPL-3.0 project transformation tables, not regenerated reference snapshots. Morse uses a compact immutable mapping mechanically captured from the original Node 24 / Unicode 17.0 algorithm. The native exhaustive oracle covers every Unicode scalar and every isolated UTF-16 unit (1,182,044 output units, digest cbd60b424acd4b15). This preserves normalization behavior without bundling unrelated Unicode normalization tables. diff --git a/docs/rust-engine/progress.md b/docs/rust-engine/progress.md index 62a0e67fe..07ea207fc 100644 --- a/docs/rust-engine/progress.md +++ b/docs/rust-engine/progress.md @@ -23,7 +23,7 @@ Phase 1.1 and 1.2 gates passed: inventory/baselines, lossless contract, frozen o | 1.3 Native / browser proof | Complete | Native 7 traces; Cloudflare real-browser finite slice; MIME/CSP, lifecycle, boundary benchmark. Budgets frozen. | | 1.4 Input / lifecycle / timing | Active | 19 native/browser command traces; 8 original recordings. Composition, batch failure, deferred work, visible-word bounds and browser deletion observations covered. Full generator/session integration remains open. | | 1.5 Generation / configuration | Active | 61/61 unchanged generator snapshots. Content requests, configuration validation and generator/session integration pending. | -| 1.6 Events / results / replay | Active | All computed summary fields match 8 original CompletedEvents exactly. Full results, eligibility, replay pending. | +| 1.6 Events / results / replay | Active | All computed summary fields match 8 original CompletedEvents exactly. Full results, eligibility, histories and replay pass native/browser references; malformed/historical edge coverage and incremental playback facade pending. | | 1.7 Binding / engine gate | Pending | Full three-path parity and budgets. | | 2.1–2.6 Website | Pending | Requires Stage 1 gate. | | 3.1–3.5 Terminal | Pending | Requires Stage 2 gate. | @@ -86,3 +86,6 @@ do not rerun completed checks without a new reason. - Optimized real Cloudflare browser proof now passes 3 tests: all 19 command traces, all 61 generators, and all eight historical result/replay/practice references. Native full tests and Clippy pass. - Adding full analysis exports increases optimized Wasm to 812,970 bytes raw / 321,266 bytes gzip (level 9). Raw passes; gzip exceeds 307,200-byte budget. Additional optimization is required. `--converge` and `--gufa` experiments did not resolve the excess; no budget was relaxed. + +- Replaced general-purpose Morse normalization with a 492-entry immutable compatibility map captured from original TypeScript (Node 24.21.0 / Unicode 17.0). Exhaustive native parity passes every Unicode scalar and every isolated UTF-16 unit; original 61 generator snapshots still pass. No reference was regenerated. +- Expanded engine with analysis: 709,637 bytes raw / 271,030 gzip level 6 after optimization, within both payload budgets. Full browser rerun after this change remains pending. diff --git a/packages/typing-core/scripts/capture-morse-oracle.ts b/packages/typing-core/scripts/capture-morse-oracle.ts new file mode 100644 index 000000000..00178b54e --- /dev/null +++ b/packages/typing-core/scripts/capture-morse-oracle.ts @@ -0,0 +1,33 @@ +import { writeFileSync } from "node:fs"; +import { resolve } from "node:path"; +import { getMorse } from "../src/generate"; +const mappings: [number, string][] = []; +let hash = 14695981039346656037n; +let units = 0; +function record(value: string): void { + const output = getMorse(value); + const values = [output.length, ...Array.from(output, (c) => c.charCodeAt(0))]; + units += values.length; + for (const unit of values) { + hash = BigInt.asUintN(64, (hash ^ BigInt(unit)) * 1099511628211n); + } +} +for (let unit = 0; unit <= 0xffff; unit++) record(String.fromCharCode(unit)); +for (let code = 0; code <= 0x10ffff; code++) { + if (code >= 0xd800 && code <= 0xdfff) continue; + const value = String.fromCodePoint(code); + const output = getMorse(value); + if (code > 127 && output !== "") mappings.push([code, output]); + record(value); +} +const root = resolve(import.meta.dirname, "../../.."); +writeFileSync( + resolve(root, "crates/typing-engine/data/morse.rs"), + `// Mechanically captured from the frozen TypeScript getMorse, Unicode ${process.versions["unicode"]}.\n&[\n${mappings.map(([code, value]) => ` (${code}, ${JSON.stringify(value)}),`).join("\n")}\n]\n`, + { flag: "wx" }, +); +writeFileSync( + resolve(root, "packages/typing-fixtures/oracle/morse-unicode.json"), + `${JSON.stringify({ source: `TypeScript getMorse; Node ${process.version}`, unicode: process.versions["unicode"], units, hash: hash.toString(16), mappingCount: mappings.length }, null, 2)}\n`, + { flag: "wx" }, +); diff --git a/packages/typing-fixtures/oracle/morse-unicode.json b/packages/typing-fixtures/oracle/morse-unicode.json new file mode 100644 index 000000000..7cc342343 --- /dev/null +++ b/packages/typing-fixtures/oracle/morse-unicode.json @@ -0,0 +1,7 @@ +{ + "source": "TypeScript getMorse; Node v24.21.0", + "unicode": "17.0", + "units": 1182044, + "hash": "cbd60b424acd4b15", + "mappingCount": 492 +} From 3772c8accf6a5de2500e3d497773ff0bfa1675a4 Mon Sep 17 00:00:00 2001 From: Lakshmi Tanmay Date: Fri, 23 Oct 2026 04:23:15 +0530 Subject: [PATCH 35/93] Record verified historical parity and remaining engine cutover gaps --- docs/rust-engine/progress.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/rust-engine/progress.md b/docs/rust-engine/progress.md index 07ea207fc..44e383601 100644 --- a/docs/rust-engine/progress.md +++ b/docs/rust-engine/progress.md @@ -89,3 +89,12 @@ do not rerun completed checks without a new reason. - Replaced general-purpose Morse normalization with a 492-entry immutable compatibility map captured from original TypeScript (Node 24.21.0 / Unicode 17.0). Exhaustive native parity passes every Unicode scalar and every isolated UTF-16 unit; original 61 generator snapshots still pass. No reference was regenerated. - Expanded engine with analysis: 709,637 bytes raw / 271,030 gzip level 6 after optimization, within both payload budgets. Full browser rerun after this change remains pending. + +## Next concrete engine gaps (before Stage 1 exit) + +- Session must own incremental generation, custom limits and pending section requests; preserve final separator stripping and plus-X lookahead. +- Port browser preflight/layout observations, live config, slow-render failure policy, bailout/restart rules. Capture actual web discrepancies, including progression before batch failure. +- Add recorder delta invalidation when start rebases timestamps or finalization removes earlier observations; hosts must not accumulate stale deltas. +- Add persistent replay handle, malformed/bounded DTO checks, randomized lifecycle and memory/performance checks. +- Re-run optimized Cloudflare browser proof after Morse reduction, then fresh-checkout/complete CI and final performance budgets. +- Stage 2/3 remain pending; do not cut over website while these gaps exist. From ccb7db67fbb6ff8b178a526e88b66ad51715770b Mon Sep 17 00:00:00 2001 From: Lakshmi Tanmay Date: Fri, 23 Oct 2026 04:27:51 +0530 Subject: [PATCH 36/93] Own incremental generation, custom limits and recorder invalidation in sessions --- crates/typing-engine-wasm/src/lib.rs | 18 + .../typing-engine/src/bin/export-bindings.rs | 1 + crates/typing-engine/src/generation.rs | 17 +- crates/typing-engine/src/session.rs | 335 +++++++++++++++++- .../typing-engine/tests/generated_session.rs | 199 +++++++++++ docs/rust-engine/progress.md | 3 + packages/typing-engine/src/browser.client.ts | 26 +- 7 files changed, 571 insertions(+), 28 deletions(-) create mode 100644 crates/typing-engine/tests/generated_session.rs diff --git a/crates/typing-engine-wasm/src/lib.rs b/crates/typing-engine-wasm/src/lib.rs index 70b26c533..8d386d654 100644 --- a/crates/typing-engine-wasm/src/lib.rs +++ b/crates/typing-engine-wasm/src/lib.rs @@ -37,6 +37,13 @@ impl WasmSession { .map_err(|message| JsError::new(&message))?; Ok(serde_json::to_string(&update)?) } + pub fn snapshot(&self) -> Result { + let session = self + .session + .as_ref() + .ok_or_else(|| JsError::new("Session disposed"))?; + Ok(serde_json::to_string(&session.snapshot())?) + } pub fn log(&self) -> Result { let session = self .session @@ -127,6 +134,17 @@ impl WasmContent { &serde_json::json!({ "allWordsGenerated": generator.all_generated(), "currentQuote": generator.current_quote }), )?) } + pub fn session(&mut self, repeating: bool) -> Result { + let session = self + .content + .as_mut() + .ok_or_else(|| JsError::new("Content disposed"))? + .start_session(repeating) + .map_err(|message| JsError::new(&message))?; + Ok(WasmSession { + session: Some(session), + }) + } pub fn dispose(&mut self) { self.content = None; } diff --git a/crates/typing-engine/src/bin/export-bindings.rs b/crates/typing-engine/src/bin/export-bindings.rs index c069bc4f4..03a0050cd 100644 --- a/crates/typing-engine/src/bin/export-bindings.rs +++ b/crates/typing-engine/src/bin/export-bindings.rs @@ -11,6 +11,7 @@ fn main() -> Result<(), Box> { Command::export_all(&config)?; Config::export_all(&config)?; State::export_all(&config)?; + typing_engine::session::Snapshot::export_all(&config)?; Update::export_all(&config)?; typing_engine::stats::Log::export_all(&config)?; typing_engine::analysis::Query::export_all(&config)?; diff --git a/crates/typing-engine/src/generation.rs b/crates/typing-engine/src/generation.rs index 77fed3969..f2ebd90c0 100644 --- a/crates/typing-engine/src/generation.rs +++ b/crates/typing-engine/src/generation.rs @@ -131,6 +131,15 @@ fn collapse_spaces(text: Text) -> Text { Text(result) } impl Generator { + pub fn config(&self) -> &GenerationConfig { + &self.config + } + pub fn custom_limit(&self) -> Option { + self.custom.as_ref().map(|custom| crate::results::Limit { + mode: custom.limit.mode.clone(), + value: custom.limit.value as f64, + }) + } pub fn new( config: GenerationConfig, language: Language, @@ -146,9 +155,7 @@ impl Generator { mut random: Random, assets: &mut Assets, ) -> Result { - if !["time", "words", "custom", "zen", "quote"].contains(&config.mode.as_str()) { - return Err("Invalid mode".into()); - } + config.validate()?; if config.funbox.iter().any(|name| { ![ "polyglot", @@ -744,6 +751,10 @@ impl Content { )?); Ok(()) } + pub fn start_session(&mut self, repeating: bool) -> Result { + let generator = self.generator.take().ok_or("Content not prepared")?; + crate::Session::from_generator(generator, repeating) + } pub fn generator(&mut self) -> Result<&mut Generator, String> { self.generator.as_mut().ok_or("Content not prepared".into()) } diff --git a/crates/typing-engine/src/session.rs b/crates/typing-engine/src/session.rs index 3f6a41179..a3678f5c5 100644 --- a/crates/typing-engine/src/session.rs +++ b/crates/typing-engine/src/session.rs @@ -78,6 +78,84 @@ impl Default for Config { } } +impl Config { + pub fn validate(&self) -> Result<(), String> { + for (name, value, allowed) in [ + ( + "mode", + self.mode.as_str(), + &["time", "words", "quote", "custom", "zen"][..], + ), + ( + "difficulty", + self.difficulty.as_str(), + &["normal", "expert", "master"][..], + ), + ( + "stopOnError", + self.stop_on_error.as_str(), + &["off", "word", "letter"][..], + ), + ( + "deleteOnError", + self.delete_on_error.as_str(), + &["off", "word", "letter", "word_hard", "letter_hard"][..], + ), + ( + "confidenceMode", + self.confidence_mode.as_str(), + &["off", "on", "max"][..], + ), + ( + "minBurst", + self.min_burst.as_str(), + &["off", "fixed", "flex"][..], + ), + ("minWpm", self.min_wpm.as_str(), &["off", "custom"][..]), + ("minAcc", self.min_acc.as_str(), &["off", "custom"][..]), + ] { + if !allowed.contains(&value) { + return Err(format!("Invalid {name}")); + } + } + if !self.time.is_finite() || self.time < 0.0 { + return Err("Invalid time limit".into()); + } + if [ + self.min_burst_custom_speed, + self.min_wpm_custom_speed, + self.min_acc_custom, + ] + .iter() + .any(|value| !value.is_finite() || *value < 0.0) + || self.min_acc_custom > 100.0 + { + return Err("Invalid minimum threshold".into()); + } + if self.language.is_empty() + || self.language.len() > 128 + || !self + .language + .bytes() + .all(|c| c.is_ascii_alphanumeric() || c == b'_') + { + return Err("Invalid language".into()); + } + if self.funbox.len() > 64 + || self + .funbox + .iter() + .any(|name| name.is_empty() || name.len() > 64) + { + return Err("Invalid funbox list".into()); + } + if self.quote_length.len() > 64 || self.custom_polyglot.len() > 64 { + return Err("Content selection exceeds bound".into()); + } + Ok(()) + } +} + #[derive(Clone, Debug, Deserialize, Serialize, TS)] #[serde(tag = "op", rename_all = "camelCase")] pub enum Command { @@ -153,6 +231,21 @@ pub enum Command { #[serde(default)] defer_automatic: Option, }, + Configure { + now: f64, + config: Box, + }, + #[serde(rename_all = "camelCase")] + CustomLimit { + now: f64, + mode: String, + value: f64, + }, + Bailout { + now: f64, + #[serde(default)] + date: f64, + }, FlushAutomatic { now: f64, generation: u32, @@ -187,6 +280,25 @@ pub struct Update { pub generation: u32, #[serde(rename = "automaticPending")] pub automatic_pending: bool, + pub words: Vec, + #[serde(rename = "logInvalidated")] + pub log_invalidated: bool, +} +#[derive(Clone, Serialize, TS)] +#[serde(rename_all = "camelCase")] +pub struct WordChange { + pub index: usize, + pub word: Text, + pub section_index: usize, +} +#[derive(Serialize, TS)] +#[serde(rename_all = "camelCase")] +pub struct Snapshot { + pub state: State, + pub words: Vec, + pub section_indexes: Vec, + pub all_words_generated: bool, + pub current_quote: Option, } #[derive(Clone)] @@ -218,16 +330,25 @@ pub struct Session { defer_automatic: bool, automatic: VecDeque<(f64, f64)>, generation: u32, + generator: Option, + all_generated: bool, + custom_limit: Option, + quote: Option, + sections: Vec, + word_changes: Vec, + bailed_out: bool, } impl Session { pub fn new(config: Config, words: Vec) -> Result { - if !["time", "words", "quote", "custom", "zen"].contains(&config.mode.as_str()) { - return Err("Invalid mode".into()); - } - if !config.time.is_finite() || config.time < 0.0 { - return Err("Invalid time limit".into()); + config.validate()?; + if words.len() > 1_000_000 || words.iter().any(|word| word.len() > 1_000_000) { + return Err("Content exceeds session bound".into()); } + let all_generated = config.mode != "time" + && config.mode != "zen" + && !(config.mode == "words" && config.words == 0); + let sections = (1..=words.len()).collect(); Ok(Self { pace: crate::pace::Pace::default(), weak_spot: crate::weak_spot::WeakSpot::default(), @@ -250,9 +371,102 @@ impl Session { defer_automatic: false, automatic: VecDeque::new(), generation: 0, + generator: None, + all_generated, + custom_limit: None, + quote: None, + sections, + word_changes: vec![], + bailed_out: false, }) } + pub fn from_generator( + mut generator: crate::generation::Generator, + repeating: bool, + ) -> Result { + let generated = generator.generate(repeating)?; + let mut session = Self::new(generator.config().clone(), generated.words)?; + session.sections = generated.section_indexes; + session.custom_limit = generator.custom_limit(); + session.quote = generator + .current_quote + .as_ref() + .map(|quote| crate::results::QuoteRef { + id: quote.id, + group: quote.group, + }); + session.all_generated = generator.all_generated(); + session.generator = Some(generator); + session.strip_final_separator(); + session.word_changes.clear(); + Ok(session) + } + pub fn snapshot(&self) -> Snapshot { + Snapshot { + state: self.state(), + words: self.words.clone(), + section_indexes: self.sections.clone(), + all_words_generated: self.all_generated, + current_quote: self.quote.clone(), + } + } + fn strip_final_separator(&mut self) { + if self.all_generated + && let Some(word) = self.words.last_mut() + && word.len() > 1 + && matches!(word.0.last(), Some(32 | 10)) + { + word.0.pop(); + let index = self.words.len() - 1; + self.word_changes.push(WordChange { + index, + word: self.words[index].clone(), + section_index: self.sections[index], + }); + } + } + fn refill(&mut self) -> Result<(), String> { + let bound = self + .config + .funbox + .iter() + .find_map(|name| match name.as_str() { + "plus_zero" => Some(0), + "plus_one" => Some(1), + "plus_two" => Some(2), + "plus_three" => Some(3), + _ => None, + }) + .unwrap_or(100); + if self.all_generated || self.words.len().saturating_sub(self.index + 1) > bound { + return Ok(()); + } + let strip = |word: &Text| { + let mut word = word.clone(); + if matches!(word.0.last(), Some(32 | 10)) { + word.0.pop(); + } + word + }; + let previous = self.words.last().map(strip); + let previous2 = self.words.get(self.words.len().wrapping_sub(2)).map(strip); + let Some(generator) = &mut self.generator else { + return Ok(()); + }; + let index = self.words.len(); + let next = generator.next(index, bound, previous.as_ref(), previous2.as_ref())?; + self.words.push(next.word.clone()); + self.sections.push(next.section_index); + self.word_changes.push(WordChange { + index, + word: next.word, + section_index: next.section_index, + }); + self.all_generated = generator.all_generated(); + self.strip_final_separator(); + Ok(()) + } pub fn state(&self) -> State { State { word_index: self.index, @@ -316,12 +530,47 @@ impl Session { | Command::Observe { now, .. } | Command::Finalize { now } | Command::View { now, .. } - | Command::FlushAutomatic { now, .. } => *now, + | Command::FlushAutomatic { now, .. } + | Command::Configure { now, .. } + | Command::CustomLimit { now, .. } + | Command::Bailout { now, .. } => *now, }; if !now.is_finite() || now.abs() > 9_007_199_254_740_991.0 { return Err("Invalid timestamp".into()); } + match &command { + Command::Configure { config, .. } => config.validate()?, + Command::Insert { data, date, .. } => { + if data.len() > 65536 { + return Err("Committed input exceeds bound".into()); + } + if !date.is_finite() { + return Err("Invalid wall-clock date".into()); + } + } + Command::Start { date, .. } + | Command::Advance { date, .. } + | Command::Finish { date, .. } + | Command::End { date, .. } + | Command::Abandon { date, .. } + | Command::Bailout { date, .. } + if !date.is_finite() => + { + return Err("Invalid wall-clock date".into()); + } + _ => {} + } + if matches!(command, Command::Advance { .. }) + && self + .start + .is_some_and(|start| (now - start) / 1000.0 - f64::from(self.ticks) > 1_000_000.0) + { + return Err("Timer catchup exceeds bounded work".into()); + } let before = self.events.len(); + let previous_origin = self.origin(); + let invalidated = matches!(command, Command::Finalize { .. } | Command::Reset { .. }); + self.word_changes.clear(); match command { Command::Insert { data, @@ -371,6 +620,28 @@ impl Session { } self.delete(&kind, now, observed_input, sentinel_removed); } + Command::Configure { config, .. } => { + self.config = *config; + } + Command::CustomLimit { mode, value, .. } => { + if !["word", "time", "section"].contains(&mode.as_str()) + || !value.is_finite() + || value < 0.0 + { + return Err("Invalid custom limit".into()); + } + self.all_generated = mode != "time" + && value > 0.0 + && self + .generator + .as_ref() + .is_none_or(|generator| generator.all_generated()); + self.custom_limit = Some(crate::results::Limit { mode, value }); + } + Command::Bailout { now, date } => { + self.bailed_out = true; + self.end(now, date, None); + } Command::Start { now, date } => self.start(now, date), Command::Finish { now, date, reason } => self.end(now, date, reason), Command::End { now, date } | Command::Abandon { now, date } => { @@ -379,6 +650,7 @@ impl Session { Command::Advance { now, date } => self.advance(now, date), Command::Reset { .. } => { self.index = 0; + self.bailed_out = false; self.active = false; self.ended = false; self.reason = None; @@ -435,6 +707,8 @@ impl Session { events, generation: self.generation, automatic_pending: !self.automatic.is_empty(), + words: self.word_changes.clone(), + log_invalidated: invalidated || previous_origin != origin, }) } @@ -571,7 +845,11 @@ impl Session { .find(|event| event.kind == "input") .map(|event| event.now); self.record("input", now, data); - self.weak_spot.update( + let learner = self + .generator + .as_mut() + .map_or(&mut self.weak_spot, |generator| &mut generator.weak_spot); + learner.update( &Text(vec![unit]), correct, previous.map(|previous| round2(round2(now) - previous)), @@ -579,6 +857,10 @@ impl Session { if !correct && !stopped && self.config.delete_on_error != "off" { self.delete_error(now); } + if advance && let Err(_) = self.refill() { + self.end(now, date, Some("word generation error".into())); + return; + } if self.completion_checkpoint && self.config.mode != "zen" && ((self.config.difficulty == "master" && !correct) @@ -590,7 +872,11 @@ impl Session { self.end(now, date, Some("difficulty".into())); return; } - if self.completion_checkpoint && advance && self.config.min_burst != "off" { + if self.completion_checkpoint + && advance + && (self.index + 1 < self.words.len() || self.config.mode == "zen") + && self.config.min_burst != "off" + { let log: crate::stats::Log = serde_json::from_value(self.log()).expect("Engine log is valid"); let burst = crate::stats::word_burst(&log, self.index, now - self.start.unwrap_or(now)); @@ -614,9 +900,7 @@ impl Session { return; } } - let finite = self.config.mode != "time" - && self.config.mode != "zen" - && !(self.config.mode == "words" && self.config.words == 0); + let finite = self.all_generated; let quick = self.config.quick_end && target.len() == input.len() + 1 && self.config.stop_on_error == "off" @@ -823,10 +1107,17 @@ impl Session { self.end(now, date, Some(reason.into())); } } - if self.config.mode == "time" - && self.config.time > 0.0 - && f64::from(self.ticks) >= self.config.time - { + let limit = if self.config.mode == "time" { + Some(self.config.time) + } else if self.config.mode == "custom" { + self.custom_limit + .as_ref() + .filter(|limit| limit.mode == "time") + .map(|limit| limit.value) + } else { + None + }; + if limit.is_some_and(|limit| limit > 0.0 && f64::from(self.ticks) >= limit) { self.end(now, date, None); } } @@ -1044,11 +1335,19 @@ impl Session { let mode2 = match self.config.mode.as_str() { "words" => self.config.words.to_string(), "time" => self.config.time.to_string(), - "quote" => "-1".into(), + "quote" => self + .quote + .as_ref() + .map_or_else(|| "-1".into(), |quote| quote.id.to_string()), other => other.into(), }; let korean=self.words.iter().flat_map(|word| &word.0).any(|c| matches!(*c,0xac00..=0xd7af | 0x1100..=0x11ff | 0x3130..=0x318f | 0xa960..=0xa97f | 0xd7b0..=0xd7ff)); - json!({"version":EVENT_LOG_VERSION,"events":events.iter().map(|event|Self::event(event,self.origin())).collect::>(), - "context":{"targetWords":self.words,"mode":self.config.mode,"mode2":mode2,"koreanStatus":korean,"bailedOut":false,"isFunboxWithNospacePropertyActive":self.nospace()}}) + let mut log = json!({"version":EVENT_LOG_VERSION,"events":events.iter().map(|event|Self::event(event,self.origin())).collect::>(), + "context":{"targetWords":self.words,"mode":self.config.mode,"mode2":mode2,"koreanStatus":korean,"bailedOut":self.bailed_out,"isFunboxWithNospacePropertyActive":self.nospace()}}); + if let Some(limit) = &self.custom_limit { + log["context"]["customTextLimitMode"] = json!(limit.mode); + log["context"]["customTextLimitValue"] = json!(limit.value); + } + log } } diff --git a/crates/typing-engine/tests/generated_session.rs b/crates/typing-engine/tests/generated_session.rs new file mode 100644 index 000000000..6a568a325 --- /dev/null +++ b/crates/typing-engine/tests/generated_session.rs @@ -0,0 +1,199 @@ +use typing_engine::{ + Command, Config, Session, Text, + generation::{CustomLimit, CustomText, Generator, Language}, + random::Random, +}; +fn generator(config: Config, custom: Option) -> Generator { + Generator::new( + config, + Language { + name: "english".into(), + words: ["one", "two", "three", "four"] + .into_iter() + .map(Text::from) + .collect(), + ..Language::default() + }, + custom, + Random::seeded(42), + ) + .unwrap() +} +fn insert(session: &mut Session, data: Text, now: f64) -> typing_engine::session::Update { + session + .apply(Command::Insert { + data, + now, + date: now + 1_000_000.0, + correct_shift_used: None, + automatic: None, + is_composition_ending: None, + }) + .unwrap() +} +#[test] +fn finite_session_generates_past_initial_window_and_finishes_on_final_bare_word() { + let config = Config { + mode: "words".into(), + words: 150, + ..Config::default() + }; + let mut session = Session::from_generator(generator(config, None), false).unwrap(); + assert_eq!(session.snapshot().words.len(), 100); + let mut changes = 0; + for index in 0..150 { + assert_eq!(session.state().word_index, index); + let target = session.snapshot().words[index].clone(); + let update = insert(&mut session, target, 1000.0 + index as f64 * 100.0); + changes += update.words.len(); + assert_eq!(update.state.ended, index == 149); + } + let snapshot = session.snapshot(); + assert!(changes >= 50); + assert_eq!(snapshot.words.len(), 150); + assert!(snapshot.all_words_generated); + assert_ne!(snapshot.words.last().unwrap().0.last(), Some(&32)); + let ends = session.log()["events"] + .as_array() + .unwrap() + .iter() + .filter(|event| event["type"] == "timer" && event["data"]["event"] == "end") + .count(); + assert_eq!(ends, 1); +} +#[test] +fn plus_zero_refills_before_deciding_completion() { + let config = Config { + mode: "words".into(), + words: 3, + funbox: vec!["plus_zero".into()], + ..Config::default() + }; + let mut session = Session::from_generator(generator(config, None), false).unwrap(); + assert_eq!(session.snapshot().words.len(), 1); + for index in 0..3 { + let target = session.snapshot().words[index].clone(); + let update = insert(&mut session, target, index as f64 * 500.0); + assert_eq!(update.state.ended, index == 2); + if index < 2 { + assert_eq!(update.state.word_index, index + 1); + assert!(!update.words.is_empty()); + } + } +} +#[test] +fn custom_time_and_infinite_limits_do_not_finish_at_prepared_content_end() { + for value in [2, 0] { + let custom = CustomText { + text: vec![Text::from("a")], + mode: "repeat".into(), + limit: CustomLimit { + mode: "time".into(), + value, + }, + pipe_delimiter: false, + }; + let mut session = Session::from_generator( + generator( + Config { + mode: "custom".into(), + ..Config::default() + }, + Some(custom), + ), + false, + ) + .unwrap(); + for index in 0..105 { + insert(&mut session, Text::from("a "), index as f64 * 10.0); + } + assert!(!session.state().ended); + session + .apply(Command::Advance { + now: 2000.0, + date: 1_002_000.0, + }) + .unwrap(); + assert_eq!(session.state().ended, value == 2); + assert_eq!( + session.log()["context"]["customTextLimitValue"].as_f64(), + Some(value as f64) + ); + } +} +#[test] +fn log_deltas_signal_rebasing_and_finalize_invalidation() { + let mut session = Session::new( + Config { + mode: "words".into(), + words: 1, + ..Config::default() + }, + vec![Text::from("a")], + ) + .unwrap(); + session + .apply(Command::Observe { + now: 90.0, + event_type: "keydown".into(), + event_data: serde_json::json!({"code":"KeyA"}), + }) + .unwrap(); + let update = insert(&mut session, Text::from("a"), 100.0); + assert!(update.log_invalidated); + assert_eq!(session.log()["events"][0]["testMs"], -10.0); + assert!( + session + .apply(Command::Finalize { now: 120.0 }) + .unwrap() + .log_invalidated + ); + assert!( + session + .apply(Command::Reset { now: 130.0 }) + .unwrap() + .log_invalidated + ); +} + +#[test] +fn invalid_configuration_and_overdue_work_leave_session_unchanged() { + let mut session = Session::new(Config::default(), vec![Text::from("word ")]).unwrap(); + let snapshot = serde_json::to_value(session.snapshot()).unwrap(); + let config = Config { + difficulty: "unknown".into(), + ..Config::default() + }; + assert!( + session + .apply(Command::Configure { + now: 0.0, + config: Box::new(config) + }) + .is_err() + ); + assert!( + session + .apply(Command::Insert { + data: Text(vec![97; 65537]), + now: 0.0, + date: 0.0, + correct_shift_used: None, + automatic: None, + is_composition_ending: None + }) + .is_err() + ); + assert_eq!(snapshot, serde_json::to_value(session.snapshot()).unwrap()); + insert(&mut session, Text::from("w"), 0.0); + let log = session.log(); + assert!( + session + .apply(Command::Advance { + now: 1_000_001_000.0, + date: 0.0 + }) + .is_err() + ); + assert_eq!(log, session.log()); +} diff --git a/docs/rust-engine/progress.md b/docs/rust-engine/progress.md index 44e383601..0a54a479e 100644 --- a/docs/rust-engine/progress.md +++ b/docs/rust-engine/progress.md @@ -98,3 +98,6 @@ do not rerun completed checks without a new reason. - Add persistent replay handle, malformed/bounded DTO checks, randomized lifecycle and memory/performance checks. - Re-run optimized Cloudflare browser proof after Morse reduction, then fresh-checkout/complete CI and final performance budgets. - Stage 2/3 remain pending; do not cut over website while these gaps exist. + +- Session now owns an attached generator, incremental lookahead refill, final separator stripping, quote identity, custom time/infinite limits and explicit bailout state. Full snapshots are explicit; per-command word changes are incremental. Recorder deltas signal invalidation when origin changes or finalization/reset changes earlier events. +- Five new native session tests cover 150-word continuation, plus-zero refill/completion, custom time/infinite limits, recorder rebasing/finalization, and pre-mutation malformed config / oversized commits / bounded timer catchup. Browser integration of these new APIs still pending. diff --git a/packages/typing-engine/src/browser.client.ts b/packages/typing-engine/src/browser.client.ts index 2830e7d84..be340a29c 100644 --- a/packages/typing-engine/src/browser.client.ts +++ b/packages/typing-engine/src/browser.client.ts @@ -16,8 +16,9 @@ import type { NextWord } from "../generated/types/NextWord"; import type { Quote } from "../generated/types/Quote"; import type { Query } from "../generated/types/Query"; import type { Response } from "../generated/types/Response"; +import type { Snapshot } from "../generated/types/Snapshot"; import type { Log } from "../generated/types/Log"; -export type { Query, Response, Log }; +export type { Query, Response, Log, Snapshot }; export type { Command, @@ -64,12 +65,14 @@ export async function loadEngine(): Promise<{ export class BrowserSession { private handle: WasmSession | undefined; - constructor(config: Partial, words: Text[]) { - this.handle = new WasmSession( - JSON.stringify(config), - JSON.stringify(words), - BINDING_VERSION, - ); + constructor(config: Partial, words: Text[], handle?: WasmSession) { + this.handle = + handle ?? + new WasmSession( + JSON.stringify(config), + JSON.stringify(words), + BINDING_VERSION, + ); } command(command: Command): Update { if (this.handle === undefined) { @@ -77,6 +80,12 @@ export class BrowserSession { } return JSON.parse(this.handle.command(JSON.stringify(command))) as Update; } + snapshot(): Snapshot { + if (this.handle === undefined) { + throw new Error("Typing engine session disposed"); + } + return JSON.parse(this.handle.snapshot()) as Snapshot; + } log(): unknown { if (this.handle === undefined) { throw new Error("Typing engine session disposed"); @@ -167,6 +176,9 @@ export class BrowserContent { ), ) as NextWord; } + createSession(repeating = false): BrowserSession { + return new BrowserSession({}, [], this.live().session(repeating)); + } metadata(): { allWordsGenerated: boolean; currentQuote: Quote | null } { return JSON.parse(this.live().metadata()) as { allWordsGenerated: boolean; From d28dd5424e8bb73bae4cd5dc171b28dae142f522 Mon Sep 17 00:00:00 2001 From: Lakshmi Tanmay Date: Fri, 23 Oct 2026 04:31:10 +0530 Subject: [PATCH 37/93] Freeze every funbox pair and forced-setting compatibility case --- .../scripts/capture-funbox-oracle.ts | 74 + packages/typing-fixtures/oracle/funboxes.json | 16208 ++++++++++++++++ 2 files changed, 16282 insertions(+) create mode 100644 packages/typing-core/scripts/capture-funbox-oracle.ts create mode 100644 packages/typing-fixtures/oracle/funboxes.json diff --git a/packages/typing-core/scripts/capture-funbox-oracle.ts b/packages/typing-core/scripts/capture-funbox-oracle.ts new file mode 100644 index 000000000..dee76952a --- /dev/null +++ b/packages/typing-core/scripts/capture-funbox-oracle.ts @@ -0,0 +1,74 @@ +import { writeFileSync } from "node:fs"; +import { resolve } from "node:path"; +import { + getAllFunboxes, + checkCompatibility, + checkForcedConfig, +} from "@oxytype/funbox"; +import { canSetConfigWithCurrentFunboxes } from "../src/config/funbox-validation"; +import type { ConfigValue } from "@oxytype/schemas/configs"; +const source = getAllFunboxes(); +const metadata = source.map( + ({ + name, + properties = [], + frontendFunctions = [], + frontendForcedConfig = {}, + canGetPb, + difficultyLevel, + cssModifications = [], + }) => ({ + name, + properties, + frontendFunctions, + frontendForcedConfig, + canGetPb, + difficultyLevel, + cssModifications, + }), +); +const combinations = metadata.flatMap((a) => + metadata.map((b) => ({ + names: [a.name, b.name], + compatible: checkCompatibility([a.name, b.name]), + })), +); +const forced = source.flatMap((f) => + ( + [ + ["mode", "time"], + ["mode", "words"], + ["mode", "quote"], + ["mode", "custom"], + ["mode", "zen"], + ["words", 0], + ["words", 10], + ["time", 0], + ["time", 15], + ["punctuation", true], + ["punctuation", false], + ["numbers", true], + ["numbers", false], + ["highlightMode", "word"], + ["highlightMode", "letter"], + ["highlightMode", "off"], + ] as [string, ConfigValue][] + ).map(([key, value]) => ({ + names: [f.name], + key, + value, + forced: checkForcedConfig(key, value, [f]), + canSet: canSetConfigWithCurrentFunboxes(key, value, [f.name]), + })), +); +const root = resolve(import.meta.dirname, "../../.."); +writeFileSync( + resolve(root, "crates/typing-engine/data/funboxes.json"), + `${JSON.stringify(metadata, null, 2)}\n`, + { flag: "wx" }, +); +writeFileSync( + resolve(root, "packages/typing-fixtures/oracle/funboxes.json"), + `${JSON.stringify({ combinations, forced }, null, 2)}\n`, + { flag: "wx" }, +); diff --git a/packages/typing-fixtures/oracle/funboxes.json b/packages/typing-fixtures/oracle/funboxes.json new file mode 100644 index 000000000..9a17875e9 --- /dev/null +++ b/packages/typing-fixtures/oracle/funboxes.json @@ -0,0 +1,16208 @@ +{ + "combinations": [ + { + "names": ["58008", "58008"], + "compatible": false + }, + { + "names": ["58008", "mirror"], + "compatible": true + }, + { + "names": ["58008", "upside_down"], + "compatible": true + }, + { + "names": ["58008", "nausea"], + "compatible": true + }, + { + "names": ["58008", "round_round_baby"], + "compatible": true + }, + { + "names": ["58008", "simon_says"], + "compatible": true + }, + { + "names": ["58008", "tts"], + "compatible": true + }, + { + "names": ["58008", "choo_choo"], + "compatible": true + }, + { + "names": ["58008", "arrows"], + "compatible": false + }, + { + "names": ["58008", "rAnDoMcAsE"], + "compatible": false + }, + { + "names": ["58008", "sPoNgEcAsE"], + "compatible": false + }, + { + "names": ["58008", "capitals"], + "compatible": false + }, + { + "names": ["58008", "layout_mirror"], + "compatible": false + }, + { + "names": ["58008", "layoutfluid"], + "compatible": false + }, + { + "names": ["58008", "earthquake"], + "compatible": true + }, + { + "names": ["58008", "space_balls"], + "compatible": true + }, + { + "names": ["58008", "gibberish"], + "compatible": false + }, + { + "names": ["58008", "ascii"], + "compatible": false + }, + { + "names": ["58008", "specials"], + "compatible": false + }, + { + "names": ["58008", "plus_zero"], + "compatible": true + }, + { + "names": ["58008", "plus_one"], + "compatible": true + }, + { + "names": ["58008", "plus_two"], + "compatible": true + }, + { + "names": ["58008", "plus_three"], + "compatible": true + }, + { + "names": ["58008", "read_ahead_easy"], + "compatible": true + }, + { + "names": ["58008", "read_ahead"], + "compatible": true + }, + { + "names": ["58008", "read_ahead_hard"], + "compatible": true + }, + { + "names": ["58008", "memory"], + "compatible": true + }, + { + "names": ["58008", "nospace"], + "compatible": true + }, + { + "names": ["58008", "poetry"], + "compatible": false + }, + { + "names": ["58008", "wikipedia"], + "compatible": false + }, + { + "names": ["58008", "weakspot"], + "compatible": false + }, + { + "names": ["58008", "pseudolang"], + "compatible": false + }, + { + "names": ["58008", "IPv4"], + "compatible": false + }, + { + "names": ["58008", "IPv6"], + "compatible": false + }, + { + "names": ["58008", "binary"], + "compatible": false + }, + { + "names": ["58008", "hexadecimal"], + "compatible": false + }, + { + "names": ["58008", "zipf"], + "compatible": false + }, + { + "names": ["58008", "morse"], + "compatible": true + }, + { + "names": ["58008", "crt"], + "compatible": true + }, + { + "names": ["58008", "backwards"], + "compatible": true + }, + { + "names": ["58008", "ddoouubblleedd"], + "compatible": true + }, + { + "names": ["58008", "instant_messaging"], + "compatible": false + }, + { + "names": ["58008", "underscore_spaces"], + "compatible": true + }, + { + "names": ["58008", "ALL_CAPS"], + "compatible": false + }, + { + "names": ["58008", "polyglot"], + "compatible": false + }, + { + "names": ["58008", "asl"], + "compatible": true + }, + { + "names": ["58008", "rot13"], + "compatible": true + }, + { + "names": ["58008", "no_quit"], + "compatible": true + }, + { + "names": ["mirror", "58008"], + "compatible": true + }, + { + "names": ["mirror", "mirror"], + "compatible": false + }, + { + "names": ["mirror", "upside_down"], + "compatible": false + }, + { + "names": ["mirror", "nausea"], + "compatible": true + }, + { + "names": ["mirror", "round_round_baby"], + "compatible": true + }, + { + "names": ["mirror", "simon_says"], + "compatible": true + }, + { + "names": ["mirror", "tts"], + "compatible": true + }, + { + "names": ["mirror", "choo_choo"], + "compatible": true + }, + { + "names": ["mirror", "arrows"], + "compatible": true + }, + { + "names": ["mirror", "rAnDoMcAsE"], + "compatible": true + }, + { + "names": ["mirror", "sPoNgEcAsE"], + "compatible": true + }, + { + "names": ["mirror", "capitals"], + "compatible": true + }, + { + "names": ["mirror", "layout_mirror"], + "compatible": true + }, + { + "names": ["mirror", "layoutfluid"], + "compatible": true + }, + { + "names": ["mirror", "earthquake"], + "compatible": true + }, + { + "names": ["mirror", "space_balls"], + "compatible": true + }, + { + "names": ["mirror", "gibberish"], + "compatible": true + }, + { + "names": ["mirror", "ascii"], + "compatible": true + }, + { + "names": ["mirror", "specials"], + "compatible": true + }, + { + "names": ["mirror", "plus_zero"], + "compatible": true + }, + { + "names": ["mirror", "plus_one"], + "compatible": true + }, + { + "names": ["mirror", "plus_two"], + "compatible": true + }, + { + "names": ["mirror", "plus_three"], + "compatible": true + }, + { + "names": ["mirror", "read_ahead_easy"], + "compatible": true + }, + { + "names": ["mirror", "read_ahead"], + "compatible": true + }, + { + "names": ["mirror", "read_ahead_hard"], + "compatible": true + }, + { + "names": ["mirror", "memory"], + "compatible": true + }, + { + "names": ["mirror", "nospace"], + "compatible": true + }, + { + "names": ["mirror", "poetry"], + "compatible": true + }, + { + "names": ["mirror", "wikipedia"], + "compatible": true + }, + { + "names": ["mirror", "weakspot"], + "compatible": true + }, + { + "names": ["mirror", "pseudolang"], + "compatible": true + }, + { + "names": ["mirror", "IPv4"], + "compatible": true + }, + { + "names": ["mirror", "IPv6"], + "compatible": true + }, + { + "names": ["mirror", "binary"], + "compatible": true + }, + { + "names": ["mirror", "hexadecimal"], + "compatible": true + }, + { + "names": ["mirror", "zipf"], + "compatible": true + }, + { + "names": ["mirror", "morse"], + "compatible": true + }, + { + "names": ["mirror", "crt"], + "compatible": true + }, + { + "names": ["mirror", "backwards"], + "compatible": true + }, + { + "names": ["mirror", "ddoouubblleedd"], + "compatible": true + }, + { + "names": ["mirror", "instant_messaging"], + "compatible": true + }, + { + "names": ["mirror", "underscore_spaces"], + "compatible": true + }, + { + "names": ["mirror", "ALL_CAPS"], + "compatible": true + }, + { + "names": ["mirror", "polyglot"], + "compatible": true + }, + { + "names": ["mirror", "asl"], + "compatible": true + }, + { + "names": ["mirror", "rot13"], + "compatible": true + }, + { + "names": ["mirror", "no_quit"], + "compatible": true + }, + { + "names": ["upside_down", "58008"], + "compatible": true + }, + { + "names": ["upside_down", "mirror"], + "compatible": false + }, + { + "names": ["upside_down", "upside_down"], + "compatible": false + }, + { + "names": ["upside_down", "nausea"], + "compatible": true + }, + { + "names": ["upside_down", "round_round_baby"], + "compatible": true + }, + { + "names": ["upside_down", "simon_says"], + "compatible": true + }, + { + "names": ["upside_down", "tts"], + "compatible": true + }, + { + "names": ["upside_down", "choo_choo"], + "compatible": true + }, + { + "names": ["upside_down", "arrows"], + "compatible": true + }, + { + "names": ["upside_down", "rAnDoMcAsE"], + "compatible": true + }, + { + "names": ["upside_down", "sPoNgEcAsE"], + "compatible": true + }, + { + "names": ["upside_down", "capitals"], + "compatible": true + }, + { + "names": ["upside_down", "layout_mirror"], + "compatible": true + }, + { + "names": ["upside_down", "layoutfluid"], + "compatible": true + }, + { + "names": ["upside_down", "earthquake"], + "compatible": true + }, + { + "names": ["upside_down", "space_balls"], + "compatible": true + }, + { + "names": ["upside_down", "gibberish"], + "compatible": true + }, + { + "names": ["upside_down", "ascii"], + "compatible": true + }, + { + "names": ["upside_down", "specials"], + "compatible": true + }, + { + "names": ["upside_down", "plus_zero"], + "compatible": true + }, + { + "names": ["upside_down", "plus_one"], + "compatible": true + }, + { + "names": ["upside_down", "plus_two"], + "compatible": true + }, + { + "names": ["upside_down", "plus_three"], + "compatible": true + }, + { + "names": ["upside_down", "read_ahead_easy"], + "compatible": true + }, + { + "names": ["upside_down", "read_ahead"], + "compatible": true + }, + { + "names": ["upside_down", "read_ahead_hard"], + "compatible": true + }, + { + "names": ["upside_down", "memory"], + "compatible": true + }, + { + "names": ["upside_down", "nospace"], + "compatible": true + }, + { + "names": ["upside_down", "poetry"], + "compatible": true + }, + { + "names": ["upside_down", "wikipedia"], + "compatible": true + }, + { + "names": ["upside_down", "weakspot"], + "compatible": true + }, + { + "names": ["upside_down", "pseudolang"], + "compatible": true + }, + { + "names": ["upside_down", "IPv4"], + "compatible": true + }, + { + "names": ["upside_down", "IPv6"], + "compatible": true + }, + { + "names": ["upside_down", "binary"], + "compatible": true + }, + { + "names": ["upside_down", "hexadecimal"], + "compatible": true + }, + { + "names": ["upside_down", "zipf"], + "compatible": true + }, + { + "names": ["upside_down", "morse"], + "compatible": true + }, + { + "names": ["upside_down", "crt"], + "compatible": true + }, + { + "names": ["upside_down", "backwards"], + "compatible": true + }, + { + "names": ["upside_down", "ddoouubblleedd"], + "compatible": true + }, + { + "names": ["upside_down", "instant_messaging"], + "compatible": true + }, + { + "names": ["upside_down", "underscore_spaces"], + "compatible": true + }, + { + "names": ["upside_down", "ALL_CAPS"], + "compatible": true + }, + { + "names": ["upside_down", "polyglot"], + "compatible": true + }, + { + "names": ["upside_down", "asl"], + "compatible": true + }, + { + "names": ["upside_down", "rot13"], + "compatible": true + }, + { + "names": ["upside_down", "no_quit"], + "compatible": true + }, + { + "names": ["nausea", "58008"], + "compatible": true + }, + { + "names": ["nausea", "mirror"], + "compatible": true + }, + { + "names": ["nausea", "upside_down"], + "compatible": true + }, + { + "names": ["nausea", "nausea"], + "compatible": false + }, + { + "names": ["nausea", "round_round_baby"], + "compatible": false + }, + { + "names": ["nausea", "simon_says"], + "compatible": true + }, + { + "names": ["nausea", "tts"], + "compatible": true + }, + { + "names": ["nausea", "choo_choo"], + "compatible": true + }, + { + "names": ["nausea", "arrows"], + "compatible": true + }, + { + "names": ["nausea", "rAnDoMcAsE"], + "compatible": true + }, + { + "names": ["nausea", "sPoNgEcAsE"], + "compatible": true + }, + { + "names": ["nausea", "capitals"], + "compatible": true + }, + { + "names": ["nausea", "layout_mirror"], + "compatible": true + }, + { + "names": ["nausea", "layoutfluid"], + "compatible": true + }, + { + "names": ["nausea", "earthquake"], + "compatible": true + }, + { + "names": ["nausea", "space_balls"], + "compatible": true + }, + { + "names": ["nausea", "gibberish"], + "compatible": true + }, + { + "names": ["nausea", "ascii"], + "compatible": true + }, + { + "names": ["nausea", "specials"], + "compatible": true + }, + { + "names": ["nausea", "plus_zero"], + "compatible": true + }, + { + "names": ["nausea", "plus_one"], + "compatible": true + }, + { + "names": ["nausea", "plus_two"], + "compatible": true + }, + { + "names": ["nausea", "plus_three"], + "compatible": true + }, + { + "names": ["nausea", "read_ahead_easy"], + "compatible": true + }, + { + "names": ["nausea", "read_ahead"], + "compatible": true + }, + { + "names": ["nausea", "read_ahead_hard"], + "compatible": true + }, + { + "names": ["nausea", "memory"], + "compatible": true + }, + { + "names": ["nausea", "nospace"], + "compatible": true + }, + { + "names": ["nausea", "poetry"], + "compatible": true + }, + { + "names": ["nausea", "wikipedia"], + "compatible": true + }, + { + "names": ["nausea", "weakspot"], + "compatible": true + }, + { + "names": ["nausea", "pseudolang"], + "compatible": true + }, + { + "names": ["nausea", "IPv4"], + "compatible": true + }, + { + "names": ["nausea", "IPv6"], + "compatible": true + }, + { + "names": ["nausea", "binary"], + "compatible": true + }, + { + "names": ["nausea", "hexadecimal"], + "compatible": true + }, + { + "names": ["nausea", "zipf"], + "compatible": true + }, + { + "names": ["nausea", "morse"], + "compatible": true + }, + { + "names": ["nausea", "crt"], + "compatible": true + }, + { + "names": ["nausea", "backwards"], + "compatible": true + }, + { + "names": ["nausea", "ddoouubblleedd"], + "compatible": true + }, + { + "names": ["nausea", "instant_messaging"], + "compatible": true + }, + { + "names": ["nausea", "underscore_spaces"], + "compatible": true + }, + { + "names": ["nausea", "ALL_CAPS"], + "compatible": true + }, + { + "names": ["nausea", "polyglot"], + "compatible": true + }, + { + "names": ["nausea", "asl"], + "compatible": true + }, + { + "names": ["nausea", "rot13"], + "compatible": true + }, + { + "names": ["nausea", "no_quit"], + "compatible": true + }, + { + "names": ["round_round_baby", "58008"], + "compatible": true + }, + { + "names": ["round_round_baby", "mirror"], + "compatible": true + }, + { + "names": ["round_round_baby", "upside_down"], + "compatible": true + }, + { + "names": ["round_round_baby", "nausea"], + "compatible": false + }, + { + "names": ["round_round_baby", "round_round_baby"], + "compatible": false + }, + { + "names": ["round_round_baby", "simon_says"], + "compatible": true + }, + { + "names": ["round_round_baby", "tts"], + "compatible": true + }, + { + "names": ["round_round_baby", "choo_choo"], + "compatible": true + }, + { + "names": ["round_round_baby", "arrows"], + "compatible": true + }, + { + "names": ["round_round_baby", "rAnDoMcAsE"], + "compatible": true + }, + { + "names": ["round_round_baby", "sPoNgEcAsE"], + "compatible": true + }, + { + "names": ["round_round_baby", "capitals"], + "compatible": true + }, + { + "names": ["round_round_baby", "layout_mirror"], + "compatible": true + }, + { + "names": ["round_round_baby", "layoutfluid"], + "compatible": true + }, + { + "names": ["round_round_baby", "earthquake"], + "compatible": true + }, + { + "names": ["round_round_baby", "space_balls"], + "compatible": true + }, + { + "names": ["round_round_baby", "gibberish"], + "compatible": true + }, + { + "names": ["round_round_baby", "ascii"], + "compatible": true + }, + { + "names": ["round_round_baby", "specials"], + "compatible": true + }, + { + "names": ["round_round_baby", "plus_zero"], + "compatible": true + }, + { + "names": ["round_round_baby", "plus_one"], + "compatible": true + }, + { + "names": ["round_round_baby", "plus_two"], + "compatible": true + }, + { + "names": ["round_round_baby", "plus_three"], + "compatible": true + }, + { + "names": ["round_round_baby", "read_ahead_easy"], + "compatible": true + }, + { + "names": ["round_round_baby", "read_ahead"], + "compatible": true + }, + { + "names": ["round_round_baby", "read_ahead_hard"], + "compatible": true + }, + { + "names": ["round_round_baby", "memory"], + "compatible": true + }, + { + "names": ["round_round_baby", "nospace"], + "compatible": true + }, + { + "names": ["round_round_baby", "poetry"], + "compatible": true + }, + { + "names": ["round_round_baby", "wikipedia"], + "compatible": true + }, + { + "names": ["round_round_baby", "weakspot"], + "compatible": true + }, + { + "names": ["round_round_baby", "pseudolang"], + "compatible": true + }, + { + "names": ["round_round_baby", "IPv4"], + "compatible": true + }, + { + "names": ["round_round_baby", "IPv6"], + "compatible": true + }, + { + "names": ["round_round_baby", "binary"], + "compatible": true + }, + { + "names": ["round_round_baby", "hexadecimal"], + "compatible": true + }, + { + "names": ["round_round_baby", "zipf"], + "compatible": true + }, + { + "names": ["round_round_baby", "morse"], + "compatible": true + }, + { + "names": ["round_round_baby", "crt"], + "compatible": true + }, + { + "names": ["round_round_baby", "backwards"], + "compatible": true + }, + { + "names": ["round_round_baby", "ddoouubblleedd"], + "compatible": true + }, + { + "names": ["round_round_baby", "instant_messaging"], + "compatible": true + }, + { + "names": ["round_round_baby", "underscore_spaces"], + "compatible": true + }, + { + "names": ["round_round_baby", "ALL_CAPS"], + "compatible": true + }, + { + "names": ["round_round_baby", "polyglot"], + "compatible": true + }, + { + "names": ["round_round_baby", "asl"], + "compatible": true + }, + { + "names": ["round_round_baby", "rot13"], + "compatible": true + }, + { + "names": ["round_round_baby", "no_quit"], + "compatible": true + }, + { + "names": ["simon_says", "58008"], + "compatible": true + }, + { + "names": ["simon_says", "mirror"], + "compatible": true + }, + { + "names": ["simon_says", "upside_down"], + "compatible": true + }, + { + "names": ["simon_says", "nausea"], + "compatible": true + }, + { + "names": ["simon_says", "round_round_baby"], + "compatible": true + }, + { + "names": ["simon_says", "simon_says"], + "compatible": false + }, + { + "names": ["simon_says", "tts"], + "compatible": false + }, + { + "names": ["simon_says", "choo_choo"], + "compatible": true + }, + { + "names": ["simon_says", "arrows"], + "compatible": true + }, + { + "names": ["simon_says", "rAnDoMcAsE"], + "compatible": true + }, + { + "names": ["simon_says", "sPoNgEcAsE"], + "compatible": true + }, + { + "names": ["simon_says", "capitals"], + "compatible": true + }, + { + "names": ["simon_says", "layout_mirror"], + "compatible": false + }, + { + "names": ["simon_says", "layoutfluid"], + "compatible": false + }, + { + "names": ["simon_says", "earthquake"], + "compatible": true + }, + { + "names": ["simon_says", "space_balls"], + "compatible": true + }, + { + "names": ["simon_says", "gibberish"], + "compatible": true + }, + { + "names": ["simon_says", "ascii"], + "compatible": true + }, + { + "names": ["simon_says", "specials"], + "compatible": true + }, + { + "names": ["simon_says", "plus_zero"], + "compatible": false + }, + { + "names": ["simon_says", "plus_one"], + "compatible": false + }, + { + "names": ["simon_says", "plus_two"], + "compatible": false + }, + { + "names": ["simon_says", "plus_three"], + "compatible": false + }, + { + "names": ["simon_says", "read_ahead_easy"], + "compatible": false + }, + { + "names": ["simon_says", "read_ahead"], + "compatible": false + }, + { + "names": ["simon_says", "read_ahead_hard"], + "compatible": false + }, + { + "names": ["simon_says", "memory"], + "compatible": false + }, + { + "names": ["simon_says", "nospace"], + "compatible": true + }, + { + "names": ["simon_says", "poetry"], + "compatible": true + }, + { + "names": ["simon_says", "wikipedia"], + "compatible": true + }, + { + "names": ["simon_says", "weakspot"], + "compatible": true + }, + { + "names": ["simon_says", "pseudolang"], + "compatible": true + }, + { + "names": ["simon_says", "IPv4"], + "compatible": true + }, + { + "names": ["simon_says", "IPv6"], + "compatible": true + }, + { + "names": ["simon_says", "binary"], + "compatible": true + }, + { + "names": ["simon_says", "hexadecimal"], + "compatible": true + }, + { + "names": ["simon_says", "zipf"], + "compatible": true + }, + { + "names": ["simon_says", "morse"], + "compatible": true + }, + { + "names": ["simon_says", "crt"], + "compatible": true + }, + { + "names": ["simon_says", "backwards"], + "compatible": true + }, + { + "names": ["simon_says", "ddoouubblleedd"], + "compatible": true + }, + { + "names": ["simon_says", "instant_messaging"], + "compatible": true + }, + { + "names": ["simon_says", "underscore_spaces"], + "compatible": true + }, + { + "names": ["simon_says", "ALL_CAPS"], + "compatible": true + }, + { + "names": ["simon_says", "polyglot"], + "compatible": true + }, + { + "names": ["simon_says", "asl"], + "compatible": true + }, + { + "names": ["simon_says", "rot13"], + "compatible": true + }, + { + "names": ["simon_says", "no_quit"], + "compatible": true + }, + { + "names": ["tts", "58008"], + "compatible": true + }, + { + "names": ["tts", "mirror"], + "compatible": true + }, + { + "names": ["tts", "upside_down"], + "compatible": true + }, + { + "names": ["tts", "nausea"], + "compatible": true + }, + { + "names": ["tts", "round_round_baby"], + "compatible": true + }, + { + "names": ["tts", "simon_says"], + "compatible": false + }, + { + "names": ["tts", "tts"], + "compatible": false + }, + { + "names": ["tts", "choo_choo"], + "compatible": false + }, + { + "names": ["tts", "arrows"], + "compatible": true + }, + { + "names": ["tts", "rAnDoMcAsE"], + "compatible": true + }, + { + "names": ["tts", "sPoNgEcAsE"], + "compatible": true + }, + { + "names": ["tts", "capitals"], + "compatible": true + }, + { + "names": ["tts", "layout_mirror"], + "compatible": true + }, + { + "names": ["tts", "layoutfluid"], + "compatible": true + }, + { + "names": ["tts", "earthquake"], + "compatible": false + }, + { + "names": ["tts", "space_balls"], + "compatible": true + }, + { + "names": ["tts", "gibberish"], + "compatible": false + }, + { + "names": ["tts", "ascii"], + "compatible": false + }, + { + "names": ["tts", "specials"], + "compatible": false + }, + { + "names": ["tts", "plus_zero"], + "compatible": false + }, + { + "names": ["tts", "plus_one"], + "compatible": false + }, + { + "names": ["tts", "plus_two"], + "compatible": false + }, + { + "names": ["tts", "plus_three"], + "compatible": false + }, + { + "names": ["tts", "read_ahead_easy"], + "compatible": false + }, + { + "names": ["tts", "read_ahead"], + "compatible": false + }, + { + "names": ["tts", "read_ahead_hard"], + "compatible": false + }, + { + "names": ["tts", "memory"], + "compatible": false + }, + { + "names": ["tts", "nospace"], + "compatible": true + }, + { + "names": ["tts", "poetry"], + "compatible": true + }, + { + "names": ["tts", "wikipedia"], + "compatible": true + }, + { + "names": ["tts", "weakspot"], + "compatible": true + }, + { + "names": ["tts", "pseudolang"], + "compatible": false + }, + { + "names": ["tts", "IPv4"], + "compatible": true + }, + { + "names": ["tts", "IPv6"], + "compatible": true + }, + { + "names": ["tts", "binary"], + "compatible": true + }, + { + "names": ["tts", "hexadecimal"], + "compatible": true + }, + { + "names": ["tts", "zipf"], + "compatible": true + }, + { + "names": ["tts", "morse"], + "compatible": true + }, + { + "names": ["tts", "crt"], + "compatible": true + }, + { + "names": ["tts", "backwards"], + "compatible": false + }, + { + "names": ["tts", "ddoouubblleedd"], + "compatible": true + }, + { + "names": ["tts", "instant_messaging"], + "compatible": true + }, + { + "names": ["tts", "underscore_spaces"], + "compatible": true + }, + { + "names": ["tts", "ALL_CAPS"], + "compatible": true + }, + { + "names": ["tts", "polyglot"], + "compatible": true + }, + { + "names": ["tts", "asl"], + "compatible": false + }, + { + "names": ["tts", "rot13"], + "compatible": true + }, + { + "names": ["tts", "no_quit"], + "compatible": true + }, + { + "names": ["choo_choo", "58008"], + "compatible": true + }, + { + "names": ["choo_choo", "mirror"], + "compatible": true + }, + { + "names": ["choo_choo", "upside_down"], + "compatible": true + }, + { + "names": ["choo_choo", "nausea"], + "compatible": true + }, + { + "names": ["choo_choo", "round_round_baby"], + "compatible": true + }, + { + "names": ["choo_choo", "simon_says"], + "compatible": true + }, + { + "names": ["choo_choo", "tts"], + "compatible": false + }, + { + "names": ["choo_choo", "choo_choo"], + "compatible": false + }, + { + "names": ["choo_choo", "arrows"], + "compatible": false + }, + { + "names": ["choo_choo", "rAnDoMcAsE"], + "compatible": true + }, + { + "names": ["choo_choo", "sPoNgEcAsE"], + "compatible": true + }, + { + "names": ["choo_choo", "capitals"], + "compatible": true + }, + { + "names": ["choo_choo", "layout_mirror"], + "compatible": true + }, + { + "names": ["choo_choo", "layoutfluid"], + "compatible": true + }, + { + "names": ["choo_choo", "earthquake"], + "compatible": false + }, + { + "names": ["choo_choo", "space_balls"], + "compatible": true + }, + { + "names": ["choo_choo", "gibberish"], + "compatible": true + }, + { + "names": ["choo_choo", "ascii"], + "compatible": true + }, + { + "names": ["choo_choo", "specials"], + "compatible": true + }, + { + "names": ["choo_choo", "plus_zero"], + "compatible": true + }, + { + "names": ["choo_choo", "plus_one"], + "compatible": true + }, + { + "names": ["choo_choo", "plus_two"], + "compatible": true + }, + { + "names": ["choo_choo", "plus_three"], + "compatible": true + }, + { + "names": ["choo_choo", "read_ahead_easy"], + "compatible": true + }, + { + "names": ["choo_choo", "read_ahead"], + "compatible": true + }, + { + "names": ["choo_choo", "read_ahead_hard"], + "compatible": true + }, + { + "names": ["choo_choo", "memory"], + "compatible": true + }, + { + "names": ["choo_choo", "nospace"], + "compatible": true + }, + { + "names": ["choo_choo", "poetry"], + "compatible": true + }, + { + "names": ["choo_choo", "wikipedia"], + "compatible": true + }, + { + "names": ["choo_choo", "weakspot"], + "compatible": true + }, + { + "names": ["choo_choo", "pseudolang"], + "compatible": true + }, + { + "names": ["choo_choo", "IPv4"], + "compatible": true + }, + { + "names": ["choo_choo", "IPv6"], + "compatible": true + }, + { + "names": ["choo_choo", "binary"], + "compatible": true + }, + { + "names": ["choo_choo", "hexadecimal"], + "compatible": true + }, + { + "names": ["choo_choo", "zipf"], + "compatible": true + }, + { + "names": ["choo_choo", "morse"], + "compatible": true + }, + { + "names": ["choo_choo", "crt"], + "compatible": true + }, + { + "names": ["choo_choo", "backwards"], + "compatible": false + }, + { + "names": ["choo_choo", "ddoouubblleedd"], + "compatible": true + }, + { + "names": ["choo_choo", "instant_messaging"], + "compatible": true + }, + { + "names": ["choo_choo", "underscore_spaces"], + "compatible": true + }, + { + "names": ["choo_choo", "ALL_CAPS"], + "compatible": true + }, + { + "names": ["choo_choo", "polyglot"], + "compatible": true + }, + { + "names": ["choo_choo", "asl"], + "compatible": false + }, + { + "names": ["choo_choo", "rot13"], + "compatible": true + }, + { + "names": ["choo_choo", "no_quit"], + "compatible": true + }, + { + "names": ["arrows", "58008"], + "compatible": false + }, + { + "names": ["arrows", "mirror"], + "compatible": true + }, + { + "names": ["arrows", "upside_down"], + "compatible": true + }, + { + "names": ["arrows", "nausea"], + "compatible": true + }, + { + "names": ["arrows", "round_round_baby"], + "compatible": true + }, + { + "names": ["arrows", "simon_says"], + "compatible": true + }, + { + "names": ["arrows", "tts"], + "compatible": true + }, + { + "names": ["arrows", "choo_choo"], + "compatible": false + }, + { + "names": ["arrows", "arrows"], + "compatible": false + }, + { + "names": ["arrows", "rAnDoMcAsE"], + "compatible": false + }, + { + "names": ["arrows", "sPoNgEcAsE"], + "compatible": false + }, + { + "names": ["arrows", "capitals"], + "compatible": false + }, + { + "names": ["arrows", "layout_mirror"], + "compatible": false + }, + { + "names": ["arrows", "layoutfluid"], + "compatible": false + }, + { + "names": ["arrows", "earthquake"], + "compatible": true + }, + { + "names": ["arrows", "space_balls"], + "compatible": true + }, + { + "names": ["arrows", "gibberish"], + "compatible": false + }, + { + "names": ["arrows", "ascii"], + "compatible": false + }, + { + "names": ["arrows", "specials"], + "compatible": false + }, + { + "names": ["arrows", "plus_zero"], + "compatible": false + }, + { + "names": ["arrows", "plus_one"], + "compatible": false + }, + { + "names": ["arrows", "plus_two"], + "compatible": false + }, + { + "names": ["arrows", "plus_three"], + "compatible": false + }, + { + "names": ["arrows", "read_ahead_easy"], + "compatible": true + }, + { + "names": ["arrows", "read_ahead"], + "compatible": true + }, + { + "names": ["arrows", "read_ahead_hard"], + "compatible": true + }, + { + "names": ["arrows", "memory"], + "compatible": true + }, + { + "names": ["arrows", "nospace"], + "compatible": false + }, + { + "names": ["arrows", "poetry"], + "compatible": false + }, + { + "names": ["arrows", "wikipedia"], + "compatible": false + }, + { + "names": ["arrows", "weakspot"], + "compatible": false + }, + { + "names": ["arrows", "pseudolang"], + "compatible": false + }, + { + "names": ["arrows", "IPv4"], + "compatible": false + }, + { + "names": ["arrows", "IPv6"], + "compatible": false + }, + { + "names": ["arrows", "binary"], + "compatible": false + }, + { + "names": ["arrows", "hexadecimal"], + "compatible": false + }, + { + "names": ["arrows", "zipf"], + "compatible": false + }, + { + "names": ["arrows", "morse"], + "compatible": false + }, + { + "names": ["arrows", "crt"], + "compatible": true + }, + { + "names": ["arrows", "backwards"], + "compatible": false + }, + { + "names": ["arrows", "ddoouubblleedd"], + "compatible": true + }, + { + "names": ["arrows", "instant_messaging"], + "compatible": false + }, + { + "names": ["arrows", "underscore_spaces"], + "compatible": false + }, + { + "names": ["arrows", "ALL_CAPS"], + "compatible": false + }, + { + "names": ["arrows", "polyglot"], + "compatible": false + }, + { + "names": ["arrows", "asl"], + "compatible": true + }, + { + "names": ["arrows", "rot13"], + "compatible": true + }, + { + "names": ["arrows", "no_quit"], + "compatible": true + }, + { + "names": ["rAnDoMcAsE", "58008"], + "compatible": false + }, + { + "names": ["rAnDoMcAsE", "mirror"], + "compatible": true + }, + { + "names": ["rAnDoMcAsE", "upside_down"], + "compatible": true + }, + { + "names": ["rAnDoMcAsE", "nausea"], + "compatible": true + }, + { + "names": ["rAnDoMcAsE", "round_round_baby"], + "compatible": true + }, + { + "names": ["rAnDoMcAsE", "simon_says"], + "compatible": true + }, + { + "names": ["rAnDoMcAsE", "tts"], + "compatible": true + }, + { + "names": ["rAnDoMcAsE", "choo_choo"], + "compatible": true + }, + { + "names": ["rAnDoMcAsE", "arrows"], + "compatible": false + }, + { + "names": ["rAnDoMcAsE", "rAnDoMcAsE"], + "compatible": false + }, + { + "names": ["rAnDoMcAsE", "sPoNgEcAsE"], + "compatible": false + }, + { + "names": ["rAnDoMcAsE", "capitals"], + "compatible": false + }, + { + "names": ["rAnDoMcAsE", "layout_mirror"], + "compatible": true + }, + { + "names": ["rAnDoMcAsE", "layoutfluid"], + "compatible": true + }, + { + "names": ["rAnDoMcAsE", "earthquake"], + "compatible": true + }, + { + "names": ["rAnDoMcAsE", "space_balls"], + "compatible": true + }, + { + "names": ["rAnDoMcAsE", "gibberish"], + "compatible": true + }, + { + "names": ["rAnDoMcAsE", "ascii"], + "compatible": false + }, + { + "names": ["rAnDoMcAsE", "specials"], + "compatible": false + }, + { + "names": ["rAnDoMcAsE", "plus_zero"], + "compatible": true + }, + { + "names": ["rAnDoMcAsE", "plus_one"], + "compatible": true + }, + { + "names": ["rAnDoMcAsE", "plus_two"], + "compatible": true + }, + { + "names": ["rAnDoMcAsE", "plus_three"], + "compatible": true + }, + { + "names": ["rAnDoMcAsE", "read_ahead_easy"], + "compatible": true + }, + { + "names": ["rAnDoMcAsE", "read_ahead"], + "compatible": true + }, + { + "names": ["rAnDoMcAsE", "read_ahead_hard"], + "compatible": true + }, + { + "names": ["rAnDoMcAsE", "memory"], + "compatible": true + }, + { + "names": ["rAnDoMcAsE", "nospace"], + "compatible": true + }, + { + "names": ["rAnDoMcAsE", "poetry"], + "compatible": true + }, + { + "names": ["rAnDoMcAsE", "wikipedia"], + "compatible": true + }, + { + "names": ["rAnDoMcAsE", "weakspot"], + "compatible": true + }, + { + "names": ["rAnDoMcAsE", "pseudolang"], + "compatible": true + }, + { + "names": ["rAnDoMcAsE", "IPv4"], + "compatible": false + }, + { + "names": ["rAnDoMcAsE", "IPv6"], + "compatible": false + }, + { + "names": ["rAnDoMcAsE", "binary"], + "compatible": false + }, + { + "names": ["rAnDoMcAsE", "hexadecimal"], + "compatible": false + }, + { + "names": ["rAnDoMcAsE", "zipf"], + "compatible": true + }, + { + "names": ["rAnDoMcAsE", "morse"], + "compatible": false + }, + { + "names": ["rAnDoMcAsE", "crt"], + "compatible": true + }, + { + "names": ["rAnDoMcAsE", "backwards"], + "compatible": true + }, + { + "names": ["rAnDoMcAsE", "ddoouubblleedd"], + "compatible": true + }, + { + "names": ["rAnDoMcAsE", "instant_messaging"], + "compatible": false + }, + { + "names": ["rAnDoMcAsE", "underscore_spaces"], + "compatible": true + }, + { + "names": ["rAnDoMcAsE", "ALL_CAPS"], + "compatible": false + }, + { + "names": ["rAnDoMcAsE", "polyglot"], + "compatible": true + }, + { + "names": ["rAnDoMcAsE", "asl"], + "compatible": true + }, + { + "names": ["rAnDoMcAsE", "rot13"], + "compatible": true + }, + { + "names": ["rAnDoMcAsE", "no_quit"], + "compatible": true + }, + { + "names": ["sPoNgEcAsE", "58008"], + "compatible": false + }, + { + "names": ["sPoNgEcAsE", "mirror"], + "compatible": true + }, + { + "names": ["sPoNgEcAsE", "upside_down"], + "compatible": true + }, + { + "names": ["sPoNgEcAsE", "nausea"], + "compatible": true + }, + { + "names": ["sPoNgEcAsE", "round_round_baby"], + "compatible": true + }, + { + "names": ["sPoNgEcAsE", "simon_says"], + "compatible": true + }, + { + "names": ["sPoNgEcAsE", "tts"], + "compatible": true + }, + { + "names": ["sPoNgEcAsE", "choo_choo"], + "compatible": true + }, + { + "names": ["sPoNgEcAsE", "arrows"], + "compatible": false + }, + { + "names": ["sPoNgEcAsE", "rAnDoMcAsE"], + "compatible": false + }, + { + "names": ["sPoNgEcAsE", "sPoNgEcAsE"], + "compatible": false + }, + { + "names": ["sPoNgEcAsE", "capitals"], + "compatible": false + }, + { + "names": ["sPoNgEcAsE", "layout_mirror"], + "compatible": true + }, + { + "names": ["sPoNgEcAsE", "layoutfluid"], + "compatible": true + }, + { + "names": ["sPoNgEcAsE", "earthquake"], + "compatible": true + }, + { + "names": ["sPoNgEcAsE", "space_balls"], + "compatible": true + }, + { + "names": ["sPoNgEcAsE", "gibberish"], + "compatible": true + }, + { + "names": ["sPoNgEcAsE", "ascii"], + "compatible": false + }, + { + "names": ["sPoNgEcAsE", "specials"], + "compatible": false + }, + { + "names": ["sPoNgEcAsE", "plus_zero"], + "compatible": true + }, + { + "names": ["sPoNgEcAsE", "plus_one"], + "compatible": true + }, + { + "names": ["sPoNgEcAsE", "plus_two"], + "compatible": true + }, + { + "names": ["sPoNgEcAsE", "plus_three"], + "compatible": true + }, + { + "names": ["sPoNgEcAsE", "read_ahead_easy"], + "compatible": true + }, + { + "names": ["sPoNgEcAsE", "read_ahead"], + "compatible": true + }, + { + "names": ["sPoNgEcAsE", "read_ahead_hard"], + "compatible": true + }, + { + "names": ["sPoNgEcAsE", "memory"], + "compatible": true + }, + { + "names": ["sPoNgEcAsE", "nospace"], + "compatible": true + }, + { + "names": ["sPoNgEcAsE", "poetry"], + "compatible": true + }, + { + "names": ["sPoNgEcAsE", "wikipedia"], + "compatible": true + }, + { + "names": ["sPoNgEcAsE", "weakspot"], + "compatible": true + }, + { + "names": ["sPoNgEcAsE", "pseudolang"], + "compatible": true + }, + { + "names": ["sPoNgEcAsE", "IPv4"], + "compatible": false + }, + { + "names": ["sPoNgEcAsE", "IPv6"], + "compatible": false + }, + { + "names": ["sPoNgEcAsE", "binary"], + "compatible": false + }, + { + "names": ["sPoNgEcAsE", "hexadecimal"], + "compatible": false + }, + { + "names": ["sPoNgEcAsE", "zipf"], + "compatible": true + }, + { + "names": ["sPoNgEcAsE", "morse"], + "compatible": false + }, + { + "names": ["sPoNgEcAsE", "crt"], + "compatible": true + }, + { + "names": ["sPoNgEcAsE", "backwards"], + "compatible": true + }, + { + "names": ["sPoNgEcAsE", "ddoouubblleedd"], + "compatible": true + }, + { + "names": ["sPoNgEcAsE", "instant_messaging"], + "compatible": false + }, + { + "names": ["sPoNgEcAsE", "underscore_spaces"], + "compatible": true + }, + { + "names": ["sPoNgEcAsE", "ALL_CAPS"], + "compatible": false + }, + { + "names": ["sPoNgEcAsE", "polyglot"], + "compatible": true + }, + { + "names": ["sPoNgEcAsE", "asl"], + "compatible": true + }, + { + "names": ["sPoNgEcAsE", "rot13"], + "compatible": true + }, + { + "names": ["sPoNgEcAsE", "no_quit"], + "compatible": true + }, + { + "names": ["capitals", "58008"], + "compatible": false + }, + { + "names": ["capitals", "mirror"], + "compatible": true + }, + { + "names": ["capitals", "upside_down"], + "compatible": true + }, + { + "names": ["capitals", "nausea"], + "compatible": true + }, + { + "names": ["capitals", "round_round_baby"], + "compatible": true + }, + { + "names": ["capitals", "simon_says"], + "compatible": true + }, + { + "names": ["capitals", "tts"], + "compatible": true + }, + { + "names": ["capitals", "choo_choo"], + "compatible": true + }, + { + "names": ["capitals", "arrows"], + "compatible": false + }, + { + "names": ["capitals", "rAnDoMcAsE"], + "compatible": false + }, + { + "names": ["capitals", "sPoNgEcAsE"], + "compatible": false + }, + { + "names": ["capitals", "capitals"], + "compatible": false + }, + { + "names": ["capitals", "layout_mirror"], + "compatible": true + }, + { + "names": ["capitals", "layoutfluid"], + "compatible": true + }, + { + "names": ["capitals", "earthquake"], + "compatible": true + }, + { + "names": ["capitals", "space_balls"], + "compatible": true + }, + { + "names": ["capitals", "gibberish"], + "compatible": true + }, + { + "names": ["capitals", "ascii"], + "compatible": false + }, + { + "names": ["capitals", "specials"], + "compatible": false + }, + { + "names": ["capitals", "plus_zero"], + "compatible": true + }, + { + "names": ["capitals", "plus_one"], + "compatible": true + }, + { + "names": ["capitals", "plus_two"], + "compatible": true + }, + { + "names": ["capitals", "plus_three"], + "compatible": true + }, + { + "names": ["capitals", "read_ahead_easy"], + "compatible": true + }, + { + "names": ["capitals", "read_ahead"], + "compatible": true + }, + { + "names": ["capitals", "read_ahead_hard"], + "compatible": true + }, + { + "names": ["capitals", "memory"], + "compatible": true + }, + { + "names": ["capitals", "nospace"], + "compatible": true + }, + { + "names": ["capitals", "poetry"], + "compatible": true + }, + { + "names": ["capitals", "wikipedia"], + "compatible": true + }, + { + "names": ["capitals", "weakspot"], + "compatible": true + }, + { + "names": ["capitals", "pseudolang"], + "compatible": true + }, + { + "names": ["capitals", "IPv4"], + "compatible": false + }, + { + "names": ["capitals", "IPv6"], + "compatible": false + }, + { + "names": ["capitals", "binary"], + "compatible": false + }, + { + "names": ["capitals", "hexadecimal"], + "compatible": false + }, + { + "names": ["capitals", "zipf"], + "compatible": true + }, + { + "names": ["capitals", "morse"], + "compatible": false + }, + { + "names": ["capitals", "crt"], + "compatible": true + }, + { + "names": ["capitals", "backwards"], + "compatible": true + }, + { + "names": ["capitals", "ddoouubblleedd"], + "compatible": true + }, + { + "names": ["capitals", "instant_messaging"], + "compatible": false + }, + { + "names": ["capitals", "underscore_spaces"], + "compatible": true + }, + { + "names": ["capitals", "ALL_CAPS"], + "compatible": false + }, + { + "names": ["capitals", "polyglot"], + "compatible": true + }, + { + "names": ["capitals", "asl"], + "compatible": true + }, + { + "names": ["capitals", "rot13"], + "compatible": true + }, + { + "names": ["capitals", "no_quit"], + "compatible": true + }, + { + "names": ["layout_mirror", "58008"], + "compatible": false + }, + { + "names": ["layout_mirror", "mirror"], + "compatible": true + }, + { + "names": ["layout_mirror", "upside_down"], + "compatible": true + }, + { + "names": ["layout_mirror", "nausea"], + "compatible": true + }, + { + "names": ["layout_mirror", "round_round_baby"], + "compatible": true + }, + { + "names": ["layout_mirror", "simon_says"], + "compatible": false + }, + { + "names": ["layout_mirror", "tts"], + "compatible": true + }, + { + "names": ["layout_mirror", "choo_choo"], + "compatible": true + }, + { + "names": ["layout_mirror", "arrows"], + "compatible": false + }, + { + "names": ["layout_mirror", "rAnDoMcAsE"], + "compatible": true + }, + { + "names": ["layout_mirror", "sPoNgEcAsE"], + "compatible": true + }, + { + "names": ["layout_mirror", "capitals"], + "compatible": true + }, + { + "names": ["layout_mirror", "layout_mirror"], + "compatible": true + }, + { + "names": ["layout_mirror", "layoutfluid"], + "compatible": true + }, + { + "names": ["layout_mirror", "earthquake"], + "compatible": true + }, + { + "names": ["layout_mirror", "space_balls"], + "compatible": true + }, + { + "names": ["layout_mirror", "gibberish"], + "compatible": true + }, + { + "names": ["layout_mirror", "ascii"], + "compatible": true + }, + { + "names": ["layout_mirror", "specials"], + "compatible": true + }, + { + "names": ["layout_mirror", "plus_zero"], + "compatible": true + }, + { + "names": ["layout_mirror", "plus_one"], + "compatible": true + }, + { + "names": ["layout_mirror", "plus_two"], + "compatible": true + }, + { + "names": ["layout_mirror", "plus_three"], + "compatible": true + }, + { + "names": ["layout_mirror", "read_ahead_easy"], + "compatible": true + }, + { + "names": ["layout_mirror", "read_ahead"], + "compatible": true + }, + { + "names": ["layout_mirror", "read_ahead_hard"], + "compatible": true + }, + { + "names": ["layout_mirror", "memory"], + "compatible": true + }, + { + "names": ["layout_mirror", "nospace"], + "compatible": true + }, + { + "names": ["layout_mirror", "poetry"], + "compatible": true + }, + { + "names": ["layout_mirror", "wikipedia"], + "compatible": true + }, + { + "names": ["layout_mirror", "weakspot"], + "compatible": true + }, + { + "names": ["layout_mirror", "pseudolang"], + "compatible": true + }, + { + "names": ["layout_mirror", "IPv4"], + "compatible": false + }, + { + "names": ["layout_mirror", "IPv6"], + "compatible": false + }, + { + "names": ["layout_mirror", "binary"], + "compatible": false + }, + { + "names": ["layout_mirror", "hexadecimal"], + "compatible": false + }, + { + "names": ["layout_mirror", "zipf"], + "compatible": true + }, + { + "names": ["layout_mirror", "morse"], + "compatible": false + }, + { + "names": ["layout_mirror", "crt"], + "compatible": true + }, + { + "names": ["layout_mirror", "backwards"], + "compatible": true + }, + { + "names": ["layout_mirror", "ddoouubblleedd"], + "compatible": true + }, + { + "names": ["layout_mirror", "instant_messaging"], + "compatible": true + }, + { + "names": ["layout_mirror", "underscore_spaces"], + "compatible": false + }, + { + "names": ["layout_mirror", "ALL_CAPS"], + "compatible": true + }, + { + "names": ["layout_mirror", "polyglot"], + "compatible": true + }, + { + "names": ["layout_mirror", "asl"], + "compatible": true + }, + { + "names": ["layout_mirror", "rot13"], + "compatible": true + }, + { + "names": ["layout_mirror", "no_quit"], + "compatible": true + }, + { + "names": ["layoutfluid", "58008"], + "compatible": false + }, + { + "names": ["layoutfluid", "mirror"], + "compatible": true + }, + { + "names": ["layoutfluid", "upside_down"], + "compatible": true + }, + { + "names": ["layoutfluid", "nausea"], + "compatible": true + }, + { + "names": ["layoutfluid", "round_round_baby"], + "compatible": true + }, + { + "names": ["layoutfluid", "simon_says"], + "compatible": false + }, + { + "names": ["layoutfluid", "tts"], + "compatible": true + }, + { + "names": ["layoutfluid", "choo_choo"], + "compatible": true + }, + { + "names": ["layoutfluid", "arrows"], + "compatible": false + }, + { + "names": ["layoutfluid", "rAnDoMcAsE"], + "compatible": true + }, + { + "names": ["layoutfluid", "sPoNgEcAsE"], + "compatible": true + }, + { + "names": ["layoutfluid", "capitals"], + "compatible": true + }, + { + "names": ["layoutfluid", "layout_mirror"], + "compatible": true + }, + { + "names": ["layoutfluid", "layoutfluid"], + "compatible": true + }, + { + "names": ["layoutfluid", "earthquake"], + "compatible": true + }, + { + "names": ["layoutfluid", "space_balls"], + "compatible": true + }, + { + "names": ["layoutfluid", "gibberish"], + "compatible": true + }, + { + "names": ["layoutfluid", "ascii"], + "compatible": true + }, + { + "names": ["layoutfluid", "specials"], + "compatible": true + }, + { + "names": ["layoutfluid", "plus_zero"], + "compatible": true + }, + { + "names": ["layoutfluid", "plus_one"], + "compatible": true + }, + { + "names": ["layoutfluid", "plus_two"], + "compatible": true + }, + { + "names": ["layoutfluid", "plus_three"], + "compatible": true + }, + { + "names": ["layoutfluid", "read_ahead_easy"], + "compatible": true + }, + { + "names": ["layoutfluid", "read_ahead"], + "compatible": true + }, + { + "names": ["layoutfluid", "read_ahead_hard"], + "compatible": true + }, + { + "names": ["layoutfluid", "memory"], + "compatible": true + }, + { + "names": ["layoutfluid", "nospace"], + "compatible": true + }, + { + "names": ["layoutfluid", "poetry"], + "compatible": true + }, + { + "names": ["layoutfluid", "wikipedia"], + "compatible": true + }, + { + "names": ["layoutfluid", "weakspot"], + "compatible": true + }, + { + "names": ["layoutfluid", "pseudolang"], + "compatible": true + }, + { + "names": ["layoutfluid", "IPv4"], + "compatible": false + }, + { + "names": ["layoutfluid", "IPv6"], + "compatible": false + }, + { + "names": ["layoutfluid", "binary"], + "compatible": false + }, + { + "names": ["layoutfluid", "hexadecimal"], + "compatible": false + }, + { + "names": ["layoutfluid", "zipf"], + "compatible": true + }, + { + "names": ["layoutfluid", "morse"], + "compatible": false + }, + { + "names": ["layoutfluid", "crt"], + "compatible": true + }, + { + "names": ["layoutfluid", "backwards"], + "compatible": true + }, + { + "names": ["layoutfluid", "ddoouubblleedd"], + "compatible": true + }, + { + "names": ["layoutfluid", "instant_messaging"], + "compatible": true + }, + { + "names": ["layoutfluid", "underscore_spaces"], + "compatible": false + }, + { + "names": ["layoutfluid", "ALL_CAPS"], + "compatible": true + }, + { + "names": ["layoutfluid", "polyglot"], + "compatible": true + }, + { + "names": ["layoutfluid", "asl"], + "compatible": true + }, + { + "names": ["layoutfluid", "rot13"], + "compatible": true + }, + { + "names": ["layoutfluid", "no_quit"], + "compatible": true + }, + { + "names": ["earthquake", "58008"], + "compatible": true + }, + { + "names": ["earthquake", "mirror"], + "compatible": true + }, + { + "names": ["earthquake", "upside_down"], + "compatible": true + }, + { + "names": ["earthquake", "nausea"], + "compatible": true + }, + { + "names": ["earthquake", "round_round_baby"], + "compatible": true + }, + { + "names": ["earthquake", "simon_says"], + "compatible": true + }, + { + "names": ["earthquake", "tts"], + "compatible": false + }, + { + "names": ["earthquake", "choo_choo"], + "compatible": false + }, + { + "names": ["earthquake", "arrows"], + "compatible": true + }, + { + "names": ["earthquake", "rAnDoMcAsE"], + "compatible": true + }, + { + "names": ["earthquake", "sPoNgEcAsE"], + "compatible": true + }, + { + "names": ["earthquake", "capitals"], + "compatible": true + }, + { + "names": ["earthquake", "layout_mirror"], + "compatible": true + }, + { + "names": ["earthquake", "layoutfluid"], + "compatible": true + }, + { + "names": ["earthquake", "earthquake"], + "compatible": false + }, + { + "names": ["earthquake", "space_balls"], + "compatible": true + }, + { + "names": ["earthquake", "gibberish"], + "compatible": true + }, + { + "names": ["earthquake", "ascii"], + "compatible": true + }, + { + "names": ["earthquake", "specials"], + "compatible": true + }, + { + "names": ["earthquake", "plus_zero"], + "compatible": true + }, + { + "names": ["earthquake", "plus_one"], + "compatible": true + }, + { + "names": ["earthquake", "plus_two"], + "compatible": true + }, + { + "names": ["earthquake", "plus_three"], + "compatible": true + }, + { + "names": ["earthquake", "read_ahead_easy"], + "compatible": true + }, + { + "names": ["earthquake", "read_ahead"], + "compatible": true + }, + { + "names": ["earthquake", "read_ahead_hard"], + "compatible": true + }, + { + "names": ["earthquake", "memory"], + "compatible": true + }, + { + "names": ["earthquake", "nospace"], + "compatible": true + }, + { + "names": ["earthquake", "poetry"], + "compatible": true + }, + { + "names": ["earthquake", "wikipedia"], + "compatible": true + }, + { + "names": ["earthquake", "weakspot"], + "compatible": true + }, + { + "names": ["earthquake", "pseudolang"], + "compatible": true + }, + { + "names": ["earthquake", "IPv4"], + "compatible": true + }, + { + "names": ["earthquake", "IPv6"], + "compatible": true + }, + { + "names": ["earthquake", "binary"], + "compatible": true + }, + { + "names": ["earthquake", "hexadecimal"], + "compatible": true + }, + { + "names": ["earthquake", "zipf"], + "compatible": true + }, + { + "names": ["earthquake", "morse"], + "compatible": true + }, + { + "names": ["earthquake", "crt"], + "compatible": true + }, + { + "names": ["earthquake", "backwards"], + "compatible": false + }, + { + "names": ["earthquake", "ddoouubblleedd"], + "compatible": true + }, + { + "names": ["earthquake", "instant_messaging"], + "compatible": true + }, + { + "names": ["earthquake", "underscore_spaces"], + "compatible": true + }, + { + "names": ["earthquake", "ALL_CAPS"], + "compatible": true + }, + { + "names": ["earthquake", "polyglot"], + "compatible": true + }, + { + "names": ["earthquake", "asl"], + "compatible": false + }, + { + "names": ["earthquake", "rot13"], + "compatible": true + }, + { + "names": ["earthquake", "no_quit"], + "compatible": true + }, + { + "names": ["space_balls", "58008"], + "compatible": true + }, + { + "names": ["space_balls", "mirror"], + "compatible": true + }, + { + "names": ["space_balls", "upside_down"], + "compatible": true + }, + { + "names": ["space_balls", "nausea"], + "compatible": true + }, + { + "names": ["space_balls", "round_round_baby"], + "compatible": true + }, + { + "names": ["space_balls", "simon_says"], + "compatible": true + }, + { + "names": ["space_balls", "tts"], + "compatible": true + }, + { + "names": ["space_balls", "choo_choo"], + "compatible": true + }, + { + "names": ["space_balls", "arrows"], + "compatible": true + }, + { + "names": ["space_balls", "rAnDoMcAsE"], + "compatible": true + }, + { + "names": ["space_balls", "sPoNgEcAsE"], + "compatible": true + }, + { + "names": ["space_balls", "capitals"], + "compatible": true + }, + { + "names": ["space_balls", "layout_mirror"], + "compatible": true + }, + { + "names": ["space_balls", "layoutfluid"], + "compatible": true + }, + { + "names": ["space_balls", "earthquake"], + "compatible": true + }, + { + "names": ["space_balls", "space_balls"], + "compatible": false + }, + { + "names": ["space_balls", "gibberish"], + "compatible": true + }, + { + "names": ["space_balls", "ascii"], + "compatible": true + }, + { + "names": ["space_balls", "specials"], + "compatible": true + }, + { + "names": ["space_balls", "plus_zero"], + "compatible": true + }, + { + "names": ["space_balls", "plus_one"], + "compatible": true + }, + { + "names": ["space_balls", "plus_two"], + "compatible": true + }, + { + "names": ["space_balls", "plus_three"], + "compatible": true + }, + { + "names": ["space_balls", "read_ahead_easy"], + "compatible": true + }, + { + "names": ["space_balls", "read_ahead"], + "compatible": true + }, + { + "names": ["space_balls", "read_ahead_hard"], + "compatible": true + }, + { + "names": ["space_balls", "memory"], + "compatible": true + }, + { + "names": ["space_balls", "nospace"], + "compatible": true + }, + { + "names": ["space_balls", "poetry"], + "compatible": true + }, + { + "names": ["space_balls", "wikipedia"], + "compatible": true + }, + { + "names": ["space_balls", "weakspot"], + "compatible": true + }, + { + "names": ["space_balls", "pseudolang"], + "compatible": true + }, + { + "names": ["space_balls", "IPv4"], + "compatible": true + }, + { + "names": ["space_balls", "IPv6"], + "compatible": true + }, + { + "names": ["space_balls", "binary"], + "compatible": true + }, + { + "names": ["space_balls", "hexadecimal"], + "compatible": true + }, + { + "names": ["space_balls", "zipf"], + "compatible": true + }, + { + "names": ["space_balls", "morse"], + "compatible": true + }, + { + "names": ["space_balls", "crt"], + "compatible": false + }, + { + "names": ["space_balls", "backwards"], + "compatible": true + }, + { + "names": ["space_balls", "ddoouubblleedd"], + "compatible": true + }, + { + "names": ["space_balls", "instant_messaging"], + "compatible": true + }, + { + "names": ["space_balls", "underscore_spaces"], + "compatible": true + }, + { + "names": ["space_balls", "ALL_CAPS"], + "compatible": true + }, + { + "names": ["space_balls", "polyglot"], + "compatible": true + }, + { + "names": ["space_balls", "asl"], + "compatible": true + }, + { + "names": ["space_balls", "rot13"], + "compatible": true + }, + { + "names": ["space_balls", "no_quit"], + "compatible": true + }, + { + "names": ["gibberish", "58008"], + "compatible": false + }, + { + "names": ["gibberish", "mirror"], + "compatible": true + }, + { + "names": ["gibberish", "upside_down"], + "compatible": true + }, + { + "names": ["gibberish", "nausea"], + "compatible": true + }, + { + "names": ["gibberish", "round_round_baby"], + "compatible": true + }, + { + "names": ["gibberish", "simon_says"], + "compatible": true + }, + { + "names": ["gibberish", "tts"], + "compatible": false + }, + { + "names": ["gibberish", "choo_choo"], + "compatible": true + }, + { + "names": ["gibberish", "arrows"], + "compatible": false + }, + { + "names": ["gibberish", "rAnDoMcAsE"], + "compatible": true + }, + { + "names": ["gibberish", "sPoNgEcAsE"], + "compatible": true + }, + { + "names": ["gibberish", "capitals"], + "compatible": true + }, + { + "names": ["gibberish", "layout_mirror"], + "compatible": true + }, + { + "names": ["gibberish", "layoutfluid"], + "compatible": true + }, + { + "names": ["gibberish", "earthquake"], + "compatible": true + }, + { + "names": ["gibberish", "space_balls"], + "compatible": true + }, + { + "names": ["gibberish", "gibberish"], + "compatible": false + }, + { + "names": ["gibberish", "ascii"], + "compatible": false + }, + { + "names": ["gibberish", "specials"], + "compatible": false + }, + { + "names": ["gibberish", "plus_zero"], + "compatible": true + }, + { + "names": ["gibberish", "plus_one"], + "compatible": true + }, + { + "names": ["gibberish", "plus_two"], + "compatible": true + }, + { + "names": ["gibberish", "plus_three"], + "compatible": true + }, + { + "names": ["gibberish", "read_ahead_easy"], + "compatible": true + }, + { + "names": ["gibberish", "read_ahead"], + "compatible": true + }, + { + "names": ["gibberish", "read_ahead_hard"], + "compatible": true + }, + { + "names": ["gibberish", "memory"], + "compatible": true + }, + { + "names": ["gibberish", "nospace"], + "compatible": true + }, + { + "names": ["gibberish", "poetry"], + "compatible": false + }, + { + "names": ["gibberish", "wikipedia"], + "compatible": false + }, + { + "names": ["gibberish", "weakspot"], + "compatible": false + }, + { + "names": ["gibberish", "pseudolang"], + "compatible": false + }, + { + "names": ["gibberish", "IPv4"], + "compatible": false + }, + { + "names": ["gibberish", "IPv6"], + "compatible": false + }, + { + "names": ["gibberish", "binary"], + "compatible": false + }, + { + "names": ["gibberish", "hexadecimal"], + "compatible": false + }, + { + "names": ["gibberish", "zipf"], + "compatible": false + }, + { + "names": ["gibberish", "morse"], + "compatible": true + }, + { + "names": ["gibberish", "crt"], + "compatible": true + }, + { + "names": ["gibberish", "backwards"], + "compatible": true + }, + { + "names": ["gibberish", "ddoouubblleedd"], + "compatible": true + }, + { + "names": ["gibberish", "instant_messaging"], + "compatible": true + }, + { + "names": ["gibberish", "underscore_spaces"], + "compatible": true + }, + { + "names": ["gibberish", "ALL_CAPS"], + "compatible": true + }, + { + "names": ["gibberish", "polyglot"], + "compatible": false + }, + { + "names": ["gibberish", "asl"], + "compatible": true + }, + { + "names": ["gibberish", "rot13"], + "compatible": true + }, + { + "names": ["gibberish", "no_quit"], + "compatible": true + }, + { + "names": ["ascii", "58008"], + "compatible": false + }, + { + "names": ["ascii", "mirror"], + "compatible": true + }, + { + "names": ["ascii", "upside_down"], + "compatible": true + }, + { + "names": ["ascii", "nausea"], + "compatible": true + }, + { + "names": ["ascii", "round_round_baby"], + "compatible": true + }, + { + "names": ["ascii", "simon_says"], + "compatible": true + }, + { + "names": ["ascii", "tts"], + "compatible": false + }, + { + "names": ["ascii", "choo_choo"], + "compatible": true + }, + { + "names": ["ascii", "arrows"], + "compatible": false + }, + { + "names": ["ascii", "rAnDoMcAsE"], + "compatible": false + }, + { + "names": ["ascii", "sPoNgEcAsE"], + "compatible": false + }, + { + "names": ["ascii", "capitals"], + "compatible": false + }, + { + "names": ["ascii", "layout_mirror"], + "compatible": true + }, + { + "names": ["ascii", "layoutfluid"], + "compatible": true + }, + { + "names": ["ascii", "earthquake"], + "compatible": true + }, + { + "names": ["ascii", "space_balls"], + "compatible": true + }, + { + "names": ["ascii", "gibberish"], + "compatible": false + }, + { + "names": ["ascii", "ascii"], + "compatible": false + }, + { + "names": ["ascii", "specials"], + "compatible": false + }, + { + "names": ["ascii", "plus_zero"], + "compatible": true + }, + { + "names": ["ascii", "plus_one"], + "compatible": true + }, + { + "names": ["ascii", "plus_two"], + "compatible": true + }, + { + "names": ["ascii", "plus_three"], + "compatible": true + }, + { + "names": ["ascii", "read_ahead_easy"], + "compatible": true + }, + { + "names": ["ascii", "read_ahead"], + "compatible": true + }, + { + "names": ["ascii", "read_ahead_hard"], + "compatible": true + }, + { + "names": ["ascii", "memory"], + "compatible": true + }, + { + "names": ["ascii", "nospace"], + "compatible": true + }, + { + "names": ["ascii", "poetry"], + "compatible": false + }, + { + "names": ["ascii", "wikipedia"], + "compatible": false + }, + { + "names": ["ascii", "weakspot"], + "compatible": false + }, + { + "names": ["ascii", "pseudolang"], + "compatible": false + }, + { + "names": ["ascii", "IPv4"], + "compatible": false + }, + { + "names": ["ascii", "IPv6"], + "compatible": false + }, + { + "names": ["ascii", "binary"], + "compatible": false + }, + { + "names": ["ascii", "hexadecimal"], + "compatible": false + }, + { + "names": ["ascii", "zipf"], + "compatible": false + }, + { + "names": ["ascii", "morse"], + "compatible": true + }, + { + "names": ["ascii", "crt"], + "compatible": true + }, + { + "names": ["ascii", "backwards"], + "compatible": true + }, + { + "names": ["ascii", "ddoouubblleedd"], + "compatible": true + }, + { + "names": ["ascii", "instant_messaging"], + "compatible": false + }, + { + "names": ["ascii", "underscore_spaces"], + "compatible": true + }, + { + "names": ["ascii", "ALL_CAPS"], + "compatible": false + }, + { + "names": ["ascii", "polyglot"], + "compatible": false + }, + { + "names": ["ascii", "asl"], + "compatible": true + }, + { + "names": ["ascii", "rot13"], + "compatible": true + }, + { + "names": ["ascii", "no_quit"], + "compatible": true + }, + { + "names": ["specials", "58008"], + "compatible": false + }, + { + "names": ["specials", "mirror"], + "compatible": true + }, + { + "names": ["specials", "upside_down"], + "compatible": true + }, + { + "names": ["specials", "nausea"], + "compatible": true + }, + { + "names": ["specials", "round_round_baby"], + "compatible": true + }, + { + "names": ["specials", "simon_says"], + "compatible": true + }, + { + "names": ["specials", "tts"], + "compatible": false + }, + { + "names": ["specials", "choo_choo"], + "compatible": true + }, + { + "names": ["specials", "arrows"], + "compatible": false + }, + { + "names": ["specials", "rAnDoMcAsE"], + "compatible": false + }, + { + "names": ["specials", "sPoNgEcAsE"], + "compatible": false + }, + { + "names": ["specials", "capitals"], + "compatible": false + }, + { + "names": ["specials", "layout_mirror"], + "compatible": true + }, + { + "names": ["specials", "layoutfluid"], + "compatible": true + }, + { + "names": ["specials", "earthquake"], + "compatible": true + }, + { + "names": ["specials", "space_balls"], + "compatible": true + }, + { + "names": ["specials", "gibberish"], + "compatible": false + }, + { + "names": ["specials", "ascii"], + "compatible": false + }, + { + "names": ["specials", "specials"], + "compatible": false + }, + { + "names": ["specials", "plus_zero"], + "compatible": true + }, + { + "names": ["specials", "plus_one"], + "compatible": true + }, + { + "names": ["specials", "plus_two"], + "compatible": true + }, + { + "names": ["specials", "plus_three"], + "compatible": true + }, + { + "names": ["specials", "read_ahead_easy"], + "compatible": true + }, + { + "names": ["specials", "read_ahead"], + "compatible": true + }, + { + "names": ["specials", "read_ahead_hard"], + "compatible": true + }, + { + "names": ["specials", "memory"], + "compatible": true + }, + { + "names": ["specials", "nospace"], + "compatible": true + }, + { + "names": ["specials", "poetry"], + "compatible": false + }, + { + "names": ["specials", "wikipedia"], + "compatible": false + }, + { + "names": ["specials", "weakspot"], + "compatible": false + }, + { + "names": ["specials", "pseudolang"], + "compatible": false + }, + { + "names": ["specials", "IPv4"], + "compatible": false + }, + { + "names": ["specials", "IPv6"], + "compatible": false + }, + { + "names": ["specials", "binary"], + "compatible": false + }, + { + "names": ["specials", "hexadecimal"], + "compatible": false + }, + { + "names": ["specials", "zipf"], + "compatible": false + }, + { + "names": ["specials", "morse"], + "compatible": true + }, + { + "names": ["specials", "crt"], + "compatible": true + }, + { + "names": ["specials", "backwards"], + "compatible": true + }, + { + "names": ["specials", "ddoouubblleedd"], + "compatible": true + }, + { + "names": ["specials", "instant_messaging"], + "compatible": false + }, + { + "names": ["specials", "underscore_spaces"], + "compatible": true + }, + { + "names": ["specials", "ALL_CAPS"], + "compatible": false + }, + { + "names": ["specials", "polyglot"], + "compatible": false + }, + { + "names": ["specials", "asl"], + "compatible": true + }, + { + "names": ["specials", "rot13"], + "compatible": true + }, + { + "names": ["specials", "no_quit"], + "compatible": true + }, + { + "names": ["plus_zero", "58008"], + "compatible": true + }, + { + "names": ["plus_zero", "mirror"], + "compatible": true + }, + { + "names": ["plus_zero", "upside_down"], + "compatible": true + }, + { + "names": ["plus_zero", "nausea"], + "compatible": true + }, + { + "names": ["plus_zero", "round_round_baby"], + "compatible": true + }, + { + "names": ["plus_zero", "simon_says"], + "compatible": false + }, + { + "names": ["plus_zero", "tts"], + "compatible": false + }, + { + "names": ["plus_zero", "choo_choo"], + "compatible": true + }, + { + "names": ["plus_zero", "arrows"], + "compatible": false + }, + { + "names": ["plus_zero", "rAnDoMcAsE"], + "compatible": true + }, + { + "names": ["plus_zero", "sPoNgEcAsE"], + "compatible": true + }, + { + "names": ["plus_zero", "capitals"], + "compatible": true + }, + { + "names": ["plus_zero", "layout_mirror"], + "compatible": true + }, + { + "names": ["plus_zero", "layoutfluid"], + "compatible": true + }, + { + "names": ["plus_zero", "earthquake"], + "compatible": true + }, + { + "names": ["plus_zero", "space_balls"], + "compatible": true + }, + { + "names": ["plus_zero", "gibberish"], + "compatible": true + }, + { + "names": ["plus_zero", "ascii"], + "compatible": true + }, + { + "names": ["plus_zero", "specials"], + "compatible": true + }, + { + "names": ["plus_zero", "plus_zero"], + "compatible": false + }, + { + "names": ["plus_zero", "plus_one"], + "compatible": false + }, + { + "names": ["plus_zero", "plus_two"], + "compatible": false + }, + { + "names": ["plus_zero", "plus_three"], + "compatible": false + }, + { + "names": ["plus_zero", "read_ahead_easy"], + "compatible": false + }, + { + "names": ["plus_zero", "read_ahead"], + "compatible": false + }, + { + "names": ["plus_zero", "read_ahead_hard"], + "compatible": false + }, + { + "names": ["plus_zero", "memory"], + "compatible": false + }, + { + "names": ["plus_zero", "nospace"], + "compatible": false + }, + { + "names": ["plus_zero", "poetry"], + "compatible": false + }, + { + "names": ["plus_zero", "wikipedia"], + "compatible": false + }, + { + "names": ["plus_zero", "weakspot"], + "compatible": true + }, + { + "names": ["plus_zero", "pseudolang"], + "compatible": true + }, + { + "names": ["plus_zero", "IPv4"], + "compatible": true + }, + { + "names": ["plus_zero", "IPv6"], + "compatible": true + }, + { + "names": ["plus_zero", "binary"], + "compatible": true + }, + { + "names": ["plus_zero", "hexadecimal"], + "compatible": true + }, + { + "names": ["plus_zero", "zipf"], + "compatible": true + }, + { + "names": ["plus_zero", "morse"], + "compatible": false + }, + { + "names": ["plus_zero", "crt"], + "compatible": true + }, + { + "names": ["plus_zero", "backwards"], + "compatible": true + }, + { + "names": ["plus_zero", "ddoouubblleedd"], + "compatible": true + }, + { + "names": ["plus_zero", "instant_messaging"], + "compatible": true + }, + { + "names": ["plus_zero", "underscore_spaces"], + "compatible": false + }, + { + "names": ["plus_zero", "ALL_CAPS"], + "compatible": true + }, + { + "names": ["plus_zero", "polyglot"], + "compatible": true + }, + { + "names": ["plus_zero", "asl"], + "compatible": true + }, + { + "names": ["plus_zero", "rot13"], + "compatible": true + }, + { + "names": ["plus_zero", "no_quit"], + "compatible": true + }, + { + "names": ["plus_one", "58008"], + "compatible": true + }, + { + "names": ["plus_one", "mirror"], + "compatible": true + }, + { + "names": ["plus_one", "upside_down"], + "compatible": true + }, + { + "names": ["plus_one", "nausea"], + "compatible": true + }, + { + "names": ["plus_one", "round_round_baby"], + "compatible": true + }, + { + "names": ["plus_one", "simon_says"], + "compatible": false + }, + { + "names": ["plus_one", "tts"], + "compatible": false + }, + { + "names": ["plus_one", "choo_choo"], + "compatible": true + }, + { + "names": ["plus_one", "arrows"], + "compatible": false + }, + { + "names": ["plus_one", "rAnDoMcAsE"], + "compatible": true + }, + { + "names": ["plus_one", "sPoNgEcAsE"], + "compatible": true + }, + { + "names": ["plus_one", "capitals"], + "compatible": true + }, + { + "names": ["plus_one", "layout_mirror"], + "compatible": true + }, + { + "names": ["plus_one", "layoutfluid"], + "compatible": true + }, + { + "names": ["plus_one", "earthquake"], + "compatible": true + }, + { + "names": ["plus_one", "space_balls"], + "compatible": true + }, + { + "names": ["plus_one", "gibberish"], + "compatible": true + }, + { + "names": ["plus_one", "ascii"], + "compatible": true + }, + { + "names": ["plus_one", "specials"], + "compatible": true + }, + { + "names": ["plus_one", "plus_zero"], + "compatible": false + }, + { + "names": ["plus_one", "plus_one"], + "compatible": false + }, + { + "names": ["plus_one", "plus_two"], + "compatible": false + }, + { + "names": ["plus_one", "plus_three"], + "compatible": false + }, + { + "names": ["plus_one", "read_ahead_easy"], + "compatible": false + }, + { + "names": ["plus_one", "read_ahead"], + "compatible": false + }, + { + "names": ["plus_one", "read_ahead_hard"], + "compatible": false + }, + { + "names": ["plus_one", "memory"], + "compatible": false + }, + { + "names": ["plus_one", "nospace"], + "compatible": false + }, + { + "names": ["plus_one", "poetry"], + "compatible": false + }, + { + "names": ["plus_one", "wikipedia"], + "compatible": false + }, + { + "names": ["plus_one", "weakspot"], + "compatible": true + }, + { + "names": ["plus_one", "pseudolang"], + "compatible": true + }, + { + "names": ["plus_one", "IPv4"], + "compatible": true + }, + { + "names": ["plus_one", "IPv6"], + "compatible": true + }, + { + "names": ["plus_one", "binary"], + "compatible": true + }, + { + "names": ["plus_one", "hexadecimal"], + "compatible": true + }, + { + "names": ["plus_one", "zipf"], + "compatible": true + }, + { + "names": ["plus_one", "morse"], + "compatible": false + }, + { + "names": ["plus_one", "crt"], + "compatible": true + }, + { + "names": ["plus_one", "backwards"], + "compatible": true + }, + { + "names": ["plus_one", "ddoouubblleedd"], + "compatible": true + }, + { + "names": ["plus_one", "instant_messaging"], + "compatible": true + }, + { + "names": ["plus_one", "underscore_spaces"], + "compatible": false + }, + { + "names": ["plus_one", "ALL_CAPS"], + "compatible": true + }, + { + "names": ["plus_one", "polyglot"], + "compatible": true + }, + { + "names": ["plus_one", "asl"], + "compatible": true + }, + { + "names": ["plus_one", "rot13"], + "compatible": true + }, + { + "names": ["plus_one", "no_quit"], + "compatible": true + }, + { + "names": ["plus_two", "58008"], + "compatible": true + }, + { + "names": ["plus_two", "mirror"], + "compatible": true + }, + { + "names": ["plus_two", "upside_down"], + "compatible": true + }, + { + "names": ["plus_two", "nausea"], + "compatible": true + }, + { + "names": ["plus_two", "round_round_baby"], + "compatible": true + }, + { + "names": ["plus_two", "simon_says"], + "compatible": false + }, + { + "names": ["plus_two", "tts"], + "compatible": false + }, + { + "names": ["plus_two", "choo_choo"], + "compatible": true + }, + { + "names": ["plus_two", "arrows"], + "compatible": false + }, + { + "names": ["plus_two", "rAnDoMcAsE"], + "compatible": true + }, + { + "names": ["plus_two", "sPoNgEcAsE"], + "compatible": true + }, + { + "names": ["plus_two", "capitals"], + "compatible": true + }, + { + "names": ["plus_two", "layout_mirror"], + "compatible": true + }, + { + "names": ["plus_two", "layoutfluid"], + "compatible": true + }, + { + "names": ["plus_two", "earthquake"], + "compatible": true + }, + { + "names": ["plus_two", "space_balls"], + "compatible": true + }, + { + "names": ["plus_two", "gibberish"], + "compatible": true + }, + { + "names": ["plus_two", "ascii"], + "compatible": true + }, + { + "names": ["plus_two", "specials"], + "compatible": true + }, + { + "names": ["plus_two", "plus_zero"], + "compatible": false + }, + { + "names": ["plus_two", "plus_one"], + "compatible": false + }, + { + "names": ["plus_two", "plus_two"], + "compatible": false + }, + { + "names": ["plus_two", "plus_three"], + "compatible": false + }, + { + "names": ["plus_two", "read_ahead_easy"], + "compatible": false + }, + { + "names": ["plus_two", "read_ahead"], + "compatible": false + }, + { + "names": ["plus_two", "read_ahead_hard"], + "compatible": false + }, + { + "names": ["plus_two", "memory"], + "compatible": false + }, + { + "names": ["plus_two", "nospace"], + "compatible": false + }, + { + "names": ["plus_two", "poetry"], + "compatible": false + }, + { + "names": ["plus_two", "wikipedia"], + "compatible": false + }, + { + "names": ["plus_two", "weakspot"], + "compatible": true + }, + { + "names": ["plus_two", "pseudolang"], + "compatible": true + }, + { + "names": ["plus_two", "IPv4"], + "compatible": true + }, + { + "names": ["plus_two", "IPv6"], + "compatible": true + }, + { + "names": ["plus_two", "binary"], + "compatible": true + }, + { + "names": ["plus_two", "hexadecimal"], + "compatible": true + }, + { + "names": ["plus_two", "zipf"], + "compatible": true + }, + { + "names": ["plus_two", "morse"], + "compatible": false + }, + { + "names": ["plus_two", "crt"], + "compatible": true + }, + { + "names": ["plus_two", "backwards"], + "compatible": true + }, + { + "names": ["plus_two", "ddoouubblleedd"], + "compatible": true + }, + { + "names": ["plus_two", "instant_messaging"], + "compatible": true + }, + { + "names": ["plus_two", "underscore_spaces"], + "compatible": false + }, + { + "names": ["plus_two", "ALL_CAPS"], + "compatible": true + }, + { + "names": ["plus_two", "polyglot"], + "compatible": true + }, + { + "names": ["plus_two", "asl"], + "compatible": true + }, + { + "names": ["plus_two", "rot13"], + "compatible": true + }, + { + "names": ["plus_two", "no_quit"], + "compatible": true + }, + { + "names": ["plus_three", "58008"], + "compatible": true + }, + { + "names": ["plus_three", "mirror"], + "compatible": true + }, + { + "names": ["plus_three", "upside_down"], + "compatible": true + }, + { + "names": ["plus_three", "nausea"], + "compatible": true + }, + { + "names": ["plus_three", "round_round_baby"], + "compatible": true + }, + { + "names": ["plus_three", "simon_says"], + "compatible": false + }, + { + "names": ["plus_three", "tts"], + "compatible": false + }, + { + "names": ["plus_three", "choo_choo"], + "compatible": true + }, + { + "names": ["plus_three", "arrows"], + "compatible": false + }, + { + "names": ["plus_three", "rAnDoMcAsE"], + "compatible": true + }, + { + "names": ["plus_three", "sPoNgEcAsE"], + "compatible": true + }, + { + "names": ["plus_three", "capitals"], + "compatible": true + }, + { + "names": ["plus_three", "layout_mirror"], + "compatible": true + }, + { + "names": ["plus_three", "layoutfluid"], + "compatible": true + }, + { + "names": ["plus_three", "earthquake"], + "compatible": true + }, + { + "names": ["plus_three", "space_balls"], + "compatible": true + }, + { + "names": ["plus_three", "gibberish"], + "compatible": true + }, + { + "names": ["plus_three", "ascii"], + "compatible": true + }, + { + "names": ["plus_three", "specials"], + "compatible": true + }, + { + "names": ["plus_three", "plus_zero"], + "compatible": false + }, + { + "names": ["plus_three", "plus_one"], + "compatible": false + }, + { + "names": ["plus_three", "plus_two"], + "compatible": false + }, + { + "names": ["plus_three", "plus_three"], + "compatible": false + }, + { + "names": ["plus_three", "read_ahead_easy"], + "compatible": false + }, + { + "names": ["plus_three", "read_ahead"], + "compatible": false + }, + { + "names": ["plus_three", "read_ahead_hard"], + "compatible": false + }, + { + "names": ["plus_three", "memory"], + "compatible": false + }, + { + "names": ["plus_three", "nospace"], + "compatible": false + }, + { + "names": ["plus_three", "poetry"], + "compatible": false + }, + { + "names": ["plus_three", "wikipedia"], + "compatible": false + }, + { + "names": ["plus_three", "weakspot"], + "compatible": true + }, + { + "names": ["plus_three", "pseudolang"], + "compatible": true + }, + { + "names": ["plus_three", "IPv4"], + "compatible": true + }, + { + "names": ["plus_three", "IPv6"], + "compatible": true + }, + { + "names": ["plus_three", "binary"], + "compatible": true + }, + { + "names": ["plus_three", "hexadecimal"], + "compatible": true + }, + { + "names": ["plus_three", "zipf"], + "compatible": true + }, + { + "names": ["plus_three", "morse"], + "compatible": false + }, + { + "names": ["plus_three", "crt"], + "compatible": true + }, + { + "names": ["plus_three", "backwards"], + "compatible": true + }, + { + "names": ["plus_three", "ddoouubblleedd"], + "compatible": true + }, + { + "names": ["plus_three", "instant_messaging"], + "compatible": true + }, + { + "names": ["plus_three", "underscore_spaces"], + "compatible": false + }, + { + "names": ["plus_three", "ALL_CAPS"], + "compatible": true + }, + { + "names": ["plus_three", "polyglot"], + "compatible": true + }, + { + "names": ["plus_three", "asl"], + "compatible": true + }, + { + "names": ["plus_three", "rot13"], + "compatible": true + }, + { + "names": ["plus_three", "no_quit"], + "compatible": true + }, + { + "names": ["read_ahead_easy", "58008"], + "compatible": true + }, + { + "names": ["read_ahead_easy", "mirror"], + "compatible": true + }, + { + "names": ["read_ahead_easy", "upside_down"], + "compatible": true + }, + { + "names": ["read_ahead_easy", "nausea"], + "compatible": true + }, + { + "names": ["read_ahead_easy", "round_round_baby"], + "compatible": true + }, + { + "names": ["read_ahead_easy", "simon_says"], + "compatible": false + }, + { + "names": ["read_ahead_easy", "tts"], + "compatible": false + }, + { + "names": ["read_ahead_easy", "choo_choo"], + "compatible": true + }, + { + "names": ["read_ahead_easy", "arrows"], + "compatible": true + }, + { + "names": ["read_ahead_easy", "rAnDoMcAsE"], + "compatible": true + }, + { + "names": ["read_ahead_easy", "sPoNgEcAsE"], + "compatible": true + }, + { + "names": ["read_ahead_easy", "capitals"], + "compatible": true + }, + { + "names": ["read_ahead_easy", "layout_mirror"], + "compatible": true + }, + { + "names": ["read_ahead_easy", "layoutfluid"], + "compatible": true + }, + { + "names": ["read_ahead_easy", "earthquake"], + "compatible": true + }, + { + "names": ["read_ahead_easy", "space_balls"], + "compatible": true + }, + { + "names": ["read_ahead_easy", "gibberish"], + "compatible": true + }, + { + "names": ["read_ahead_easy", "ascii"], + "compatible": true + }, + { + "names": ["read_ahead_easy", "specials"], + "compatible": true + }, + { + "names": ["read_ahead_easy", "plus_zero"], + "compatible": false + }, + { + "names": ["read_ahead_easy", "plus_one"], + "compatible": false + }, + { + "names": ["read_ahead_easy", "plus_two"], + "compatible": false + }, + { + "names": ["read_ahead_easy", "plus_three"], + "compatible": false + }, + { + "names": ["read_ahead_easy", "read_ahead_easy"], + "compatible": false + }, + { + "names": ["read_ahead_easy", "read_ahead"], + "compatible": false + }, + { + "names": ["read_ahead_easy", "read_ahead_hard"], + "compatible": false + }, + { + "names": ["read_ahead_easy", "memory"], + "compatible": false + }, + { + "names": ["read_ahead_easy", "nospace"], + "compatible": true + }, + { + "names": ["read_ahead_easy", "poetry"], + "compatible": true + }, + { + "names": ["read_ahead_easy", "wikipedia"], + "compatible": true + }, + { + "names": ["read_ahead_easy", "weakspot"], + "compatible": true + }, + { + "names": ["read_ahead_easy", "pseudolang"], + "compatible": true + }, + { + "names": ["read_ahead_easy", "IPv4"], + "compatible": true + }, + { + "names": ["read_ahead_easy", "IPv6"], + "compatible": true + }, + { + "names": ["read_ahead_easy", "binary"], + "compatible": true + }, + { + "names": ["read_ahead_easy", "hexadecimal"], + "compatible": true + }, + { + "names": ["read_ahead_easy", "zipf"], + "compatible": true + }, + { + "names": ["read_ahead_easy", "morse"], + "compatible": true + }, + { + "names": ["read_ahead_easy", "crt"], + "compatible": true + }, + { + "names": ["read_ahead_easy", "backwards"], + "compatible": true + }, + { + "names": ["read_ahead_easy", "ddoouubblleedd"], + "compatible": true + }, + { + "names": ["read_ahead_easy", "instant_messaging"], + "compatible": true + }, + { + "names": ["read_ahead_easy", "underscore_spaces"], + "compatible": true + }, + { + "names": ["read_ahead_easy", "ALL_CAPS"], + "compatible": true + }, + { + "names": ["read_ahead_easy", "polyglot"], + "compatible": true + }, + { + "names": ["read_ahead_easy", "asl"], + "compatible": true + }, + { + "names": ["read_ahead_easy", "rot13"], + "compatible": true + }, + { + "names": ["read_ahead_easy", "no_quit"], + "compatible": true + }, + { + "names": ["read_ahead", "58008"], + "compatible": true + }, + { + "names": ["read_ahead", "mirror"], + "compatible": true + }, + { + "names": ["read_ahead", "upside_down"], + "compatible": true + }, + { + "names": ["read_ahead", "nausea"], + "compatible": true + }, + { + "names": ["read_ahead", "round_round_baby"], + "compatible": true + }, + { + "names": ["read_ahead", "simon_says"], + "compatible": false + }, + { + "names": ["read_ahead", "tts"], + "compatible": false + }, + { + "names": ["read_ahead", "choo_choo"], + "compatible": true + }, + { + "names": ["read_ahead", "arrows"], + "compatible": true + }, + { + "names": ["read_ahead", "rAnDoMcAsE"], + "compatible": true + }, + { + "names": ["read_ahead", "sPoNgEcAsE"], + "compatible": true + }, + { + "names": ["read_ahead", "capitals"], + "compatible": true + }, + { + "names": ["read_ahead", "layout_mirror"], + "compatible": true + }, + { + "names": ["read_ahead", "layoutfluid"], + "compatible": true + }, + { + "names": ["read_ahead", "earthquake"], + "compatible": true + }, + { + "names": ["read_ahead", "space_balls"], + "compatible": true + }, + { + "names": ["read_ahead", "gibberish"], + "compatible": true + }, + { + "names": ["read_ahead", "ascii"], + "compatible": true + }, + { + "names": ["read_ahead", "specials"], + "compatible": true + }, + { + "names": ["read_ahead", "plus_zero"], + "compatible": false + }, + { + "names": ["read_ahead", "plus_one"], + "compatible": false + }, + { + "names": ["read_ahead", "plus_two"], + "compatible": false + }, + { + "names": ["read_ahead", "plus_three"], + "compatible": false + }, + { + "names": ["read_ahead", "read_ahead_easy"], + "compatible": false + }, + { + "names": ["read_ahead", "read_ahead"], + "compatible": false + }, + { + "names": ["read_ahead", "read_ahead_hard"], + "compatible": false + }, + { + "names": ["read_ahead", "memory"], + "compatible": false + }, + { + "names": ["read_ahead", "nospace"], + "compatible": true + }, + { + "names": ["read_ahead", "poetry"], + "compatible": true + }, + { + "names": ["read_ahead", "wikipedia"], + "compatible": true + }, + { + "names": ["read_ahead", "weakspot"], + "compatible": true + }, + { + "names": ["read_ahead", "pseudolang"], + "compatible": true + }, + { + "names": ["read_ahead", "IPv4"], + "compatible": true + }, + { + "names": ["read_ahead", "IPv6"], + "compatible": true + }, + { + "names": ["read_ahead", "binary"], + "compatible": true + }, + { + "names": ["read_ahead", "hexadecimal"], + "compatible": true + }, + { + "names": ["read_ahead", "zipf"], + "compatible": true + }, + { + "names": ["read_ahead", "morse"], + "compatible": true + }, + { + "names": ["read_ahead", "crt"], + "compatible": true + }, + { + "names": ["read_ahead", "backwards"], + "compatible": true + }, + { + "names": ["read_ahead", "ddoouubblleedd"], + "compatible": true + }, + { + "names": ["read_ahead", "instant_messaging"], + "compatible": true + }, + { + "names": ["read_ahead", "underscore_spaces"], + "compatible": true + }, + { + "names": ["read_ahead", "ALL_CAPS"], + "compatible": true + }, + { + "names": ["read_ahead", "polyglot"], + "compatible": true + }, + { + "names": ["read_ahead", "asl"], + "compatible": true + }, + { + "names": ["read_ahead", "rot13"], + "compatible": true + }, + { + "names": ["read_ahead", "no_quit"], + "compatible": true + }, + { + "names": ["read_ahead_hard", "58008"], + "compatible": true + }, + { + "names": ["read_ahead_hard", "mirror"], + "compatible": true + }, + { + "names": ["read_ahead_hard", "upside_down"], + "compatible": true + }, + { + "names": ["read_ahead_hard", "nausea"], + "compatible": true + }, + { + "names": ["read_ahead_hard", "round_round_baby"], + "compatible": true + }, + { + "names": ["read_ahead_hard", "simon_says"], + "compatible": false + }, + { + "names": ["read_ahead_hard", "tts"], + "compatible": false + }, + { + "names": ["read_ahead_hard", "choo_choo"], + "compatible": true + }, + { + "names": ["read_ahead_hard", "arrows"], + "compatible": true + }, + { + "names": ["read_ahead_hard", "rAnDoMcAsE"], + "compatible": true + }, + { + "names": ["read_ahead_hard", "sPoNgEcAsE"], + "compatible": true + }, + { + "names": ["read_ahead_hard", "capitals"], + "compatible": true + }, + { + "names": ["read_ahead_hard", "layout_mirror"], + "compatible": true + }, + { + "names": ["read_ahead_hard", "layoutfluid"], + "compatible": true + }, + { + "names": ["read_ahead_hard", "earthquake"], + "compatible": true + }, + { + "names": ["read_ahead_hard", "space_balls"], + "compatible": true + }, + { + "names": ["read_ahead_hard", "gibberish"], + "compatible": true + }, + { + "names": ["read_ahead_hard", "ascii"], + "compatible": true + }, + { + "names": ["read_ahead_hard", "specials"], + "compatible": true + }, + { + "names": ["read_ahead_hard", "plus_zero"], + "compatible": false + }, + { + "names": ["read_ahead_hard", "plus_one"], + "compatible": false + }, + { + "names": ["read_ahead_hard", "plus_two"], + "compatible": false + }, + { + "names": ["read_ahead_hard", "plus_three"], + "compatible": false + }, + { + "names": ["read_ahead_hard", "read_ahead_easy"], + "compatible": false + }, + { + "names": ["read_ahead_hard", "read_ahead"], + "compatible": false + }, + { + "names": ["read_ahead_hard", "read_ahead_hard"], + "compatible": false + }, + { + "names": ["read_ahead_hard", "memory"], + "compatible": false + }, + { + "names": ["read_ahead_hard", "nospace"], + "compatible": true + }, + { + "names": ["read_ahead_hard", "poetry"], + "compatible": true + }, + { + "names": ["read_ahead_hard", "wikipedia"], + "compatible": true + }, + { + "names": ["read_ahead_hard", "weakspot"], + "compatible": true + }, + { + "names": ["read_ahead_hard", "pseudolang"], + "compatible": true + }, + { + "names": ["read_ahead_hard", "IPv4"], + "compatible": true + }, + { + "names": ["read_ahead_hard", "IPv6"], + "compatible": true + }, + { + "names": ["read_ahead_hard", "binary"], + "compatible": true + }, + { + "names": ["read_ahead_hard", "hexadecimal"], + "compatible": true + }, + { + "names": ["read_ahead_hard", "zipf"], + "compatible": true + }, + { + "names": ["read_ahead_hard", "morse"], + "compatible": true + }, + { + "names": ["read_ahead_hard", "crt"], + "compatible": true + }, + { + "names": ["read_ahead_hard", "backwards"], + "compatible": true + }, + { + "names": ["read_ahead_hard", "ddoouubblleedd"], + "compatible": true + }, + { + "names": ["read_ahead_hard", "instant_messaging"], + "compatible": true + }, + { + "names": ["read_ahead_hard", "underscore_spaces"], + "compatible": true + }, + { + "names": ["read_ahead_hard", "ALL_CAPS"], + "compatible": true + }, + { + "names": ["read_ahead_hard", "polyglot"], + "compatible": true + }, + { + "names": ["read_ahead_hard", "asl"], + "compatible": true + }, + { + "names": ["read_ahead_hard", "rot13"], + "compatible": true + }, + { + "names": ["read_ahead_hard", "no_quit"], + "compatible": true + }, + { + "names": ["memory", "58008"], + "compatible": true + }, + { + "names": ["memory", "mirror"], + "compatible": true + }, + { + "names": ["memory", "upside_down"], + "compatible": true + }, + { + "names": ["memory", "nausea"], + "compatible": true + }, + { + "names": ["memory", "round_round_baby"], + "compatible": true + }, + { + "names": ["memory", "simon_says"], + "compatible": false + }, + { + "names": ["memory", "tts"], + "compatible": false + }, + { + "names": ["memory", "choo_choo"], + "compatible": true + }, + { + "names": ["memory", "arrows"], + "compatible": true + }, + { + "names": ["memory", "rAnDoMcAsE"], + "compatible": true + }, + { + "names": ["memory", "sPoNgEcAsE"], + "compatible": true + }, + { + "names": ["memory", "capitals"], + "compatible": true + }, + { + "names": ["memory", "layout_mirror"], + "compatible": true + }, + { + "names": ["memory", "layoutfluid"], + "compatible": true + }, + { + "names": ["memory", "earthquake"], + "compatible": true + }, + { + "names": ["memory", "space_balls"], + "compatible": true + }, + { + "names": ["memory", "gibberish"], + "compatible": true + }, + { + "names": ["memory", "ascii"], + "compatible": true + }, + { + "names": ["memory", "specials"], + "compatible": true + }, + { + "names": ["memory", "plus_zero"], + "compatible": false + }, + { + "names": ["memory", "plus_one"], + "compatible": false + }, + { + "names": ["memory", "plus_two"], + "compatible": false + }, + { + "names": ["memory", "plus_three"], + "compatible": false + }, + { + "names": ["memory", "read_ahead_easy"], + "compatible": false + }, + { + "names": ["memory", "read_ahead"], + "compatible": false + }, + { + "names": ["memory", "read_ahead_hard"], + "compatible": false + }, + { + "names": ["memory", "memory"], + "compatible": false + }, + { + "names": ["memory", "nospace"], + "compatible": true + }, + { + "names": ["memory", "poetry"], + "compatible": true + }, + { + "names": ["memory", "wikipedia"], + "compatible": true + }, + { + "names": ["memory", "weakspot"], + "compatible": true + }, + { + "names": ["memory", "pseudolang"], + "compatible": true + }, + { + "names": ["memory", "IPv4"], + "compatible": true + }, + { + "names": ["memory", "IPv6"], + "compatible": true + }, + { + "names": ["memory", "binary"], + "compatible": true + }, + { + "names": ["memory", "hexadecimal"], + "compatible": true + }, + { + "names": ["memory", "zipf"], + "compatible": true + }, + { + "names": ["memory", "morse"], + "compatible": true + }, + { + "names": ["memory", "crt"], + "compatible": true + }, + { + "names": ["memory", "backwards"], + "compatible": true + }, + { + "names": ["memory", "ddoouubblleedd"], + "compatible": true + }, + { + "names": ["memory", "instant_messaging"], + "compatible": true + }, + { + "names": ["memory", "underscore_spaces"], + "compatible": true + }, + { + "names": ["memory", "ALL_CAPS"], + "compatible": true + }, + { + "names": ["memory", "polyglot"], + "compatible": true + }, + { + "names": ["memory", "asl"], + "compatible": true + }, + { + "names": ["memory", "rot13"], + "compatible": true + }, + { + "names": ["memory", "no_quit"], + "compatible": true + }, + { + "names": ["nospace", "58008"], + "compatible": true + }, + { + "names": ["nospace", "mirror"], + "compatible": true + }, + { + "names": ["nospace", "upside_down"], + "compatible": true + }, + { + "names": ["nospace", "nausea"], + "compatible": true + }, + { + "names": ["nospace", "round_round_baby"], + "compatible": true + }, + { + "names": ["nospace", "simon_says"], + "compatible": true + }, + { + "names": ["nospace", "tts"], + "compatible": true + }, + { + "names": ["nospace", "choo_choo"], + "compatible": true + }, + { + "names": ["nospace", "arrows"], + "compatible": false + }, + { + "names": ["nospace", "rAnDoMcAsE"], + "compatible": true + }, + { + "names": ["nospace", "sPoNgEcAsE"], + "compatible": true + }, + { + "names": ["nospace", "capitals"], + "compatible": true + }, + { + "names": ["nospace", "layout_mirror"], + "compatible": true + }, + { + "names": ["nospace", "layoutfluid"], + "compatible": true + }, + { + "names": ["nospace", "earthquake"], + "compatible": true + }, + { + "names": ["nospace", "space_balls"], + "compatible": true + }, + { + "names": ["nospace", "gibberish"], + "compatible": true + }, + { + "names": ["nospace", "ascii"], + "compatible": true + }, + { + "names": ["nospace", "specials"], + "compatible": true + }, + { + "names": ["nospace", "plus_zero"], + "compatible": false + }, + { + "names": ["nospace", "plus_one"], + "compatible": false + }, + { + "names": ["nospace", "plus_two"], + "compatible": false + }, + { + "names": ["nospace", "plus_three"], + "compatible": false + }, + { + "names": ["nospace", "read_ahead_easy"], + "compatible": true + }, + { + "names": ["nospace", "read_ahead"], + "compatible": true + }, + { + "names": ["nospace", "read_ahead_hard"], + "compatible": true + }, + { + "names": ["nospace", "memory"], + "compatible": true + }, + { + "names": ["nospace", "nospace"], + "compatible": false + }, + { + "names": ["nospace", "poetry"], + "compatible": true + }, + { + "names": ["nospace", "wikipedia"], + "compatible": true + }, + { + "names": ["nospace", "weakspot"], + "compatible": true + }, + { + "names": ["nospace", "pseudolang"], + "compatible": true + }, + { + "names": ["nospace", "IPv4"], + "compatible": true + }, + { + "names": ["nospace", "IPv6"], + "compatible": true + }, + { + "names": ["nospace", "binary"], + "compatible": true + }, + { + "names": ["nospace", "hexadecimal"], + "compatible": true + }, + { + "names": ["nospace", "zipf"], + "compatible": true + }, + { + "names": ["nospace", "morse"], + "compatible": false + }, + { + "names": ["nospace", "crt"], + "compatible": true + }, + { + "names": ["nospace", "backwards"], + "compatible": true + }, + { + "names": ["nospace", "ddoouubblleedd"], + "compatible": true + }, + { + "names": ["nospace", "instant_messaging"], + "compatible": true + }, + { + "names": ["nospace", "underscore_spaces"], + "compatible": false + }, + { + "names": ["nospace", "ALL_CAPS"], + "compatible": true + }, + { + "names": ["nospace", "polyglot"], + "compatible": true + }, + { + "names": ["nospace", "asl"], + "compatible": true + }, + { + "names": ["nospace", "rot13"], + "compatible": true + }, + { + "names": ["nospace", "no_quit"], + "compatible": true + }, + { + "names": ["poetry", "58008"], + "compatible": false + }, + { + "names": ["poetry", "mirror"], + "compatible": true + }, + { + "names": ["poetry", "upside_down"], + "compatible": true + }, + { + "names": ["poetry", "nausea"], + "compatible": true + }, + { + "names": ["poetry", "round_round_baby"], + "compatible": true + }, + { + "names": ["poetry", "simon_says"], + "compatible": true + }, + { + "names": ["poetry", "tts"], + "compatible": true + }, + { + "names": ["poetry", "choo_choo"], + "compatible": true + }, + { + "names": ["poetry", "arrows"], + "compatible": false + }, + { + "names": ["poetry", "rAnDoMcAsE"], + "compatible": true + }, + { + "names": ["poetry", "sPoNgEcAsE"], + "compatible": true + }, + { + "names": ["poetry", "capitals"], + "compatible": true + }, + { + "names": ["poetry", "layout_mirror"], + "compatible": true + }, + { + "names": ["poetry", "layoutfluid"], + "compatible": true + }, + { + "names": ["poetry", "earthquake"], + "compatible": true + }, + { + "names": ["poetry", "space_balls"], + "compatible": true + }, + { + "names": ["poetry", "gibberish"], + "compatible": false + }, + { + "names": ["poetry", "ascii"], + "compatible": false + }, + { + "names": ["poetry", "specials"], + "compatible": false + }, + { + "names": ["poetry", "plus_zero"], + "compatible": false + }, + { + "names": ["poetry", "plus_one"], + "compatible": false + }, + { + "names": ["poetry", "plus_two"], + "compatible": false + }, + { + "names": ["poetry", "plus_three"], + "compatible": false + }, + { + "names": ["poetry", "read_ahead_easy"], + "compatible": true + }, + { + "names": ["poetry", "read_ahead"], + "compatible": true + }, + { + "names": ["poetry", "read_ahead_hard"], + "compatible": true + }, + { + "names": ["poetry", "memory"], + "compatible": true + }, + { + "names": ["poetry", "nospace"], + "compatible": true + }, + { + "names": ["poetry", "poetry"], + "compatible": false + }, + { + "names": ["poetry", "wikipedia"], + "compatible": false + }, + { + "names": ["poetry", "weakspot"], + "compatible": false + }, + { + "names": ["poetry", "pseudolang"], + "compatible": false + }, + { + "names": ["poetry", "IPv4"], + "compatible": false + }, + { + "names": ["poetry", "IPv6"], + "compatible": false + }, + { + "names": ["poetry", "binary"], + "compatible": false + }, + { + "names": ["poetry", "hexadecimal"], + "compatible": false + }, + { + "names": ["poetry", "zipf"], + "compatible": false + }, + { + "names": ["poetry", "morse"], + "compatible": true + }, + { + "names": ["poetry", "crt"], + "compatible": true + }, + { + "names": ["poetry", "backwards"], + "compatible": true + }, + { + "names": ["poetry", "ddoouubblleedd"], + "compatible": true + }, + { + "names": ["poetry", "instant_messaging"], + "compatible": true + }, + { + "names": ["poetry", "underscore_spaces"], + "compatible": true + }, + { + "names": ["poetry", "ALL_CAPS"], + "compatible": true + }, + { + "names": ["poetry", "polyglot"], + "compatible": false + }, + { + "names": ["poetry", "asl"], + "compatible": true + }, + { + "names": ["poetry", "rot13"], + "compatible": true + }, + { + "names": ["poetry", "no_quit"], + "compatible": true + }, + { + "names": ["wikipedia", "58008"], + "compatible": false + }, + { + "names": ["wikipedia", "mirror"], + "compatible": true + }, + { + "names": ["wikipedia", "upside_down"], + "compatible": true + }, + { + "names": ["wikipedia", "nausea"], + "compatible": true + }, + { + "names": ["wikipedia", "round_round_baby"], + "compatible": true + }, + { + "names": ["wikipedia", "simon_says"], + "compatible": true + }, + { + "names": ["wikipedia", "tts"], + "compatible": true + }, + { + "names": ["wikipedia", "choo_choo"], + "compatible": true + }, + { + "names": ["wikipedia", "arrows"], + "compatible": false + }, + { + "names": ["wikipedia", "rAnDoMcAsE"], + "compatible": true + }, + { + "names": ["wikipedia", "sPoNgEcAsE"], + "compatible": true + }, + { + "names": ["wikipedia", "capitals"], + "compatible": true + }, + { + "names": ["wikipedia", "layout_mirror"], + "compatible": true + }, + { + "names": ["wikipedia", "layoutfluid"], + "compatible": true + }, + { + "names": ["wikipedia", "earthquake"], + "compatible": true + }, + { + "names": ["wikipedia", "space_balls"], + "compatible": true + }, + { + "names": ["wikipedia", "gibberish"], + "compatible": false + }, + { + "names": ["wikipedia", "ascii"], + "compatible": false + }, + { + "names": ["wikipedia", "specials"], + "compatible": false + }, + { + "names": ["wikipedia", "plus_zero"], + "compatible": false + }, + { + "names": ["wikipedia", "plus_one"], + "compatible": false + }, + { + "names": ["wikipedia", "plus_two"], + "compatible": false + }, + { + "names": ["wikipedia", "plus_three"], + "compatible": false + }, + { + "names": ["wikipedia", "read_ahead_easy"], + "compatible": true + }, + { + "names": ["wikipedia", "read_ahead"], + "compatible": true + }, + { + "names": ["wikipedia", "read_ahead_hard"], + "compatible": true + }, + { + "names": ["wikipedia", "memory"], + "compatible": true + }, + { + "names": ["wikipedia", "nospace"], + "compatible": true + }, + { + "names": ["wikipedia", "poetry"], + "compatible": false + }, + { + "names": ["wikipedia", "wikipedia"], + "compatible": false + }, + { + "names": ["wikipedia", "weakspot"], + "compatible": false + }, + { + "names": ["wikipedia", "pseudolang"], + "compatible": false + }, + { + "names": ["wikipedia", "IPv4"], + "compatible": false + }, + { + "names": ["wikipedia", "IPv6"], + "compatible": false + }, + { + "names": ["wikipedia", "binary"], + "compatible": false + }, + { + "names": ["wikipedia", "hexadecimal"], + "compatible": false + }, + { + "names": ["wikipedia", "zipf"], + "compatible": false + }, + { + "names": ["wikipedia", "morse"], + "compatible": true + }, + { + "names": ["wikipedia", "crt"], + "compatible": true + }, + { + "names": ["wikipedia", "backwards"], + "compatible": true + }, + { + "names": ["wikipedia", "ddoouubblleedd"], + "compatible": true + }, + { + "names": ["wikipedia", "instant_messaging"], + "compatible": true + }, + { + "names": ["wikipedia", "underscore_spaces"], + "compatible": true + }, + { + "names": ["wikipedia", "ALL_CAPS"], + "compatible": true + }, + { + "names": ["wikipedia", "polyglot"], + "compatible": false + }, + { + "names": ["wikipedia", "asl"], + "compatible": true + }, + { + "names": ["wikipedia", "rot13"], + "compatible": true + }, + { + "names": ["wikipedia", "no_quit"], + "compatible": true + }, + { + "names": ["weakspot", "58008"], + "compatible": false + }, + { + "names": ["weakspot", "mirror"], + "compatible": true + }, + { + "names": ["weakspot", "upside_down"], + "compatible": true + }, + { + "names": ["weakspot", "nausea"], + "compatible": true + }, + { + "names": ["weakspot", "round_round_baby"], + "compatible": true + }, + { + "names": ["weakspot", "simon_says"], + "compatible": true + }, + { + "names": ["weakspot", "tts"], + "compatible": true + }, + { + "names": ["weakspot", "choo_choo"], + "compatible": true + }, + { + "names": ["weakspot", "arrows"], + "compatible": false + }, + { + "names": ["weakspot", "rAnDoMcAsE"], + "compatible": true + }, + { + "names": ["weakspot", "sPoNgEcAsE"], + "compatible": true + }, + { + "names": ["weakspot", "capitals"], + "compatible": true + }, + { + "names": ["weakspot", "layout_mirror"], + "compatible": true + }, + { + "names": ["weakspot", "layoutfluid"], + "compatible": true + }, + { + "names": ["weakspot", "earthquake"], + "compatible": true + }, + { + "names": ["weakspot", "space_balls"], + "compatible": true + }, + { + "names": ["weakspot", "gibberish"], + "compatible": false + }, + { + "names": ["weakspot", "ascii"], + "compatible": false + }, + { + "names": ["weakspot", "specials"], + "compatible": false + }, + { + "names": ["weakspot", "plus_zero"], + "compatible": true + }, + { + "names": ["weakspot", "plus_one"], + "compatible": true + }, + { + "names": ["weakspot", "plus_two"], + "compatible": true + }, + { + "names": ["weakspot", "plus_three"], + "compatible": true + }, + { + "names": ["weakspot", "read_ahead_easy"], + "compatible": true + }, + { + "names": ["weakspot", "read_ahead"], + "compatible": true + }, + { + "names": ["weakspot", "read_ahead_hard"], + "compatible": true + }, + { + "names": ["weakspot", "memory"], + "compatible": true + }, + { + "names": ["weakspot", "nospace"], + "compatible": true + }, + { + "names": ["weakspot", "poetry"], + "compatible": false + }, + { + "names": ["weakspot", "wikipedia"], + "compatible": false + }, + { + "names": ["weakspot", "weakspot"], + "compatible": false + }, + { + "names": ["weakspot", "pseudolang"], + "compatible": false + }, + { + "names": ["weakspot", "IPv4"], + "compatible": false + }, + { + "names": ["weakspot", "IPv6"], + "compatible": false + }, + { + "names": ["weakspot", "binary"], + "compatible": false + }, + { + "names": ["weakspot", "hexadecimal"], + "compatible": false + }, + { + "names": ["weakspot", "zipf"], + "compatible": false + }, + { + "names": ["weakspot", "morse"], + "compatible": false + }, + { + "names": ["weakspot", "crt"], + "compatible": true + }, + { + "names": ["weakspot", "backwards"], + "compatible": true + }, + { + "names": ["weakspot", "ddoouubblleedd"], + "compatible": true + }, + { + "names": ["weakspot", "instant_messaging"], + "compatible": true + }, + { + "names": ["weakspot", "underscore_spaces"], + "compatible": false + }, + { + "names": ["weakspot", "ALL_CAPS"], + "compatible": true + }, + { + "names": ["weakspot", "polyglot"], + "compatible": false + }, + { + "names": ["weakspot", "asl"], + "compatible": true + }, + { + "names": ["weakspot", "rot13"], + "compatible": true + }, + { + "names": ["weakspot", "no_quit"], + "compatible": true + }, + { + "names": ["pseudolang", "58008"], + "compatible": false + }, + { + "names": ["pseudolang", "mirror"], + "compatible": true + }, + { + "names": ["pseudolang", "upside_down"], + "compatible": true + }, + { + "names": ["pseudolang", "nausea"], + "compatible": true + }, + { + "names": ["pseudolang", "round_round_baby"], + "compatible": true + }, + { + "names": ["pseudolang", "simon_says"], + "compatible": true + }, + { + "names": ["pseudolang", "tts"], + "compatible": false + }, + { + "names": ["pseudolang", "choo_choo"], + "compatible": true + }, + { + "names": ["pseudolang", "arrows"], + "compatible": false + }, + { + "names": ["pseudolang", "rAnDoMcAsE"], + "compatible": true + }, + { + "names": ["pseudolang", "sPoNgEcAsE"], + "compatible": true + }, + { + "names": ["pseudolang", "capitals"], + "compatible": true + }, + { + "names": ["pseudolang", "layout_mirror"], + "compatible": true + }, + { + "names": ["pseudolang", "layoutfluid"], + "compatible": true + }, + { + "names": ["pseudolang", "earthquake"], + "compatible": true + }, + { + "names": ["pseudolang", "space_balls"], + "compatible": true + }, + { + "names": ["pseudolang", "gibberish"], + "compatible": false + }, + { + "names": ["pseudolang", "ascii"], + "compatible": false + }, + { + "names": ["pseudolang", "specials"], + "compatible": false + }, + { + "names": ["pseudolang", "plus_zero"], + "compatible": true + }, + { + "names": ["pseudolang", "plus_one"], + "compatible": true + }, + { + "names": ["pseudolang", "plus_two"], + "compatible": true + }, + { + "names": ["pseudolang", "plus_three"], + "compatible": true + }, + { + "names": ["pseudolang", "read_ahead_easy"], + "compatible": true + }, + { + "names": ["pseudolang", "read_ahead"], + "compatible": true + }, + { + "names": ["pseudolang", "read_ahead_hard"], + "compatible": true + }, + { + "names": ["pseudolang", "memory"], + "compatible": true + }, + { + "names": ["pseudolang", "nospace"], + "compatible": true + }, + { + "names": ["pseudolang", "poetry"], + "compatible": false + }, + { + "names": ["pseudolang", "wikipedia"], + "compatible": false + }, + { + "names": ["pseudolang", "weakspot"], + "compatible": false + }, + { + "names": ["pseudolang", "pseudolang"], + "compatible": false + }, + { + "names": ["pseudolang", "IPv4"], + "compatible": false + }, + { + "names": ["pseudolang", "IPv6"], + "compatible": false + }, + { + "names": ["pseudolang", "binary"], + "compatible": false + }, + { + "names": ["pseudolang", "hexadecimal"], + "compatible": false + }, + { + "names": ["pseudolang", "zipf"], + "compatible": false + }, + { + "names": ["pseudolang", "morse"], + "compatible": true + }, + { + "names": ["pseudolang", "crt"], + "compatible": true + }, + { + "names": ["pseudolang", "backwards"], + "compatible": true + }, + { + "names": ["pseudolang", "ddoouubblleedd"], + "compatible": true + }, + { + "names": ["pseudolang", "instant_messaging"], + "compatible": true + }, + { + "names": ["pseudolang", "underscore_spaces"], + "compatible": true + }, + { + "names": ["pseudolang", "ALL_CAPS"], + "compatible": true + }, + { + "names": ["pseudolang", "polyglot"], + "compatible": false + }, + { + "names": ["pseudolang", "asl"], + "compatible": true + }, + { + "names": ["pseudolang", "rot13"], + "compatible": true + }, + { + "names": ["pseudolang", "no_quit"], + "compatible": true + }, + { + "names": ["IPv4", "58008"], + "compatible": false + }, + { + "names": ["IPv4", "mirror"], + "compatible": true + }, + { + "names": ["IPv4", "upside_down"], + "compatible": true + }, + { + "names": ["IPv4", "nausea"], + "compatible": true + }, + { + "names": ["IPv4", "round_round_baby"], + "compatible": true + }, + { + "names": ["IPv4", "simon_says"], + "compatible": true + }, + { + "names": ["IPv4", "tts"], + "compatible": true + }, + { + "names": ["IPv4", "choo_choo"], + "compatible": true + }, + { + "names": ["IPv4", "arrows"], + "compatible": false + }, + { + "names": ["IPv4", "rAnDoMcAsE"], + "compatible": false + }, + { + "names": ["IPv4", "sPoNgEcAsE"], + "compatible": false + }, + { + "names": ["IPv4", "capitals"], + "compatible": false + }, + { + "names": ["IPv4", "layout_mirror"], + "compatible": false + }, + { + "names": ["IPv4", "layoutfluid"], + "compatible": false + }, + { + "names": ["IPv4", "earthquake"], + "compatible": true + }, + { + "names": ["IPv4", "space_balls"], + "compatible": true + }, + { + "names": ["IPv4", "gibberish"], + "compatible": false + }, + { + "names": ["IPv4", "ascii"], + "compatible": false + }, + { + "names": ["IPv4", "specials"], + "compatible": false + }, + { + "names": ["IPv4", "plus_zero"], + "compatible": true + }, + { + "names": ["IPv4", "plus_one"], + "compatible": true + }, + { + "names": ["IPv4", "plus_two"], + "compatible": true + }, + { + "names": ["IPv4", "plus_three"], + "compatible": true + }, + { + "names": ["IPv4", "read_ahead_easy"], + "compatible": true + }, + { + "names": ["IPv4", "read_ahead"], + "compatible": true + }, + { + "names": ["IPv4", "read_ahead_hard"], + "compatible": true + }, + { + "names": ["IPv4", "memory"], + "compatible": true + }, + { + "names": ["IPv4", "nospace"], + "compatible": true + }, + { + "names": ["IPv4", "poetry"], + "compatible": false + }, + { + "names": ["IPv4", "wikipedia"], + "compatible": false + }, + { + "names": ["IPv4", "weakspot"], + "compatible": false + }, + { + "names": ["IPv4", "pseudolang"], + "compatible": false + }, + { + "names": ["IPv4", "IPv4"], + "compatible": false + }, + { + "names": ["IPv4", "IPv6"], + "compatible": false + }, + { + "names": ["IPv4", "binary"], + "compatible": false + }, + { + "names": ["IPv4", "hexadecimal"], + "compatible": false + }, + { + "names": ["IPv4", "zipf"], + "compatible": false + }, + { + "names": ["IPv4", "morse"], + "compatible": true + }, + { + "names": ["IPv4", "crt"], + "compatible": true + }, + { + "names": ["IPv4", "backwards"], + "compatible": true + }, + { + "names": ["IPv4", "ddoouubblleedd"], + "compatible": true + }, + { + "names": ["IPv4", "instant_messaging"], + "compatible": false + }, + { + "names": ["IPv4", "underscore_spaces"], + "compatible": true + }, + { + "names": ["IPv4", "ALL_CAPS"], + "compatible": false + }, + { + "names": ["IPv4", "polyglot"], + "compatible": false + }, + { + "names": ["IPv4", "asl"], + "compatible": true + }, + { + "names": ["IPv4", "rot13"], + "compatible": true + }, + { + "names": ["IPv4", "no_quit"], + "compatible": true + }, + { + "names": ["IPv6", "58008"], + "compatible": false + }, + { + "names": ["IPv6", "mirror"], + "compatible": true + }, + { + "names": ["IPv6", "upside_down"], + "compatible": true + }, + { + "names": ["IPv6", "nausea"], + "compatible": true + }, + { + "names": ["IPv6", "round_round_baby"], + "compatible": true + }, + { + "names": ["IPv6", "simon_says"], + "compatible": true + }, + { + "names": ["IPv6", "tts"], + "compatible": true + }, + { + "names": ["IPv6", "choo_choo"], + "compatible": true + }, + { + "names": ["IPv6", "arrows"], + "compatible": false + }, + { + "names": ["IPv6", "rAnDoMcAsE"], + "compatible": false + }, + { + "names": ["IPv6", "sPoNgEcAsE"], + "compatible": false + }, + { + "names": ["IPv6", "capitals"], + "compatible": false + }, + { + "names": ["IPv6", "layout_mirror"], + "compatible": false + }, + { + "names": ["IPv6", "layoutfluid"], + "compatible": false + }, + { + "names": ["IPv6", "earthquake"], + "compatible": true + }, + { + "names": ["IPv6", "space_balls"], + "compatible": true + }, + { + "names": ["IPv6", "gibberish"], + "compatible": false + }, + { + "names": ["IPv6", "ascii"], + "compatible": false + }, + { + "names": ["IPv6", "specials"], + "compatible": false + }, + { + "names": ["IPv6", "plus_zero"], + "compatible": true + }, + { + "names": ["IPv6", "plus_one"], + "compatible": true + }, + { + "names": ["IPv6", "plus_two"], + "compatible": true + }, + { + "names": ["IPv6", "plus_three"], + "compatible": true + }, + { + "names": ["IPv6", "read_ahead_easy"], + "compatible": true + }, + { + "names": ["IPv6", "read_ahead"], + "compatible": true + }, + { + "names": ["IPv6", "read_ahead_hard"], + "compatible": true + }, + { + "names": ["IPv6", "memory"], + "compatible": true + }, + { + "names": ["IPv6", "nospace"], + "compatible": true + }, + { + "names": ["IPv6", "poetry"], + "compatible": false + }, + { + "names": ["IPv6", "wikipedia"], + "compatible": false + }, + { + "names": ["IPv6", "weakspot"], + "compatible": false + }, + { + "names": ["IPv6", "pseudolang"], + "compatible": false + }, + { + "names": ["IPv6", "IPv4"], + "compatible": false + }, + { + "names": ["IPv6", "IPv6"], + "compatible": false + }, + { + "names": ["IPv6", "binary"], + "compatible": false + }, + { + "names": ["IPv6", "hexadecimal"], + "compatible": false + }, + { + "names": ["IPv6", "zipf"], + "compatible": false + }, + { + "names": ["IPv6", "morse"], + "compatible": true + }, + { + "names": ["IPv6", "crt"], + "compatible": true + }, + { + "names": ["IPv6", "backwards"], + "compatible": true + }, + { + "names": ["IPv6", "ddoouubblleedd"], + "compatible": true + }, + { + "names": ["IPv6", "instant_messaging"], + "compatible": false + }, + { + "names": ["IPv6", "underscore_spaces"], + "compatible": true + }, + { + "names": ["IPv6", "ALL_CAPS"], + "compatible": false + }, + { + "names": ["IPv6", "polyglot"], + "compatible": false + }, + { + "names": ["IPv6", "asl"], + "compatible": true + }, + { + "names": ["IPv6", "rot13"], + "compatible": true + }, + { + "names": ["IPv6", "no_quit"], + "compatible": true + }, + { + "names": ["binary", "58008"], + "compatible": false + }, + { + "names": ["binary", "mirror"], + "compatible": true + }, + { + "names": ["binary", "upside_down"], + "compatible": true + }, + { + "names": ["binary", "nausea"], + "compatible": true + }, + { + "names": ["binary", "round_round_baby"], + "compatible": true + }, + { + "names": ["binary", "simon_says"], + "compatible": true + }, + { + "names": ["binary", "tts"], + "compatible": true + }, + { + "names": ["binary", "choo_choo"], + "compatible": true + }, + { + "names": ["binary", "arrows"], + "compatible": false + }, + { + "names": ["binary", "rAnDoMcAsE"], + "compatible": false + }, + { + "names": ["binary", "sPoNgEcAsE"], + "compatible": false + }, + { + "names": ["binary", "capitals"], + "compatible": false + }, + { + "names": ["binary", "layout_mirror"], + "compatible": false + }, + { + "names": ["binary", "layoutfluid"], + "compatible": false + }, + { + "names": ["binary", "earthquake"], + "compatible": true + }, + { + "names": ["binary", "space_balls"], + "compatible": true + }, + { + "names": ["binary", "gibberish"], + "compatible": false + }, + { + "names": ["binary", "ascii"], + "compatible": false + }, + { + "names": ["binary", "specials"], + "compatible": false + }, + { + "names": ["binary", "plus_zero"], + "compatible": true + }, + { + "names": ["binary", "plus_one"], + "compatible": true + }, + { + "names": ["binary", "plus_two"], + "compatible": true + }, + { + "names": ["binary", "plus_three"], + "compatible": true + }, + { + "names": ["binary", "read_ahead_easy"], + "compatible": true + }, + { + "names": ["binary", "read_ahead"], + "compatible": true + }, + { + "names": ["binary", "read_ahead_hard"], + "compatible": true + }, + { + "names": ["binary", "memory"], + "compatible": true + }, + { + "names": ["binary", "nospace"], + "compatible": true + }, + { + "names": ["binary", "poetry"], + "compatible": false + }, + { + "names": ["binary", "wikipedia"], + "compatible": false + }, + { + "names": ["binary", "weakspot"], + "compatible": false + }, + { + "names": ["binary", "pseudolang"], + "compatible": false + }, + { + "names": ["binary", "IPv4"], + "compatible": false + }, + { + "names": ["binary", "IPv6"], + "compatible": false + }, + { + "names": ["binary", "binary"], + "compatible": false + }, + { + "names": ["binary", "hexadecimal"], + "compatible": false + }, + { + "names": ["binary", "zipf"], + "compatible": false + }, + { + "names": ["binary", "morse"], + "compatible": true + }, + { + "names": ["binary", "crt"], + "compatible": true + }, + { + "names": ["binary", "backwards"], + "compatible": true + }, + { + "names": ["binary", "ddoouubblleedd"], + "compatible": true + }, + { + "names": ["binary", "instant_messaging"], + "compatible": false + }, + { + "names": ["binary", "underscore_spaces"], + "compatible": true + }, + { + "names": ["binary", "ALL_CAPS"], + "compatible": false + }, + { + "names": ["binary", "polyglot"], + "compatible": false + }, + { + "names": ["binary", "asl"], + "compatible": true + }, + { + "names": ["binary", "rot13"], + "compatible": true + }, + { + "names": ["binary", "no_quit"], + "compatible": true + }, + { + "names": ["hexadecimal", "58008"], + "compatible": false + }, + { + "names": ["hexadecimal", "mirror"], + "compatible": true + }, + { + "names": ["hexadecimal", "upside_down"], + "compatible": true + }, + { + "names": ["hexadecimal", "nausea"], + "compatible": true + }, + { + "names": ["hexadecimal", "round_round_baby"], + "compatible": true + }, + { + "names": ["hexadecimal", "simon_says"], + "compatible": true + }, + { + "names": ["hexadecimal", "tts"], + "compatible": true + }, + { + "names": ["hexadecimal", "choo_choo"], + "compatible": true + }, + { + "names": ["hexadecimal", "arrows"], + "compatible": false + }, + { + "names": ["hexadecimal", "rAnDoMcAsE"], + "compatible": false + }, + { + "names": ["hexadecimal", "sPoNgEcAsE"], + "compatible": false + }, + { + "names": ["hexadecimal", "capitals"], + "compatible": false + }, + { + "names": ["hexadecimal", "layout_mirror"], + "compatible": false + }, + { + "names": ["hexadecimal", "layoutfluid"], + "compatible": false + }, + { + "names": ["hexadecimal", "earthquake"], + "compatible": true + }, + { + "names": ["hexadecimal", "space_balls"], + "compatible": true + }, + { + "names": ["hexadecimal", "gibberish"], + "compatible": false + }, + { + "names": ["hexadecimal", "ascii"], + "compatible": false + }, + { + "names": ["hexadecimal", "specials"], + "compatible": false + }, + { + "names": ["hexadecimal", "plus_zero"], + "compatible": true + }, + { + "names": ["hexadecimal", "plus_one"], + "compatible": true + }, + { + "names": ["hexadecimal", "plus_two"], + "compatible": true + }, + { + "names": ["hexadecimal", "plus_three"], + "compatible": true + }, + { + "names": ["hexadecimal", "read_ahead_easy"], + "compatible": true + }, + { + "names": ["hexadecimal", "read_ahead"], + "compatible": true + }, + { + "names": ["hexadecimal", "read_ahead_hard"], + "compatible": true + }, + { + "names": ["hexadecimal", "memory"], + "compatible": true + }, + { + "names": ["hexadecimal", "nospace"], + "compatible": true + }, + { + "names": ["hexadecimal", "poetry"], + "compatible": false + }, + { + "names": ["hexadecimal", "wikipedia"], + "compatible": false + }, + { + "names": ["hexadecimal", "weakspot"], + "compatible": false + }, + { + "names": ["hexadecimal", "pseudolang"], + "compatible": false + }, + { + "names": ["hexadecimal", "IPv4"], + "compatible": false + }, + { + "names": ["hexadecimal", "IPv6"], + "compatible": false + }, + { + "names": ["hexadecimal", "binary"], + "compatible": false + }, + { + "names": ["hexadecimal", "hexadecimal"], + "compatible": false + }, + { + "names": ["hexadecimal", "zipf"], + "compatible": false + }, + { + "names": ["hexadecimal", "morse"], + "compatible": true + }, + { + "names": ["hexadecimal", "crt"], + "compatible": true + }, + { + "names": ["hexadecimal", "backwards"], + "compatible": true + }, + { + "names": ["hexadecimal", "ddoouubblleedd"], + "compatible": true + }, + { + "names": ["hexadecimal", "instant_messaging"], + "compatible": false + }, + { + "names": ["hexadecimal", "underscore_spaces"], + "compatible": true + }, + { + "names": ["hexadecimal", "ALL_CAPS"], + "compatible": false + }, + { + "names": ["hexadecimal", "polyglot"], + "compatible": false + }, + { + "names": ["hexadecimal", "asl"], + "compatible": true + }, + { + "names": ["hexadecimal", "rot13"], + "compatible": true + }, + { + "names": ["hexadecimal", "no_quit"], + "compatible": true + }, + { + "names": ["zipf", "58008"], + "compatible": false + }, + { + "names": ["zipf", "mirror"], + "compatible": true + }, + { + "names": ["zipf", "upside_down"], + "compatible": true + }, + { + "names": ["zipf", "nausea"], + "compatible": true + }, + { + "names": ["zipf", "round_round_baby"], + "compatible": true + }, + { + "names": ["zipf", "simon_says"], + "compatible": true + }, + { + "names": ["zipf", "tts"], + "compatible": true + }, + { + "names": ["zipf", "choo_choo"], + "compatible": true + }, + { + "names": ["zipf", "arrows"], + "compatible": false + }, + { + "names": ["zipf", "rAnDoMcAsE"], + "compatible": true + }, + { + "names": ["zipf", "sPoNgEcAsE"], + "compatible": true + }, + { + "names": ["zipf", "capitals"], + "compatible": true + }, + { + "names": ["zipf", "layout_mirror"], + "compatible": true + }, + { + "names": ["zipf", "layoutfluid"], + "compatible": true + }, + { + "names": ["zipf", "earthquake"], + "compatible": true + }, + { + "names": ["zipf", "space_balls"], + "compatible": true + }, + { + "names": ["zipf", "gibberish"], + "compatible": false + }, + { + "names": ["zipf", "ascii"], + "compatible": false + }, + { + "names": ["zipf", "specials"], + "compatible": false + }, + { + "names": ["zipf", "plus_zero"], + "compatible": true + }, + { + "names": ["zipf", "plus_one"], + "compatible": true + }, + { + "names": ["zipf", "plus_two"], + "compatible": true + }, + { + "names": ["zipf", "plus_three"], + "compatible": true + }, + { + "names": ["zipf", "read_ahead_easy"], + "compatible": true + }, + { + "names": ["zipf", "read_ahead"], + "compatible": true + }, + { + "names": ["zipf", "read_ahead_hard"], + "compatible": true + }, + { + "names": ["zipf", "memory"], + "compatible": true + }, + { + "names": ["zipf", "nospace"], + "compatible": true + }, + { + "names": ["zipf", "poetry"], + "compatible": false + }, + { + "names": ["zipf", "wikipedia"], + "compatible": false + }, + { + "names": ["zipf", "weakspot"], + "compatible": false + }, + { + "names": ["zipf", "pseudolang"], + "compatible": false + }, + { + "names": ["zipf", "IPv4"], + "compatible": false + }, + { + "names": ["zipf", "IPv6"], + "compatible": false + }, + { + "names": ["zipf", "binary"], + "compatible": false + }, + { + "names": ["zipf", "hexadecimal"], + "compatible": false + }, + { + "names": ["zipf", "zipf"], + "compatible": false + }, + { + "names": ["zipf", "morse"], + "compatible": false + }, + { + "names": ["zipf", "crt"], + "compatible": true + }, + { + "names": ["zipf", "backwards"], + "compatible": true + }, + { + "names": ["zipf", "ddoouubblleedd"], + "compatible": true + }, + { + "names": ["zipf", "instant_messaging"], + "compatible": true + }, + { + "names": ["zipf", "underscore_spaces"], + "compatible": false + }, + { + "names": ["zipf", "ALL_CAPS"], + "compatible": true + }, + { + "names": ["zipf", "polyglot"], + "compatible": false + }, + { + "names": ["zipf", "asl"], + "compatible": true + }, + { + "names": ["zipf", "rot13"], + "compatible": true + }, + { + "names": ["zipf", "no_quit"], + "compatible": true + }, + { + "names": ["morse", "58008"], + "compatible": true + }, + { + "names": ["morse", "mirror"], + "compatible": true + }, + { + "names": ["morse", "upside_down"], + "compatible": true + }, + { + "names": ["morse", "nausea"], + "compatible": true + }, + { + "names": ["morse", "round_round_baby"], + "compatible": true + }, + { + "names": ["morse", "simon_says"], + "compatible": true + }, + { + "names": ["morse", "tts"], + "compatible": true + }, + { + "names": ["morse", "choo_choo"], + "compatible": true + }, + { + "names": ["morse", "arrows"], + "compatible": false + }, + { + "names": ["morse", "rAnDoMcAsE"], + "compatible": false + }, + { + "names": ["morse", "sPoNgEcAsE"], + "compatible": false + }, + { + "names": ["morse", "capitals"], + "compatible": false + }, + { + "names": ["morse", "layout_mirror"], + "compatible": false + }, + { + "names": ["morse", "layoutfluid"], + "compatible": false + }, + { + "names": ["morse", "earthquake"], + "compatible": true + }, + { + "names": ["morse", "space_balls"], + "compatible": true + }, + { + "names": ["morse", "gibberish"], + "compatible": true + }, + { + "names": ["morse", "ascii"], + "compatible": true + }, + { + "names": ["morse", "specials"], + "compatible": true + }, + { + "names": ["morse", "plus_zero"], + "compatible": false + }, + { + "names": ["morse", "plus_one"], + "compatible": false + }, + { + "names": ["morse", "plus_two"], + "compatible": false + }, + { + "names": ["morse", "plus_three"], + "compatible": false + }, + { + "names": ["morse", "read_ahead_easy"], + "compatible": true + }, + { + "names": ["morse", "read_ahead"], + "compatible": true + }, + { + "names": ["morse", "read_ahead_hard"], + "compatible": true + }, + { + "names": ["morse", "memory"], + "compatible": true + }, + { + "names": ["morse", "nospace"], + "compatible": false + }, + { + "names": ["morse", "poetry"], + "compatible": true + }, + { + "names": ["morse", "wikipedia"], + "compatible": true + }, + { + "names": ["morse", "weakspot"], + "compatible": false + }, + { + "names": ["morse", "pseudolang"], + "compatible": true + }, + { + "names": ["morse", "IPv4"], + "compatible": true + }, + { + "names": ["morse", "IPv6"], + "compatible": true + }, + { + "names": ["morse", "binary"], + "compatible": true + }, + { + "names": ["morse", "hexadecimal"], + "compatible": true + }, + { + "names": ["morse", "zipf"], + "compatible": false + }, + { + "names": ["morse", "morse"], + "compatible": false + }, + { + "names": ["morse", "crt"], + "compatible": true + }, + { + "names": ["morse", "backwards"], + "compatible": true + }, + { + "names": ["morse", "ddoouubblleedd"], + "compatible": true + }, + { + "names": ["morse", "instant_messaging"], + "compatible": false + }, + { + "names": ["morse", "underscore_spaces"], + "compatible": false + }, + { + "names": ["morse", "ALL_CAPS"], + "compatible": false + }, + { + "names": ["morse", "polyglot"], + "compatible": true + }, + { + "names": ["morse", "asl"], + "compatible": true + }, + { + "names": ["morse", "rot13"], + "compatible": true + }, + { + "names": ["morse", "no_quit"], + "compatible": true + }, + { + "names": ["crt", "58008"], + "compatible": true + }, + { + "names": ["crt", "mirror"], + "compatible": true + }, + { + "names": ["crt", "upside_down"], + "compatible": true + }, + { + "names": ["crt", "nausea"], + "compatible": true + }, + { + "names": ["crt", "round_round_baby"], + "compatible": true + }, + { + "names": ["crt", "simon_says"], + "compatible": true + }, + { + "names": ["crt", "tts"], + "compatible": true + }, + { + "names": ["crt", "choo_choo"], + "compatible": true + }, + { + "names": ["crt", "arrows"], + "compatible": true + }, + { + "names": ["crt", "rAnDoMcAsE"], + "compatible": true + }, + { + "names": ["crt", "sPoNgEcAsE"], + "compatible": true + }, + { + "names": ["crt", "capitals"], + "compatible": true + }, + { + "names": ["crt", "layout_mirror"], + "compatible": true + }, + { + "names": ["crt", "layoutfluid"], + "compatible": true + }, + { + "names": ["crt", "earthquake"], + "compatible": true + }, + { + "names": ["crt", "space_balls"], + "compatible": false + }, + { + "names": ["crt", "gibberish"], + "compatible": true + }, + { + "names": ["crt", "ascii"], + "compatible": true + }, + { + "names": ["crt", "specials"], + "compatible": true + }, + { + "names": ["crt", "plus_zero"], + "compatible": true + }, + { + "names": ["crt", "plus_one"], + "compatible": true + }, + { + "names": ["crt", "plus_two"], + "compatible": true + }, + { + "names": ["crt", "plus_three"], + "compatible": true + }, + { + "names": ["crt", "read_ahead_easy"], + "compatible": true + }, + { + "names": ["crt", "read_ahead"], + "compatible": true + }, + { + "names": ["crt", "read_ahead_hard"], + "compatible": true + }, + { + "names": ["crt", "memory"], + "compatible": true + }, + { + "names": ["crt", "nospace"], + "compatible": true + }, + { + "names": ["crt", "poetry"], + "compatible": true + }, + { + "names": ["crt", "wikipedia"], + "compatible": true + }, + { + "names": ["crt", "weakspot"], + "compatible": true + }, + { + "names": ["crt", "pseudolang"], + "compatible": true + }, + { + "names": ["crt", "IPv4"], + "compatible": true + }, + { + "names": ["crt", "IPv6"], + "compatible": true + }, + { + "names": ["crt", "binary"], + "compatible": true + }, + { + "names": ["crt", "hexadecimal"], + "compatible": true + }, + { + "names": ["crt", "zipf"], + "compatible": true + }, + { + "names": ["crt", "morse"], + "compatible": true + }, + { + "names": ["crt", "crt"], + "compatible": false + }, + { + "names": ["crt", "backwards"], + "compatible": true + }, + { + "names": ["crt", "ddoouubblleedd"], + "compatible": true + }, + { + "names": ["crt", "instant_messaging"], + "compatible": true + }, + { + "names": ["crt", "underscore_spaces"], + "compatible": true + }, + { + "names": ["crt", "ALL_CAPS"], + "compatible": true + }, + { + "names": ["crt", "polyglot"], + "compatible": true + }, + { + "names": ["crt", "asl"], + "compatible": true + }, + { + "names": ["crt", "rot13"], + "compatible": true + }, + { + "names": ["crt", "no_quit"], + "compatible": true + }, + { + "names": ["backwards", "58008"], + "compatible": true + }, + { + "names": ["backwards", "mirror"], + "compatible": true + }, + { + "names": ["backwards", "upside_down"], + "compatible": true + }, + { + "names": ["backwards", "nausea"], + "compatible": true + }, + { + "names": ["backwards", "round_round_baby"], + "compatible": true + }, + { + "names": ["backwards", "simon_says"], + "compatible": true + }, + { + "names": ["backwards", "tts"], + "compatible": false + }, + { + "names": ["backwards", "choo_choo"], + "compatible": false + }, + { + "names": ["backwards", "arrows"], + "compatible": false + }, + { + "names": ["backwards", "rAnDoMcAsE"], + "compatible": true + }, + { + "names": ["backwards", "sPoNgEcAsE"], + "compatible": true + }, + { + "names": ["backwards", "capitals"], + "compatible": true + }, + { + "names": ["backwards", "layout_mirror"], + "compatible": true + }, + { + "names": ["backwards", "layoutfluid"], + "compatible": true + }, + { + "names": ["backwards", "earthquake"], + "compatible": false + }, + { + "names": ["backwards", "space_balls"], + "compatible": true + }, + { + "names": ["backwards", "gibberish"], + "compatible": true + }, + { + "names": ["backwards", "ascii"], + "compatible": true + }, + { + "names": ["backwards", "specials"], + "compatible": true + }, + { + "names": ["backwards", "plus_zero"], + "compatible": true + }, + { + "names": ["backwards", "plus_one"], + "compatible": true + }, + { + "names": ["backwards", "plus_two"], + "compatible": true + }, + { + "names": ["backwards", "plus_three"], + "compatible": true + }, + { + "names": ["backwards", "read_ahead_easy"], + "compatible": true + }, + { + "names": ["backwards", "read_ahead"], + "compatible": true + }, + { + "names": ["backwards", "read_ahead_hard"], + "compatible": true + }, + { + "names": ["backwards", "memory"], + "compatible": true + }, + { + "names": ["backwards", "nospace"], + "compatible": true + }, + { + "names": ["backwards", "poetry"], + "compatible": true + }, + { + "names": ["backwards", "wikipedia"], + "compatible": true + }, + { + "names": ["backwards", "weakspot"], + "compatible": true + }, + { + "names": ["backwards", "pseudolang"], + "compatible": true + }, + { + "names": ["backwards", "IPv4"], + "compatible": true + }, + { + "names": ["backwards", "IPv6"], + "compatible": true + }, + { + "names": ["backwards", "binary"], + "compatible": true + }, + { + "names": ["backwards", "hexadecimal"], + "compatible": true + }, + { + "names": ["backwards", "zipf"], + "compatible": true + }, + { + "names": ["backwards", "morse"], + "compatible": true + }, + { + "names": ["backwards", "crt"], + "compatible": true + }, + { + "names": ["backwards", "backwards"], + "compatible": false + }, + { + "names": ["backwards", "ddoouubblleedd"], + "compatible": true + }, + { + "names": ["backwards", "instant_messaging"], + "compatible": true + }, + { + "names": ["backwards", "underscore_spaces"], + "compatible": true + }, + { + "names": ["backwards", "ALL_CAPS"], + "compatible": true + }, + { + "names": ["backwards", "polyglot"], + "compatible": true + }, + { + "names": ["backwards", "asl"], + "compatible": false + }, + { + "names": ["backwards", "rot13"], + "compatible": true + }, + { + "names": ["backwards", "no_quit"], + "compatible": true + }, + { + "names": ["ddoouubblleedd", "58008"], + "compatible": true + }, + { + "names": ["ddoouubblleedd", "mirror"], + "compatible": true + }, + { + "names": ["ddoouubblleedd", "upside_down"], + "compatible": true + }, + { + "names": ["ddoouubblleedd", "nausea"], + "compatible": true + }, + { + "names": ["ddoouubblleedd", "round_round_baby"], + "compatible": true + }, + { + "names": ["ddoouubblleedd", "simon_says"], + "compatible": true + }, + { + "names": ["ddoouubblleedd", "tts"], + "compatible": true + }, + { + "names": ["ddoouubblleedd", "choo_choo"], + "compatible": true + }, + { + "names": ["ddoouubblleedd", "arrows"], + "compatible": true + }, + { + "names": ["ddoouubblleedd", "rAnDoMcAsE"], + "compatible": true + }, + { + "names": ["ddoouubblleedd", "sPoNgEcAsE"], + "compatible": true + }, + { + "names": ["ddoouubblleedd", "capitals"], + "compatible": true + }, + { + "names": ["ddoouubblleedd", "layout_mirror"], + "compatible": true + }, + { + "names": ["ddoouubblleedd", "layoutfluid"], + "compatible": true + }, + { + "names": ["ddoouubblleedd", "earthquake"], + "compatible": true + }, + { + "names": ["ddoouubblleedd", "space_balls"], + "compatible": true + }, + { + "names": ["ddoouubblleedd", "gibberish"], + "compatible": true + }, + { + "names": ["ddoouubblleedd", "ascii"], + "compatible": true + }, + { + "names": ["ddoouubblleedd", "specials"], + "compatible": true + }, + { + "names": ["ddoouubblleedd", "plus_zero"], + "compatible": true + }, + { + "names": ["ddoouubblleedd", "plus_one"], + "compatible": true + }, + { + "names": ["ddoouubblleedd", "plus_two"], + "compatible": true + }, + { + "names": ["ddoouubblleedd", "plus_three"], + "compatible": true + }, + { + "names": ["ddoouubblleedd", "read_ahead_easy"], + "compatible": true + }, + { + "names": ["ddoouubblleedd", "read_ahead"], + "compatible": true + }, + { + "names": ["ddoouubblleedd", "read_ahead_hard"], + "compatible": true + }, + { + "names": ["ddoouubblleedd", "memory"], + "compatible": true + }, + { + "names": ["ddoouubblleedd", "nospace"], + "compatible": true + }, + { + "names": ["ddoouubblleedd", "poetry"], + "compatible": true + }, + { + "names": ["ddoouubblleedd", "wikipedia"], + "compatible": true + }, + { + "names": ["ddoouubblleedd", "weakspot"], + "compatible": true + }, + { + "names": ["ddoouubblleedd", "pseudolang"], + "compatible": true + }, + { + "names": ["ddoouubblleedd", "IPv4"], + "compatible": true + }, + { + "names": ["ddoouubblleedd", "IPv6"], + "compatible": true + }, + { + "names": ["ddoouubblleedd", "binary"], + "compatible": true + }, + { + "names": ["ddoouubblleedd", "hexadecimal"], + "compatible": true + }, + { + "names": ["ddoouubblleedd", "zipf"], + "compatible": true + }, + { + "names": ["ddoouubblleedd", "morse"], + "compatible": true + }, + { + "names": ["ddoouubblleedd", "crt"], + "compatible": true + }, + { + "names": ["ddoouubblleedd", "backwards"], + "compatible": true + }, + { + "names": ["ddoouubblleedd", "ddoouubblleedd"], + "compatible": true + }, + { + "names": ["ddoouubblleedd", "instant_messaging"], + "compatible": true + }, + { + "names": ["ddoouubblleedd", "underscore_spaces"], + "compatible": true + }, + { + "names": ["ddoouubblleedd", "ALL_CAPS"], + "compatible": true + }, + { + "names": ["ddoouubblleedd", "polyglot"], + "compatible": true + }, + { + "names": ["ddoouubblleedd", "asl"], + "compatible": true + }, + { + "names": ["ddoouubblleedd", "rot13"], + "compatible": true + }, + { + "names": ["ddoouubblleedd", "no_quit"], + "compatible": true + }, + { + "names": ["instant_messaging", "58008"], + "compatible": false + }, + { + "names": ["instant_messaging", "mirror"], + "compatible": true + }, + { + "names": ["instant_messaging", "upside_down"], + "compatible": true + }, + { + "names": ["instant_messaging", "nausea"], + "compatible": true + }, + { + "names": ["instant_messaging", "round_round_baby"], + "compatible": true + }, + { + "names": ["instant_messaging", "simon_says"], + "compatible": true + }, + { + "names": ["instant_messaging", "tts"], + "compatible": true + }, + { + "names": ["instant_messaging", "choo_choo"], + "compatible": true + }, + { + "names": ["instant_messaging", "arrows"], + "compatible": false + }, + { + "names": ["instant_messaging", "rAnDoMcAsE"], + "compatible": false + }, + { + "names": ["instant_messaging", "sPoNgEcAsE"], + "compatible": false + }, + { + "names": ["instant_messaging", "capitals"], + "compatible": false + }, + { + "names": ["instant_messaging", "layout_mirror"], + "compatible": true + }, + { + "names": ["instant_messaging", "layoutfluid"], + "compatible": true + }, + { + "names": ["instant_messaging", "earthquake"], + "compatible": true + }, + { + "names": ["instant_messaging", "space_balls"], + "compatible": true + }, + { + "names": ["instant_messaging", "gibberish"], + "compatible": true + }, + { + "names": ["instant_messaging", "ascii"], + "compatible": false + }, + { + "names": ["instant_messaging", "specials"], + "compatible": false + }, + { + "names": ["instant_messaging", "plus_zero"], + "compatible": true + }, + { + "names": ["instant_messaging", "plus_one"], + "compatible": true + }, + { + "names": ["instant_messaging", "plus_two"], + "compatible": true + }, + { + "names": ["instant_messaging", "plus_three"], + "compatible": true + }, + { + "names": ["instant_messaging", "read_ahead_easy"], + "compatible": true + }, + { + "names": ["instant_messaging", "read_ahead"], + "compatible": true + }, + { + "names": ["instant_messaging", "read_ahead_hard"], + "compatible": true + }, + { + "names": ["instant_messaging", "memory"], + "compatible": true + }, + { + "names": ["instant_messaging", "nospace"], + "compatible": true + }, + { + "names": ["instant_messaging", "poetry"], + "compatible": true + }, + { + "names": ["instant_messaging", "wikipedia"], + "compatible": true + }, + { + "names": ["instant_messaging", "weakspot"], + "compatible": true + }, + { + "names": ["instant_messaging", "pseudolang"], + "compatible": true + }, + { + "names": ["instant_messaging", "IPv4"], + "compatible": false + }, + { + "names": ["instant_messaging", "IPv6"], + "compatible": false + }, + { + "names": ["instant_messaging", "binary"], + "compatible": false + }, + { + "names": ["instant_messaging", "hexadecimal"], + "compatible": false + }, + { + "names": ["instant_messaging", "zipf"], + "compatible": true + }, + { + "names": ["instant_messaging", "morse"], + "compatible": false + }, + { + "names": ["instant_messaging", "crt"], + "compatible": true + }, + { + "names": ["instant_messaging", "backwards"], + "compatible": true + }, + { + "names": ["instant_messaging", "ddoouubblleedd"], + "compatible": true + }, + { + "names": ["instant_messaging", "instant_messaging"], + "compatible": false + }, + { + "names": ["instant_messaging", "underscore_spaces"], + "compatible": true + }, + { + "names": ["instant_messaging", "ALL_CAPS"], + "compatible": false + }, + { + "names": ["instant_messaging", "polyglot"], + "compatible": true + }, + { + "names": ["instant_messaging", "asl"], + "compatible": true + }, + { + "names": ["instant_messaging", "rot13"], + "compatible": true + }, + { + "names": ["instant_messaging", "no_quit"], + "compatible": true + }, + { + "names": ["underscore_spaces", "58008"], + "compatible": true + }, + { + "names": ["underscore_spaces", "mirror"], + "compatible": true + }, + { + "names": ["underscore_spaces", "upside_down"], + "compatible": true + }, + { + "names": ["underscore_spaces", "nausea"], + "compatible": true + }, + { + "names": ["underscore_spaces", "round_round_baby"], + "compatible": true + }, + { + "names": ["underscore_spaces", "simon_says"], + "compatible": true + }, + { + "names": ["underscore_spaces", "tts"], + "compatible": true + }, + { + "names": ["underscore_spaces", "choo_choo"], + "compatible": true + }, + { + "names": ["underscore_spaces", "arrows"], + "compatible": false + }, + { + "names": ["underscore_spaces", "rAnDoMcAsE"], + "compatible": true + }, + { + "names": ["underscore_spaces", "sPoNgEcAsE"], + "compatible": true + }, + { + "names": ["underscore_spaces", "capitals"], + "compatible": true + }, + { + "names": ["underscore_spaces", "layout_mirror"], + "compatible": false + }, + { + "names": ["underscore_spaces", "layoutfluid"], + "compatible": false + }, + { + "names": ["underscore_spaces", "earthquake"], + "compatible": true + }, + { + "names": ["underscore_spaces", "space_balls"], + "compatible": true + }, + { + "names": ["underscore_spaces", "gibberish"], + "compatible": true + }, + { + "names": ["underscore_spaces", "ascii"], + "compatible": true + }, + { + "names": ["underscore_spaces", "specials"], + "compatible": true + }, + { + "names": ["underscore_spaces", "plus_zero"], + "compatible": false + }, + { + "names": ["underscore_spaces", "plus_one"], + "compatible": false + }, + { + "names": ["underscore_spaces", "plus_two"], + "compatible": false + }, + { + "names": ["underscore_spaces", "plus_three"], + "compatible": false + }, + { + "names": ["underscore_spaces", "read_ahead_easy"], + "compatible": true + }, + { + "names": ["underscore_spaces", "read_ahead"], + "compatible": true + }, + { + "names": ["underscore_spaces", "read_ahead_hard"], + "compatible": true + }, + { + "names": ["underscore_spaces", "memory"], + "compatible": true + }, + { + "names": ["underscore_spaces", "nospace"], + "compatible": false + }, + { + "names": ["underscore_spaces", "poetry"], + "compatible": true + }, + { + "names": ["underscore_spaces", "wikipedia"], + "compatible": true + }, + { + "names": ["underscore_spaces", "weakspot"], + "compatible": false + }, + { + "names": ["underscore_spaces", "pseudolang"], + "compatible": true + }, + { + "names": ["underscore_spaces", "IPv4"], + "compatible": true + }, + { + "names": ["underscore_spaces", "IPv6"], + "compatible": true + }, + { + "names": ["underscore_spaces", "binary"], + "compatible": true + }, + { + "names": ["underscore_spaces", "hexadecimal"], + "compatible": true + }, + { + "names": ["underscore_spaces", "zipf"], + "compatible": false + }, + { + "names": ["underscore_spaces", "morse"], + "compatible": false + }, + { + "names": ["underscore_spaces", "crt"], + "compatible": true + }, + { + "names": ["underscore_spaces", "backwards"], + "compatible": true + }, + { + "names": ["underscore_spaces", "ddoouubblleedd"], + "compatible": true + }, + { + "names": ["underscore_spaces", "instant_messaging"], + "compatible": true + }, + { + "names": ["underscore_spaces", "underscore_spaces"], + "compatible": false + }, + { + "names": ["underscore_spaces", "ALL_CAPS"], + "compatible": true + }, + { + "names": ["underscore_spaces", "polyglot"], + "compatible": true + }, + { + "names": ["underscore_spaces", "asl"], + "compatible": true + }, + { + "names": ["underscore_spaces", "rot13"], + "compatible": true + }, + { + "names": ["underscore_spaces", "no_quit"], + "compatible": true + }, + { + "names": ["ALL_CAPS", "58008"], + "compatible": false + }, + { + "names": ["ALL_CAPS", "mirror"], + "compatible": true + }, + { + "names": ["ALL_CAPS", "upside_down"], + "compatible": true + }, + { + "names": ["ALL_CAPS", "nausea"], + "compatible": true + }, + { + "names": ["ALL_CAPS", "round_round_baby"], + "compatible": true + }, + { + "names": ["ALL_CAPS", "simon_says"], + "compatible": true + }, + { + "names": ["ALL_CAPS", "tts"], + "compatible": true + }, + { + "names": ["ALL_CAPS", "choo_choo"], + "compatible": true + }, + { + "names": ["ALL_CAPS", "arrows"], + "compatible": false + }, + { + "names": ["ALL_CAPS", "rAnDoMcAsE"], + "compatible": false + }, + { + "names": ["ALL_CAPS", "sPoNgEcAsE"], + "compatible": false + }, + { + "names": ["ALL_CAPS", "capitals"], + "compatible": false + }, + { + "names": ["ALL_CAPS", "layout_mirror"], + "compatible": true + }, + { + "names": ["ALL_CAPS", "layoutfluid"], + "compatible": true + }, + { + "names": ["ALL_CAPS", "earthquake"], + "compatible": true + }, + { + "names": ["ALL_CAPS", "space_balls"], + "compatible": true + }, + { + "names": ["ALL_CAPS", "gibberish"], + "compatible": true + }, + { + "names": ["ALL_CAPS", "ascii"], + "compatible": false + }, + { + "names": ["ALL_CAPS", "specials"], + "compatible": false + }, + { + "names": ["ALL_CAPS", "plus_zero"], + "compatible": true + }, + { + "names": ["ALL_CAPS", "plus_one"], + "compatible": true + }, + { + "names": ["ALL_CAPS", "plus_two"], + "compatible": true + }, + { + "names": ["ALL_CAPS", "plus_three"], + "compatible": true + }, + { + "names": ["ALL_CAPS", "read_ahead_easy"], + "compatible": true + }, + { + "names": ["ALL_CAPS", "read_ahead"], + "compatible": true + }, + { + "names": ["ALL_CAPS", "read_ahead_hard"], + "compatible": true + }, + { + "names": ["ALL_CAPS", "memory"], + "compatible": true + }, + { + "names": ["ALL_CAPS", "nospace"], + "compatible": true + }, + { + "names": ["ALL_CAPS", "poetry"], + "compatible": true + }, + { + "names": ["ALL_CAPS", "wikipedia"], + "compatible": true + }, + { + "names": ["ALL_CAPS", "weakspot"], + "compatible": true + }, + { + "names": ["ALL_CAPS", "pseudolang"], + "compatible": true + }, + { + "names": ["ALL_CAPS", "IPv4"], + "compatible": false + }, + { + "names": ["ALL_CAPS", "IPv6"], + "compatible": false + }, + { + "names": ["ALL_CAPS", "binary"], + "compatible": false + }, + { + "names": ["ALL_CAPS", "hexadecimal"], + "compatible": false + }, + { + "names": ["ALL_CAPS", "zipf"], + "compatible": true + }, + { + "names": ["ALL_CAPS", "morse"], + "compatible": false + }, + { + "names": ["ALL_CAPS", "crt"], + "compatible": true + }, + { + "names": ["ALL_CAPS", "backwards"], + "compatible": true + }, + { + "names": ["ALL_CAPS", "ddoouubblleedd"], + "compatible": true + }, + { + "names": ["ALL_CAPS", "instant_messaging"], + "compatible": false + }, + { + "names": ["ALL_CAPS", "underscore_spaces"], + "compatible": true + }, + { + "names": ["ALL_CAPS", "ALL_CAPS"], + "compatible": false + }, + { + "names": ["ALL_CAPS", "polyglot"], + "compatible": true + }, + { + "names": ["ALL_CAPS", "asl"], + "compatible": true + }, + { + "names": ["ALL_CAPS", "rot13"], + "compatible": true + }, + { + "names": ["ALL_CAPS", "no_quit"], + "compatible": true + }, + { + "names": ["polyglot", "58008"], + "compatible": false + }, + { + "names": ["polyglot", "mirror"], + "compatible": true + }, + { + "names": ["polyglot", "upside_down"], + "compatible": true + }, + { + "names": ["polyglot", "nausea"], + "compatible": true + }, + { + "names": ["polyglot", "round_round_baby"], + "compatible": true + }, + { + "names": ["polyglot", "simon_says"], + "compatible": true + }, + { + "names": ["polyglot", "tts"], + "compatible": true + }, + { + "names": ["polyglot", "choo_choo"], + "compatible": true + }, + { + "names": ["polyglot", "arrows"], + "compatible": false + }, + { + "names": ["polyglot", "rAnDoMcAsE"], + "compatible": true + }, + { + "names": ["polyglot", "sPoNgEcAsE"], + "compatible": true + }, + { + "names": ["polyglot", "capitals"], + "compatible": true + }, + { + "names": ["polyglot", "layout_mirror"], + "compatible": true + }, + { + "names": ["polyglot", "layoutfluid"], + "compatible": true + }, + { + "names": ["polyglot", "earthquake"], + "compatible": true + }, + { + "names": ["polyglot", "space_balls"], + "compatible": true + }, + { + "names": ["polyglot", "gibberish"], + "compatible": false + }, + { + "names": ["polyglot", "ascii"], + "compatible": false + }, + { + "names": ["polyglot", "specials"], + "compatible": false + }, + { + "names": ["polyglot", "plus_zero"], + "compatible": true + }, + { + "names": ["polyglot", "plus_one"], + "compatible": true + }, + { + "names": ["polyglot", "plus_two"], + "compatible": true + }, + { + "names": ["polyglot", "plus_three"], + "compatible": true + }, + { + "names": ["polyglot", "read_ahead_easy"], + "compatible": true + }, + { + "names": ["polyglot", "read_ahead"], + "compatible": true + }, + { + "names": ["polyglot", "read_ahead_hard"], + "compatible": true + }, + { + "names": ["polyglot", "memory"], + "compatible": true + }, + { + "names": ["polyglot", "nospace"], + "compatible": true + }, + { + "names": ["polyglot", "poetry"], + "compatible": false + }, + { + "names": ["polyglot", "wikipedia"], + "compatible": false + }, + { + "names": ["polyglot", "weakspot"], + "compatible": false + }, + { + "names": ["polyglot", "pseudolang"], + "compatible": false + }, + { + "names": ["polyglot", "IPv4"], + "compatible": false + }, + { + "names": ["polyglot", "IPv6"], + "compatible": false + }, + { + "names": ["polyglot", "binary"], + "compatible": false + }, + { + "names": ["polyglot", "hexadecimal"], + "compatible": false + }, + { + "names": ["polyglot", "zipf"], + "compatible": false + }, + { + "names": ["polyglot", "morse"], + "compatible": true + }, + { + "names": ["polyglot", "crt"], + "compatible": true + }, + { + "names": ["polyglot", "backwards"], + "compatible": true + }, + { + "names": ["polyglot", "ddoouubblleedd"], + "compatible": true + }, + { + "names": ["polyglot", "instant_messaging"], + "compatible": true + }, + { + "names": ["polyglot", "underscore_spaces"], + "compatible": true + }, + { + "names": ["polyglot", "ALL_CAPS"], + "compatible": true + }, + { + "names": ["polyglot", "polyglot"], + "compatible": false + }, + { + "names": ["polyglot", "asl"], + "compatible": true + }, + { + "names": ["polyglot", "rot13"], + "compatible": true + }, + { + "names": ["polyglot", "no_quit"], + "compatible": true + }, + { + "names": ["asl", "58008"], + "compatible": true + }, + { + "names": ["asl", "mirror"], + "compatible": true + }, + { + "names": ["asl", "upside_down"], + "compatible": true + }, + { + "names": ["asl", "nausea"], + "compatible": true + }, + { + "names": ["asl", "round_round_baby"], + "compatible": true + }, + { + "names": ["asl", "simon_says"], + "compatible": true + }, + { + "names": ["asl", "tts"], + "compatible": false + }, + { + "names": ["asl", "choo_choo"], + "compatible": false + }, + { + "names": ["asl", "arrows"], + "compatible": true + }, + { + "names": ["asl", "rAnDoMcAsE"], + "compatible": true + }, + { + "names": ["asl", "sPoNgEcAsE"], + "compatible": true + }, + { + "names": ["asl", "capitals"], + "compatible": true + }, + { + "names": ["asl", "layout_mirror"], + "compatible": true + }, + { + "names": ["asl", "layoutfluid"], + "compatible": true + }, + { + "names": ["asl", "earthquake"], + "compatible": false + }, + { + "names": ["asl", "space_balls"], + "compatible": true + }, + { + "names": ["asl", "gibberish"], + "compatible": true + }, + { + "names": ["asl", "ascii"], + "compatible": true + }, + { + "names": ["asl", "specials"], + "compatible": true + }, + { + "names": ["asl", "plus_zero"], + "compatible": true + }, + { + "names": ["asl", "plus_one"], + "compatible": true + }, + { + "names": ["asl", "plus_two"], + "compatible": true + }, + { + "names": ["asl", "plus_three"], + "compatible": true + }, + { + "names": ["asl", "read_ahead_easy"], + "compatible": true + }, + { + "names": ["asl", "read_ahead"], + "compatible": true + }, + { + "names": ["asl", "read_ahead_hard"], + "compatible": true + }, + { + "names": ["asl", "memory"], + "compatible": true + }, + { + "names": ["asl", "nospace"], + "compatible": true + }, + { + "names": ["asl", "poetry"], + "compatible": true + }, + { + "names": ["asl", "wikipedia"], + "compatible": true + }, + { + "names": ["asl", "weakspot"], + "compatible": true + }, + { + "names": ["asl", "pseudolang"], + "compatible": true + }, + { + "names": ["asl", "IPv4"], + "compatible": true + }, + { + "names": ["asl", "IPv6"], + "compatible": true + }, + { + "names": ["asl", "binary"], + "compatible": true + }, + { + "names": ["asl", "hexadecimal"], + "compatible": true + }, + { + "names": ["asl", "zipf"], + "compatible": true + }, + { + "names": ["asl", "morse"], + "compatible": true + }, + { + "names": ["asl", "crt"], + "compatible": true + }, + { + "names": ["asl", "backwards"], + "compatible": false + }, + { + "names": ["asl", "ddoouubblleedd"], + "compatible": true + }, + { + "names": ["asl", "instant_messaging"], + "compatible": true + }, + { + "names": ["asl", "underscore_spaces"], + "compatible": true + }, + { + "names": ["asl", "ALL_CAPS"], + "compatible": true + }, + { + "names": ["asl", "polyglot"], + "compatible": true + }, + { + "names": ["asl", "asl"], + "compatible": false + }, + { + "names": ["asl", "rot13"], + "compatible": true + }, + { + "names": ["asl", "no_quit"], + "compatible": true + }, + { + "names": ["rot13", "58008"], + "compatible": true + }, + { + "names": ["rot13", "mirror"], + "compatible": true + }, + { + "names": ["rot13", "upside_down"], + "compatible": true + }, + { + "names": ["rot13", "nausea"], + "compatible": true + }, + { + "names": ["rot13", "round_round_baby"], + "compatible": true + }, + { + "names": ["rot13", "simon_says"], + "compatible": true + }, + { + "names": ["rot13", "tts"], + "compatible": true + }, + { + "names": ["rot13", "choo_choo"], + "compatible": true + }, + { + "names": ["rot13", "arrows"], + "compatible": true + }, + { + "names": ["rot13", "rAnDoMcAsE"], + "compatible": true + }, + { + "names": ["rot13", "sPoNgEcAsE"], + "compatible": true + }, + { + "names": ["rot13", "capitals"], + "compatible": true + }, + { + "names": ["rot13", "layout_mirror"], + "compatible": true + }, + { + "names": ["rot13", "layoutfluid"], + "compatible": true + }, + { + "names": ["rot13", "earthquake"], + "compatible": true + }, + { + "names": ["rot13", "space_balls"], + "compatible": true + }, + { + "names": ["rot13", "gibberish"], + "compatible": true + }, + { + "names": ["rot13", "ascii"], + "compatible": true + }, + { + "names": ["rot13", "specials"], + "compatible": true + }, + { + "names": ["rot13", "plus_zero"], + "compatible": true + }, + { + "names": ["rot13", "plus_one"], + "compatible": true + }, + { + "names": ["rot13", "plus_two"], + "compatible": true + }, + { + "names": ["rot13", "plus_three"], + "compatible": true + }, + { + "names": ["rot13", "read_ahead_easy"], + "compatible": true + }, + { + "names": ["rot13", "read_ahead"], + "compatible": true + }, + { + "names": ["rot13", "read_ahead_hard"], + "compatible": true + }, + { + "names": ["rot13", "memory"], + "compatible": true + }, + { + "names": ["rot13", "nospace"], + "compatible": true + }, + { + "names": ["rot13", "poetry"], + "compatible": true + }, + { + "names": ["rot13", "wikipedia"], + "compatible": true + }, + { + "names": ["rot13", "weakspot"], + "compatible": true + }, + { + "names": ["rot13", "pseudolang"], + "compatible": true + }, + { + "names": ["rot13", "IPv4"], + "compatible": true + }, + { + "names": ["rot13", "IPv6"], + "compatible": true + }, + { + "names": ["rot13", "binary"], + "compatible": true + }, + { + "names": ["rot13", "hexadecimal"], + "compatible": true + }, + { + "names": ["rot13", "zipf"], + "compatible": true + }, + { + "names": ["rot13", "morse"], + "compatible": true + }, + { + "names": ["rot13", "crt"], + "compatible": true + }, + { + "names": ["rot13", "backwards"], + "compatible": true + }, + { + "names": ["rot13", "ddoouubblleedd"], + "compatible": true + }, + { + "names": ["rot13", "instant_messaging"], + "compatible": true + }, + { + "names": ["rot13", "underscore_spaces"], + "compatible": true + }, + { + "names": ["rot13", "ALL_CAPS"], + "compatible": true + }, + { + "names": ["rot13", "polyglot"], + "compatible": true + }, + { + "names": ["rot13", "asl"], + "compatible": true + }, + { + "names": ["rot13", "rot13"], + "compatible": true + }, + { + "names": ["rot13", "no_quit"], + "compatible": true + }, + { + "names": ["no_quit", "58008"], + "compatible": true + }, + { + "names": ["no_quit", "mirror"], + "compatible": true + }, + { + "names": ["no_quit", "upside_down"], + "compatible": true + }, + { + "names": ["no_quit", "nausea"], + "compatible": true + }, + { + "names": ["no_quit", "round_round_baby"], + "compatible": true + }, + { + "names": ["no_quit", "simon_says"], + "compatible": true + }, + { + "names": ["no_quit", "tts"], + "compatible": true + }, + { + "names": ["no_quit", "choo_choo"], + "compatible": true + }, + { + "names": ["no_quit", "arrows"], + "compatible": true + }, + { + "names": ["no_quit", "rAnDoMcAsE"], + "compatible": true + }, + { + "names": ["no_quit", "sPoNgEcAsE"], + "compatible": true + }, + { + "names": ["no_quit", "capitals"], + "compatible": true + }, + { + "names": ["no_quit", "layout_mirror"], + "compatible": true + }, + { + "names": ["no_quit", "layoutfluid"], + "compatible": true + }, + { + "names": ["no_quit", "earthquake"], + "compatible": true + }, + { + "names": ["no_quit", "space_balls"], + "compatible": true + }, + { + "names": ["no_quit", "gibberish"], + "compatible": true + }, + { + "names": ["no_quit", "ascii"], + "compatible": true + }, + { + "names": ["no_quit", "specials"], + "compatible": true + }, + { + "names": ["no_quit", "plus_zero"], + "compatible": true + }, + { + "names": ["no_quit", "plus_one"], + "compatible": true + }, + { + "names": ["no_quit", "plus_two"], + "compatible": true + }, + { + "names": ["no_quit", "plus_three"], + "compatible": true + }, + { + "names": ["no_quit", "read_ahead_easy"], + "compatible": true + }, + { + "names": ["no_quit", "read_ahead"], + "compatible": true + }, + { + "names": ["no_quit", "read_ahead_hard"], + "compatible": true + }, + { + "names": ["no_quit", "memory"], + "compatible": true + }, + { + "names": ["no_quit", "nospace"], + "compatible": true + }, + { + "names": ["no_quit", "poetry"], + "compatible": true + }, + { + "names": ["no_quit", "wikipedia"], + "compatible": true + }, + { + "names": ["no_quit", "weakspot"], + "compatible": true + }, + { + "names": ["no_quit", "pseudolang"], + "compatible": true + }, + { + "names": ["no_quit", "IPv4"], + "compatible": true + }, + { + "names": ["no_quit", "IPv6"], + "compatible": true + }, + { + "names": ["no_quit", "binary"], + "compatible": true + }, + { + "names": ["no_quit", "hexadecimal"], + "compatible": true + }, + { + "names": ["no_quit", "zipf"], + "compatible": true + }, + { + "names": ["no_quit", "morse"], + "compatible": true + }, + { + "names": ["no_quit", "crt"], + "compatible": true + }, + { + "names": ["no_quit", "backwards"], + "compatible": true + }, + { + "names": ["no_quit", "ddoouubblleedd"], + "compatible": true + }, + { + "names": ["no_quit", "instant_messaging"], + "compatible": true + }, + { + "names": ["no_quit", "underscore_spaces"], + "compatible": true + }, + { + "names": ["no_quit", "ALL_CAPS"], + "compatible": true + }, + { + "names": ["no_quit", "polyglot"], + "compatible": true + }, + { + "names": ["no_quit", "asl"], + "compatible": true + }, + { + "names": ["no_quit", "rot13"], + "compatible": true + }, + { + "names": ["no_quit", "no_quit"], + "compatible": true + } + ], + "forced": [ + { + "names": ["58008"], + "key": "mode", + "value": "time", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["58008"], + "key": "mode", + "value": "words", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["58008"], + "key": "mode", + "value": "quote", + "forced": { + "result": true + }, + "canSet": false + }, + { + "names": ["58008"], + "key": "mode", + "value": "custom", + "forced": { + "result": true + }, + "canSet": false + }, + { + "names": ["58008"], + "key": "mode", + "value": "zen", + "forced": { + "result": true + }, + "canSet": false + }, + { + "names": ["58008"], + "key": "words", + "value": 0, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["58008"], + "key": "words", + "value": 10, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["58008"], + "key": "time", + "value": 0, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["58008"], + "key": "time", + "value": 15, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["58008"], + "key": "punctuation", + "value": true, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["58008"], + "key": "punctuation", + "value": false, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["58008"], + "key": "numbers", + "value": true, + "forced": { + "result": false, + "forcedConfigs": [false] + }, + "canSet": false + }, + { + "names": ["58008"], + "key": "numbers", + "value": false, + "forced": { + "result": true, + "forcedConfigs": [false] + }, + "canSet": true + }, + { + "names": ["58008"], + "key": "highlightMode", + "value": "word", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["58008"], + "key": "highlightMode", + "value": "letter", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["58008"], + "key": "highlightMode", + "value": "off", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["mirror"], + "key": "mode", + "value": "time", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["mirror"], + "key": "mode", + "value": "words", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["mirror"], + "key": "mode", + "value": "quote", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["mirror"], + "key": "mode", + "value": "custom", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["mirror"], + "key": "mode", + "value": "zen", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["mirror"], + "key": "words", + "value": 0, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["mirror"], + "key": "words", + "value": 10, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["mirror"], + "key": "time", + "value": 0, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["mirror"], + "key": "time", + "value": 15, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["mirror"], + "key": "punctuation", + "value": true, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["mirror"], + "key": "punctuation", + "value": false, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["mirror"], + "key": "numbers", + "value": true, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["mirror"], + "key": "numbers", + "value": false, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["mirror"], + "key": "highlightMode", + "value": "word", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["mirror"], + "key": "highlightMode", + "value": "letter", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["mirror"], + "key": "highlightMode", + "value": "off", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["upside_down"], + "key": "mode", + "value": "time", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["upside_down"], + "key": "mode", + "value": "words", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["upside_down"], + "key": "mode", + "value": "quote", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["upside_down"], + "key": "mode", + "value": "custom", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["upside_down"], + "key": "mode", + "value": "zen", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["upside_down"], + "key": "words", + "value": 0, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["upside_down"], + "key": "words", + "value": 10, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["upside_down"], + "key": "time", + "value": 0, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["upside_down"], + "key": "time", + "value": 15, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["upside_down"], + "key": "punctuation", + "value": true, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["upside_down"], + "key": "punctuation", + "value": false, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["upside_down"], + "key": "numbers", + "value": true, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["upside_down"], + "key": "numbers", + "value": false, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["upside_down"], + "key": "highlightMode", + "value": "word", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["upside_down"], + "key": "highlightMode", + "value": "letter", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["upside_down"], + "key": "highlightMode", + "value": "off", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["nausea"], + "key": "mode", + "value": "time", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["nausea"], + "key": "mode", + "value": "words", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["nausea"], + "key": "mode", + "value": "quote", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["nausea"], + "key": "mode", + "value": "custom", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["nausea"], + "key": "mode", + "value": "zen", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["nausea"], + "key": "words", + "value": 0, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["nausea"], + "key": "words", + "value": 10, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["nausea"], + "key": "time", + "value": 0, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["nausea"], + "key": "time", + "value": 15, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["nausea"], + "key": "punctuation", + "value": true, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["nausea"], + "key": "punctuation", + "value": false, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["nausea"], + "key": "numbers", + "value": true, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["nausea"], + "key": "numbers", + "value": false, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["nausea"], + "key": "highlightMode", + "value": "word", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["nausea"], + "key": "highlightMode", + "value": "letter", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["nausea"], + "key": "highlightMode", + "value": "off", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["round_round_baby"], + "key": "mode", + "value": "time", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["round_round_baby"], + "key": "mode", + "value": "words", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["round_round_baby"], + "key": "mode", + "value": "quote", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["round_round_baby"], + "key": "mode", + "value": "custom", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["round_round_baby"], + "key": "mode", + "value": "zen", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["round_round_baby"], + "key": "words", + "value": 0, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["round_round_baby"], + "key": "words", + "value": 10, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["round_round_baby"], + "key": "time", + "value": 0, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["round_round_baby"], + "key": "time", + "value": 15, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["round_round_baby"], + "key": "punctuation", + "value": true, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["round_round_baby"], + "key": "punctuation", + "value": false, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["round_round_baby"], + "key": "numbers", + "value": true, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["round_round_baby"], + "key": "numbers", + "value": false, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["round_round_baby"], + "key": "highlightMode", + "value": "word", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["round_round_baby"], + "key": "highlightMode", + "value": "letter", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["round_round_baby"], + "key": "highlightMode", + "value": "off", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["simon_says"], + "key": "mode", + "value": "time", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["simon_says"], + "key": "mode", + "value": "words", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["simon_says"], + "key": "mode", + "value": "quote", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["simon_says"], + "key": "mode", + "value": "custom", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["simon_says"], + "key": "mode", + "value": "zen", + "forced": { + "result": true + }, + "canSet": false + }, + { + "names": ["simon_says"], + "key": "words", + "value": 0, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["simon_says"], + "key": "words", + "value": 10, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["simon_says"], + "key": "time", + "value": 0, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["simon_says"], + "key": "time", + "value": 15, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["simon_says"], + "key": "punctuation", + "value": true, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["simon_says"], + "key": "punctuation", + "value": false, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["simon_says"], + "key": "numbers", + "value": true, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["simon_says"], + "key": "numbers", + "value": false, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["simon_says"], + "key": "highlightMode", + "value": "word", + "forced": { + "result": false, + "forcedConfigs": ["letter", "off"] + }, + "canSet": false + }, + { + "names": ["simon_says"], + "key": "highlightMode", + "value": "letter", + "forced": { + "result": true, + "forcedConfigs": ["letter", "off"] + }, + "canSet": true + }, + { + "names": ["simon_says"], + "key": "highlightMode", + "value": "off", + "forced": { + "result": true, + "forcedConfigs": ["letter", "off"] + }, + "canSet": true + }, + { + "names": ["tts"], + "key": "mode", + "value": "time", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["tts"], + "key": "mode", + "value": "words", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["tts"], + "key": "mode", + "value": "quote", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["tts"], + "key": "mode", + "value": "custom", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["tts"], + "key": "mode", + "value": "zen", + "forced": { + "result": true + }, + "canSet": false + }, + { + "names": ["tts"], + "key": "words", + "value": 0, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["tts"], + "key": "words", + "value": 10, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["tts"], + "key": "time", + "value": 0, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["tts"], + "key": "time", + "value": 15, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["tts"], + "key": "punctuation", + "value": true, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["tts"], + "key": "punctuation", + "value": false, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["tts"], + "key": "numbers", + "value": true, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["tts"], + "key": "numbers", + "value": false, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["tts"], + "key": "highlightMode", + "value": "word", + "forced": { + "result": false, + "forcedConfigs": ["letter", "off"] + }, + "canSet": false + }, + { + "names": ["tts"], + "key": "highlightMode", + "value": "letter", + "forced": { + "result": true, + "forcedConfigs": ["letter", "off"] + }, + "canSet": true + }, + { + "names": ["tts"], + "key": "highlightMode", + "value": "off", + "forced": { + "result": true, + "forcedConfigs": ["letter", "off"] + }, + "canSet": true + }, + { + "names": ["choo_choo"], + "key": "mode", + "value": "time", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["choo_choo"], + "key": "mode", + "value": "words", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["choo_choo"], + "key": "mode", + "value": "quote", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["choo_choo"], + "key": "mode", + "value": "custom", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["choo_choo"], + "key": "mode", + "value": "zen", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["choo_choo"], + "key": "words", + "value": 0, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["choo_choo"], + "key": "words", + "value": 10, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["choo_choo"], + "key": "time", + "value": 0, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["choo_choo"], + "key": "time", + "value": 15, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["choo_choo"], + "key": "punctuation", + "value": true, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["choo_choo"], + "key": "punctuation", + "value": false, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["choo_choo"], + "key": "numbers", + "value": true, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["choo_choo"], + "key": "numbers", + "value": false, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["choo_choo"], + "key": "highlightMode", + "value": "word", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["choo_choo"], + "key": "highlightMode", + "value": "letter", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["choo_choo"], + "key": "highlightMode", + "value": "off", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["arrows"], + "key": "mode", + "value": "time", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["arrows"], + "key": "mode", + "value": "words", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["arrows"], + "key": "mode", + "value": "quote", + "forced": { + "result": true + }, + "canSet": false + }, + { + "names": ["arrows"], + "key": "mode", + "value": "custom", + "forced": { + "result": true + }, + "canSet": false + }, + { + "names": ["arrows"], + "key": "mode", + "value": "zen", + "forced": { + "result": true + }, + "canSet": false + }, + { + "names": ["arrows"], + "key": "words", + "value": 0, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["arrows"], + "key": "words", + "value": 10, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["arrows"], + "key": "time", + "value": 0, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["arrows"], + "key": "time", + "value": 15, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["arrows"], + "key": "punctuation", + "value": true, + "forced": { + "result": false, + "forcedConfigs": [false] + }, + "canSet": false + }, + { + "names": ["arrows"], + "key": "punctuation", + "value": false, + "forced": { + "result": true, + "forcedConfigs": [false] + }, + "canSet": true + }, + { + "names": ["arrows"], + "key": "numbers", + "value": true, + "forced": { + "result": false, + "forcedConfigs": [false] + }, + "canSet": false + }, + { + "names": ["arrows"], + "key": "numbers", + "value": false, + "forced": { + "result": true, + "forcedConfigs": [false] + }, + "canSet": true + }, + { + "names": ["arrows"], + "key": "highlightMode", + "value": "word", + "forced": { + "result": false, + "forcedConfigs": ["letter", "off"] + }, + "canSet": false + }, + { + "names": ["arrows"], + "key": "highlightMode", + "value": "letter", + "forced": { + "result": true, + "forcedConfigs": ["letter", "off"] + }, + "canSet": true + }, + { + "names": ["arrows"], + "key": "highlightMode", + "value": "off", + "forced": { + "result": true, + "forcedConfigs": ["letter", "off"] + }, + "canSet": true + }, + { + "names": ["rAnDoMcAsE"], + "key": "mode", + "value": "time", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["rAnDoMcAsE"], + "key": "mode", + "value": "words", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["rAnDoMcAsE"], + "key": "mode", + "value": "quote", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["rAnDoMcAsE"], + "key": "mode", + "value": "custom", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["rAnDoMcAsE"], + "key": "mode", + "value": "zen", + "forced": { + "result": true + }, + "canSet": false + }, + { + "names": ["rAnDoMcAsE"], + "key": "words", + "value": 0, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["rAnDoMcAsE"], + "key": "words", + "value": 10, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["rAnDoMcAsE"], + "key": "time", + "value": 0, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["rAnDoMcAsE"], + "key": "time", + "value": 15, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["rAnDoMcAsE"], + "key": "punctuation", + "value": true, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["rAnDoMcAsE"], + "key": "punctuation", + "value": false, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["rAnDoMcAsE"], + "key": "numbers", + "value": true, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["rAnDoMcAsE"], + "key": "numbers", + "value": false, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["rAnDoMcAsE"], + "key": "highlightMode", + "value": "word", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["rAnDoMcAsE"], + "key": "highlightMode", + "value": "letter", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["rAnDoMcAsE"], + "key": "highlightMode", + "value": "off", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["sPoNgEcAsE"], + "key": "mode", + "value": "time", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["sPoNgEcAsE"], + "key": "mode", + "value": "words", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["sPoNgEcAsE"], + "key": "mode", + "value": "quote", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["sPoNgEcAsE"], + "key": "mode", + "value": "custom", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["sPoNgEcAsE"], + "key": "mode", + "value": "zen", + "forced": { + "result": true + }, + "canSet": false + }, + { + "names": ["sPoNgEcAsE"], + "key": "words", + "value": 0, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["sPoNgEcAsE"], + "key": "words", + "value": 10, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["sPoNgEcAsE"], + "key": "time", + "value": 0, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["sPoNgEcAsE"], + "key": "time", + "value": 15, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["sPoNgEcAsE"], + "key": "punctuation", + "value": true, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["sPoNgEcAsE"], + "key": "punctuation", + "value": false, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["sPoNgEcAsE"], + "key": "numbers", + "value": true, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["sPoNgEcAsE"], + "key": "numbers", + "value": false, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["sPoNgEcAsE"], + "key": "highlightMode", + "value": "word", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["sPoNgEcAsE"], + "key": "highlightMode", + "value": "letter", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["sPoNgEcAsE"], + "key": "highlightMode", + "value": "off", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["capitals"], + "key": "mode", + "value": "time", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["capitals"], + "key": "mode", + "value": "words", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["capitals"], + "key": "mode", + "value": "quote", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["capitals"], + "key": "mode", + "value": "custom", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["capitals"], + "key": "mode", + "value": "zen", + "forced": { + "result": true + }, + "canSet": false + }, + { + "names": ["capitals"], + "key": "words", + "value": 0, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["capitals"], + "key": "words", + "value": 10, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["capitals"], + "key": "time", + "value": 0, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["capitals"], + "key": "time", + "value": 15, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["capitals"], + "key": "punctuation", + "value": true, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["capitals"], + "key": "punctuation", + "value": false, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["capitals"], + "key": "numbers", + "value": true, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["capitals"], + "key": "numbers", + "value": false, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["capitals"], + "key": "highlightMode", + "value": "word", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["capitals"], + "key": "highlightMode", + "value": "letter", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["capitals"], + "key": "highlightMode", + "value": "off", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["layout_mirror"], + "key": "mode", + "value": "time", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["layout_mirror"], + "key": "mode", + "value": "words", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["layout_mirror"], + "key": "mode", + "value": "quote", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["layout_mirror"], + "key": "mode", + "value": "custom", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["layout_mirror"], + "key": "mode", + "value": "zen", + "forced": { + "result": true + }, + "canSet": false + }, + { + "names": ["layout_mirror"], + "key": "words", + "value": 0, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["layout_mirror"], + "key": "words", + "value": 10, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["layout_mirror"], + "key": "time", + "value": 0, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["layout_mirror"], + "key": "time", + "value": 15, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["layout_mirror"], + "key": "punctuation", + "value": true, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["layout_mirror"], + "key": "punctuation", + "value": false, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["layout_mirror"], + "key": "numbers", + "value": true, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["layout_mirror"], + "key": "numbers", + "value": false, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["layout_mirror"], + "key": "highlightMode", + "value": "word", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["layout_mirror"], + "key": "highlightMode", + "value": "letter", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["layout_mirror"], + "key": "highlightMode", + "value": "off", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["layoutfluid"], + "key": "mode", + "value": "time", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["layoutfluid"], + "key": "mode", + "value": "words", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["layoutfluid"], + "key": "mode", + "value": "quote", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["layoutfluid"], + "key": "mode", + "value": "custom", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["layoutfluid"], + "key": "mode", + "value": "zen", + "forced": { + "result": true + }, + "canSet": false + }, + { + "names": ["layoutfluid"], + "key": "words", + "value": 0, + "forced": { + "result": false, + "forcedConfigs": [10] + }, + "canSet": false + }, + { + "names": ["layoutfluid"], + "key": "words", + "value": 10, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["layoutfluid"], + "key": "time", + "value": 0, + "forced": { + "result": false, + "forcedConfigs": [15] + }, + "canSet": false + }, + { + "names": ["layoutfluid"], + "key": "time", + "value": 15, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["layoutfluid"], + "key": "punctuation", + "value": true, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["layoutfluid"], + "key": "punctuation", + "value": false, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["layoutfluid"], + "key": "numbers", + "value": true, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["layoutfluid"], + "key": "numbers", + "value": false, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["layoutfluid"], + "key": "highlightMode", + "value": "word", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["layoutfluid"], + "key": "highlightMode", + "value": "letter", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["layoutfluid"], + "key": "highlightMode", + "value": "off", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["earthquake"], + "key": "mode", + "value": "time", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["earthquake"], + "key": "mode", + "value": "words", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["earthquake"], + "key": "mode", + "value": "quote", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["earthquake"], + "key": "mode", + "value": "custom", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["earthquake"], + "key": "mode", + "value": "zen", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["earthquake"], + "key": "words", + "value": 0, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["earthquake"], + "key": "words", + "value": 10, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["earthquake"], + "key": "time", + "value": 0, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["earthquake"], + "key": "time", + "value": 15, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["earthquake"], + "key": "punctuation", + "value": true, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["earthquake"], + "key": "punctuation", + "value": false, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["earthquake"], + "key": "numbers", + "value": true, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["earthquake"], + "key": "numbers", + "value": false, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["earthquake"], + "key": "highlightMode", + "value": "word", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["earthquake"], + "key": "highlightMode", + "value": "letter", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["earthquake"], + "key": "highlightMode", + "value": "off", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["space_balls"], + "key": "mode", + "value": "time", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["space_balls"], + "key": "mode", + "value": "words", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["space_balls"], + "key": "mode", + "value": "quote", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["space_balls"], + "key": "mode", + "value": "custom", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["space_balls"], + "key": "mode", + "value": "zen", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["space_balls"], + "key": "words", + "value": 0, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["space_balls"], + "key": "words", + "value": 10, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["space_balls"], + "key": "time", + "value": 0, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["space_balls"], + "key": "time", + "value": 15, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["space_balls"], + "key": "punctuation", + "value": true, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["space_balls"], + "key": "punctuation", + "value": false, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["space_balls"], + "key": "numbers", + "value": true, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["space_balls"], + "key": "numbers", + "value": false, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["space_balls"], + "key": "highlightMode", + "value": "word", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["space_balls"], + "key": "highlightMode", + "value": "letter", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["space_balls"], + "key": "highlightMode", + "value": "off", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["gibberish"], + "key": "mode", + "value": "time", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["gibberish"], + "key": "mode", + "value": "words", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["gibberish"], + "key": "mode", + "value": "quote", + "forced": { + "result": true + }, + "canSet": false + }, + { + "names": ["gibberish"], + "key": "mode", + "value": "custom", + "forced": { + "result": true + }, + "canSet": false + }, + { + "names": ["gibberish"], + "key": "mode", + "value": "zen", + "forced": { + "result": true + }, + "canSet": false + }, + { + "names": ["gibberish"], + "key": "words", + "value": 0, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["gibberish"], + "key": "words", + "value": 10, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["gibberish"], + "key": "time", + "value": 0, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["gibberish"], + "key": "time", + "value": 15, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["gibberish"], + "key": "punctuation", + "value": true, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["gibberish"], + "key": "punctuation", + "value": false, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["gibberish"], + "key": "numbers", + "value": true, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["gibberish"], + "key": "numbers", + "value": false, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["gibberish"], + "key": "highlightMode", + "value": "word", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["gibberish"], + "key": "highlightMode", + "value": "letter", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["gibberish"], + "key": "highlightMode", + "value": "off", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["ascii"], + "key": "mode", + "value": "time", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["ascii"], + "key": "mode", + "value": "words", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["ascii"], + "key": "mode", + "value": "quote", + "forced": { + "result": true + }, + "canSet": false + }, + { + "names": ["ascii"], + "key": "mode", + "value": "custom", + "forced": { + "result": true + }, + "canSet": false + }, + { + "names": ["ascii"], + "key": "mode", + "value": "zen", + "forced": { + "result": true + }, + "canSet": false + }, + { + "names": ["ascii"], + "key": "words", + "value": 0, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["ascii"], + "key": "words", + "value": 10, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["ascii"], + "key": "time", + "value": 0, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["ascii"], + "key": "time", + "value": 15, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["ascii"], + "key": "punctuation", + "value": true, + "forced": { + "result": false, + "forcedConfigs": [false] + }, + "canSet": false + }, + { + "names": ["ascii"], + "key": "punctuation", + "value": false, + "forced": { + "result": true, + "forcedConfigs": [false] + }, + "canSet": true + }, + { + "names": ["ascii"], + "key": "numbers", + "value": true, + "forced": { + "result": false, + "forcedConfigs": [false] + }, + "canSet": false + }, + { + "names": ["ascii"], + "key": "numbers", + "value": false, + "forced": { + "result": true, + "forcedConfigs": [false] + }, + "canSet": true + }, + { + "names": ["ascii"], + "key": "highlightMode", + "value": "word", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["ascii"], + "key": "highlightMode", + "value": "letter", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["ascii"], + "key": "highlightMode", + "value": "off", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["specials"], + "key": "mode", + "value": "time", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["specials"], + "key": "mode", + "value": "words", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["specials"], + "key": "mode", + "value": "quote", + "forced": { + "result": true + }, + "canSet": false + }, + { + "names": ["specials"], + "key": "mode", + "value": "custom", + "forced": { + "result": true + }, + "canSet": false + }, + { + "names": ["specials"], + "key": "mode", + "value": "zen", + "forced": { + "result": true + }, + "canSet": false + }, + { + "names": ["specials"], + "key": "words", + "value": 0, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["specials"], + "key": "words", + "value": 10, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["specials"], + "key": "time", + "value": 0, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["specials"], + "key": "time", + "value": 15, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["specials"], + "key": "punctuation", + "value": true, + "forced": { + "result": false, + "forcedConfigs": [false] + }, + "canSet": false + }, + { + "names": ["specials"], + "key": "punctuation", + "value": false, + "forced": { + "result": true, + "forcedConfigs": [false] + }, + "canSet": true + }, + { + "names": ["specials"], + "key": "numbers", + "value": true, + "forced": { + "result": false, + "forcedConfigs": [false] + }, + "canSet": false + }, + { + "names": ["specials"], + "key": "numbers", + "value": false, + "forced": { + "result": true, + "forcedConfigs": [false] + }, + "canSet": true + }, + { + "names": ["specials"], + "key": "highlightMode", + "value": "word", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["specials"], + "key": "highlightMode", + "value": "letter", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["specials"], + "key": "highlightMode", + "value": "off", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["plus_zero"], + "key": "mode", + "value": "time", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["plus_zero"], + "key": "mode", + "value": "words", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["plus_zero"], + "key": "mode", + "value": "quote", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["plus_zero"], + "key": "mode", + "value": "custom", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["plus_zero"], + "key": "mode", + "value": "zen", + "forced": { + "result": true + }, + "canSet": false + }, + { + "names": ["plus_zero"], + "key": "words", + "value": 0, + "forced": { + "result": false, + "forcedConfigs": [10] + }, + "canSet": false + }, + { + "names": ["plus_zero"], + "key": "words", + "value": 10, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["plus_zero"], + "key": "time", + "value": 0, + "forced": { + "result": false, + "forcedConfigs": [15] + }, + "canSet": false + }, + { + "names": ["plus_zero"], + "key": "time", + "value": 15, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["plus_zero"], + "key": "punctuation", + "value": true, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["plus_zero"], + "key": "punctuation", + "value": false, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["plus_zero"], + "key": "numbers", + "value": true, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["plus_zero"], + "key": "numbers", + "value": false, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["plus_zero"], + "key": "highlightMode", + "value": "word", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["plus_zero"], + "key": "highlightMode", + "value": "letter", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["plus_zero"], + "key": "highlightMode", + "value": "off", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["plus_one"], + "key": "mode", + "value": "time", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["plus_one"], + "key": "mode", + "value": "words", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["plus_one"], + "key": "mode", + "value": "quote", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["plus_one"], + "key": "mode", + "value": "custom", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["plus_one"], + "key": "mode", + "value": "zen", + "forced": { + "result": true + }, + "canSet": false + }, + { + "names": ["plus_one"], + "key": "words", + "value": 0, + "forced": { + "result": false, + "forcedConfigs": [10] + }, + "canSet": false + }, + { + "names": ["plus_one"], + "key": "words", + "value": 10, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["plus_one"], + "key": "time", + "value": 0, + "forced": { + "result": false, + "forcedConfigs": [15] + }, + "canSet": false + }, + { + "names": ["plus_one"], + "key": "time", + "value": 15, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["plus_one"], + "key": "punctuation", + "value": true, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["plus_one"], + "key": "punctuation", + "value": false, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["plus_one"], + "key": "numbers", + "value": true, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["plus_one"], + "key": "numbers", + "value": false, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["plus_one"], + "key": "highlightMode", + "value": "word", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["plus_one"], + "key": "highlightMode", + "value": "letter", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["plus_one"], + "key": "highlightMode", + "value": "off", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["plus_two"], + "key": "mode", + "value": "time", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["plus_two"], + "key": "mode", + "value": "words", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["plus_two"], + "key": "mode", + "value": "quote", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["plus_two"], + "key": "mode", + "value": "custom", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["plus_two"], + "key": "mode", + "value": "zen", + "forced": { + "result": true + }, + "canSet": false + }, + { + "names": ["plus_two"], + "key": "words", + "value": 0, + "forced": { + "result": false, + "forcedConfigs": [10] + }, + "canSet": false + }, + { + "names": ["plus_two"], + "key": "words", + "value": 10, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["plus_two"], + "key": "time", + "value": 0, + "forced": { + "result": false, + "forcedConfigs": [15] + }, + "canSet": false + }, + { + "names": ["plus_two"], + "key": "time", + "value": 15, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["plus_two"], + "key": "punctuation", + "value": true, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["plus_two"], + "key": "punctuation", + "value": false, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["plus_two"], + "key": "numbers", + "value": true, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["plus_two"], + "key": "numbers", + "value": false, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["plus_two"], + "key": "highlightMode", + "value": "word", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["plus_two"], + "key": "highlightMode", + "value": "letter", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["plus_two"], + "key": "highlightMode", + "value": "off", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["plus_three"], + "key": "mode", + "value": "time", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["plus_three"], + "key": "mode", + "value": "words", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["plus_three"], + "key": "mode", + "value": "quote", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["plus_three"], + "key": "mode", + "value": "custom", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["plus_three"], + "key": "mode", + "value": "zen", + "forced": { + "result": true + }, + "canSet": false + }, + { + "names": ["plus_three"], + "key": "words", + "value": 0, + "forced": { + "result": false, + "forcedConfigs": [10] + }, + "canSet": false + }, + { + "names": ["plus_three"], + "key": "words", + "value": 10, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["plus_three"], + "key": "time", + "value": 0, + "forced": { + "result": false, + "forcedConfigs": [15] + }, + "canSet": false + }, + { + "names": ["plus_three"], + "key": "time", + "value": 15, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["plus_three"], + "key": "punctuation", + "value": true, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["plus_three"], + "key": "punctuation", + "value": false, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["plus_three"], + "key": "numbers", + "value": true, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["plus_three"], + "key": "numbers", + "value": false, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["plus_three"], + "key": "highlightMode", + "value": "word", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["plus_three"], + "key": "highlightMode", + "value": "letter", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["plus_three"], + "key": "highlightMode", + "value": "off", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["read_ahead_easy"], + "key": "mode", + "value": "time", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["read_ahead_easy"], + "key": "mode", + "value": "words", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["read_ahead_easy"], + "key": "mode", + "value": "quote", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["read_ahead_easy"], + "key": "mode", + "value": "custom", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["read_ahead_easy"], + "key": "mode", + "value": "zen", + "forced": { + "result": true + }, + "canSet": false + }, + { + "names": ["read_ahead_easy"], + "key": "words", + "value": 0, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["read_ahead_easy"], + "key": "words", + "value": 10, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["read_ahead_easy"], + "key": "time", + "value": 0, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["read_ahead_easy"], + "key": "time", + "value": 15, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["read_ahead_easy"], + "key": "punctuation", + "value": true, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["read_ahead_easy"], + "key": "punctuation", + "value": false, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["read_ahead_easy"], + "key": "numbers", + "value": true, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["read_ahead_easy"], + "key": "numbers", + "value": false, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["read_ahead_easy"], + "key": "highlightMode", + "value": "word", + "forced": { + "result": false, + "forcedConfigs": ["letter", "off"] + }, + "canSet": false + }, + { + "names": ["read_ahead_easy"], + "key": "highlightMode", + "value": "letter", + "forced": { + "result": true, + "forcedConfigs": ["letter", "off"] + }, + "canSet": true + }, + { + "names": ["read_ahead_easy"], + "key": "highlightMode", + "value": "off", + "forced": { + "result": true, + "forcedConfigs": ["letter", "off"] + }, + "canSet": true + }, + { + "names": ["read_ahead"], + "key": "mode", + "value": "time", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["read_ahead"], + "key": "mode", + "value": "words", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["read_ahead"], + "key": "mode", + "value": "quote", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["read_ahead"], + "key": "mode", + "value": "custom", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["read_ahead"], + "key": "mode", + "value": "zen", + "forced": { + "result": true + }, + "canSet": false + }, + { + "names": ["read_ahead"], + "key": "words", + "value": 0, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["read_ahead"], + "key": "words", + "value": 10, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["read_ahead"], + "key": "time", + "value": 0, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["read_ahead"], + "key": "time", + "value": 15, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["read_ahead"], + "key": "punctuation", + "value": true, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["read_ahead"], + "key": "punctuation", + "value": false, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["read_ahead"], + "key": "numbers", + "value": true, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["read_ahead"], + "key": "numbers", + "value": false, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["read_ahead"], + "key": "highlightMode", + "value": "word", + "forced": { + "result": false, + "forcedConfigs": ["letter", "off"] + }, + "canSet": false + }, + { + "names": ["read_ahead"], + "key": "highlightMode", + "value": "letter", + "forced": { + "result": true, + "forcedConfigs": ["letter", "off"] + }, + "canSet": true + }, + { + "names": ["read_ahead"], + "key": "highlightMode", + "value": "off", + "forced": { + "result": true, + "forcedConfigs": ["letter", "off"] + }, + "canSet": true + }, + { + "names": ["read_ahead_hard"], + "key": "mode", + "value": "time", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["read_ahead_hard"], + "key": "mode", + "value": "words", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["read_ahead_hard"], + "key": "mode", + "value": "quote", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["read_ahead_hard"], + "key": "mode", + "value": "custom", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["read_ahead_hard"], + "key": "mode", + "value": "zen", + "forced": { + "result": true + }, + "canSet": false + }, + { + "names": ["read_ahead_hard"], + "key": "words", + "value": 0, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["read_ahead_hard"], + "key": "words", + "value": 10, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["read_ahead_hard"], + "key": "time", + "value": 0, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["read_ahead_hard"], + "key": "time", + "value": 15, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["read_ahead_hard"], + "key": "punctuation", + "value": true, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["read_ahead_hard"], + "key": "punctuation", + "value": false, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["read_ahead_hard"], + "key": "numbers", + "value": true, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["read_ahead_hard"], + "key": "numbers", + "value": false, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["read_ahead_hard"], + "key": "highlightMode", + "value": "word", + "forced": { + "result": false, + "forcedConfigs": ["letter", "off"] + }, + "canSet": false + }, + { + "names": ["read_ahead_hard"], + "key": "highlightMode", + "value": "letter", + "forced": { + "result": true, + "forcedConfigs": ["letter", "off"] + }, + "canSet": true + }, + { + "names": ["read_ahead_hard"], + "key": "highlightMode", + "value": "off", + "forced": { + "result": true, + "forcedConfigs": ["letter", "off"] + }, + "canSet": true + }, + { + "names": ["memory"], + "key": "mode", + "value": "time", + "forced": { + "result": false, + "forcedConfigs": ["words", "quote", "custom"] + }, + "canSet": false + }, + { + "names": ["memory"], + "key": "mode", + "value": "words", + "forced": { + "result": true, + "forcedConfigs": ["words", "quote", "custom"] + }, + "canSet": true + }, + { + "names": ["memory"], + "key": "mode", + "value": "quote", + "forced": { + "result": true, + "forcedConfigs": ["words", "quote", "custom"] + }, + "canSet": true + }, + { + "names": ["memory"], + "key": "mode", + "value": "custom", + "forced": { + "result": true, + "forcedConfigs": ["words", "quote", "custom"] + }, + "canSet": true + }, + { + "names": ["memory"], + "key": "mode", + "value": "zen", + "forced": { + "result": false, + "forcedConfigs": ["words", "quote", "custom"] + }, + "canSet": false + }, + { + "names": ["memory"], + "key": "words", + "value": 0, + "forced": { + "result": false, + "forcedConfigs": [10] + }, + "canSet": false + }, + { + "names": ["memory"], + "key": "words", + "value": 10, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["memory"], + "key": "time", + "value": 0, + "forced": { + "result": false, + "forcedConfigs": [15] + }, + "canSet": false + }, + { + "names": ["memory"], + "key": "time", + "value": 15, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["memory"], + "key": "punctuation", + "value": true, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["memory"], + "key": "punctuation", + "value": false, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["memory"], + "key": "numbers", + "value": true, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["memory"], + "key": "numbers", + "value": false, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["memory"], + "key": "highlightMode", + "value": "word", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["memory"], + "key": "highlightMode", + "value": "letter", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["memory"], + "key": "highlightMode", + "value": "off", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["nospace"], + "key": "mode", + "value": "time", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["nospace"], + "key": "mode", + "value": "words", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["nospace"], + "key": "mode", + "value": "quote", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["nospace"], + "key": "mode", + "value": "custom", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["nospace"], + "key": "mode", + "value": "zen", + "forced": { + "result": true + }, + "canSet": false + }, + { + "names": ["nospace"], + "key": "words", + "value": 0, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["nospace"], + "key": "words", + "value": 10, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["nospace"], + "key": "time", + "value": 0, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["nospace"], + "key": "time", + "value": 15, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["nospace"], + "key": "punctuation", + "value": true, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["nospace"], + "key": "punctuation", + "value": false, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["nospace"], + "key": "numbers", + "value": true, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["nospace"], + "key": "numbers", + "value": false, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["nospace"], + "key": "highlightMode", + "value": "word", + "forced": { + "result": false, + "forcedConfigs": ["letter", "off"] + }, + "canSet": false + }, + { + "names": ["nospace"], + "key": "highlightMode", + "value": "letter", + "forced": { + "result": true, + "forcedConfigs": ["letter", "off"] + }, + "canSet": true + }, + { + "names": ["nospace"], + "key": "highlightMode", + "value": "off", + "forced": { + "result": true, + "forcedConfigs": ["letter", "off"] + }, + "canSet": true + }, + { + "names": ["poetry"], + "key": "mode", + "value": "time", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["poetry"], + "key": "mode", + "value": "words", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["poetry"], + "key": "mode", + "value": "quote", + "forced": { + "result": true + }, + "canSet": false + }, + { + "names": ["poetry"], + "key": "mode", + "value": "custom", + "forced": { + "result": true + }, + "canSet": false + }, + { + "names": ["poetry"], + "key": "mode", + "value": "zen", + "forced": { + "result": true + }, + "canSet": false + }, + { + "names": ["poetry"], + "key": "words", + "value": 0, + "forced": { + "result": false, + "forcedConfigs": [10] + }, + "canSet": false + }, + { + "names": ["poetry"], + "key": "words", + "value": 10, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["poetry"], + "key": "time", + "value": 0, + "forced": { + "result": false, + "forcedConfigs": [15] + }, + "canSet": false + }, + { + "names": ["poetry"], + "key": "time", + "value": 15, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["poetry"], + "key": "punctuation", + "value": true, + "forced": { + "result": false, + "forcedConfigs": [false] + }, + "canSet": false + }, + { + "names": ["poetry"], + "key": "punctuation", + "value": false, + "forced": { + "result": true, + "forcedConfigs": [false] + }, + "canSet": true + }, + { + "names": ["poetry"], + "key": "numbers", + "value": true, + "forced": { + "result": false, + "forcedConfigs": [false] + }, + "canSet": false + }, + { + "names": ["poetry"], + "key": "numbers", + "value": false, + "forced": { + "result": true, + "forcedConfigs": [false] + }, + "canSet": true + }, + { + "names": ["poetry"], + "key": "highlightMode", + "value": "word", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["poetry"], + "key": "highlightMode", + "value": "letter", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["poetry"], + "key": "highlightMode", + "value": "off", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["wikipedia"], + "key": "mode", + "value": "time", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["wikipedia"], + "key": "mode", + "value": "words", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["wikipedia"], + "key": "mode", + "value": "quote", + "forced": { + "result": true + }, + "canSet": false + }, + { + "names": ["wikipedia"], + "key": "mode", + "value": "custom", + "forced": { + "result": true + }, + "canSet": false + }, + { + "names": ["wikipedia"], + "key": "mode", + "value": "zen", + "forced": { + "result": true + }, + "canSet": false + }, + { + "names": ["wikipedia"], + "key": "words", + "value": 0, + "forced": { + "result": false, + "forcedConfigs": [10] + }, + "canSet": false + }, + { + "names": ["wikipedia"], + "key": "words", + "value": 10, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["wikipedia"], + "key": "time", + "value": 0, + "forced": { + "result": false, + "forcedConfigs": [15] + }, + "canSet": false + }, + { + "names": ["wikipedia"], + "key": "time", + "value": 15, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["wikipedia"], + "key": "punctuation", + "value": true, + "forced": { + "result": false, + "forcedConfigs": [false] + }, + "canSet": false + }, + { + "names": ["wikipedia"], + "key": "punctuation", + "value": false, + "forced": { + "result": true, + "forcedConfigs": [false] + }, + "canSet": true + }, + { + "names": ["wikipedia"], + "key": "numbers", + "value": true, + "forced": { + "result": false, + "forcedConfigs": [false] + }, + "canSet": false + }, + { + "names": ["wikipedia"], + "key": "numbers", + "value": false, + "forced": { + "result": true, + "forcedConfigs": [false] + }, + "canSet": true + }, + { + "names": ["wikipedia"], + "key": "highlightMode", + "value": "word", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["wikipedia"], + "key": "highlightMode", + "value": "letter", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["wikipedia"], + "key": "highlightMode", + "value": "off", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["weakspot"], + "key": "mode", + "value": "time", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["weakspot"], + "key": "mode", + "value": "words", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["weakspot"], + "key": "mode", + "value": "quote", + "forced": { + "result": true + }, + "canSet": false + }, + { + "names": ["weakspot"], + "key": "mode", + "value": "custom", + "forced": { + "result": true + }, + "canSet": false + }, + { + "names": ["weakspot"], + "key": "mode", + "value": "zen", + "forced": { + "result": true + }, + "canSet": false + }, + { + "names": ["weakspot"], + "key": "words", + "value": 0, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["weakspot"], + "key": "words", + "value": 10, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["weakspot"], + "key": "time", + "value": 0, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["weakspot"], + "key": "time", + "value": 15, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["weakspot"], + "key": "punctuation", + "value": true, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["weakspot"], + "key": "punctuation", + "value": false, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["weakspot"], + "key": "numbers", + "value": true, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["weakspot"], + "key": "numbers", + "value": false, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["weakspot"], + "key": "highlightMode", + "value": "word", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["weakspot"], + "key": "highlightMode", + "value": "letter", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["weakspot"], + "key": "highlightMode", + "value": "off", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["pseudolang"], + "key": "mode", + "value": "time", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["pseudolang"], + "key": "mode", + "value": "words", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["pseudolang"], + "key": "mode", + "value": "quote", + "forced": { + "result": true + }, + "canSet": false + }, + { + "names": ["pseudolang"], + "key": "mode", + "value": "custom", + "forced": { + "result": true + }, + "canSet": false + }, + { + "names": ["pseudolang"], + "key": "mode", + "value": "zen", + "forced": { + "result": true + }, + "canSet": false + }, + { + "names": ["pseudolang"], + "key": "words", + "value": 0, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["pseudolang"], + "key": "words", + "value": 10, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["pseudolang"], + "key": "time", + "value": 0, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["pseudolang"], + "key": "time", + "value": 15, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["pseudolang"], + "key": "punctuation", + "value": true, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["pseudolang"], + "key": "punctuation", + "value": false, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["pseudolang"], + "key": "numbers", + "value": true, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["pseudolang"], + "key": "numbers", + "value": false, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["pseudolang"], + "key": "highlightMode", + "value": "word", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["pseudolang"], + "key": "highlightMode", + "value": "letter", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["pseudolang"], + "key": "highlightMode", + "value": "off", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["IPv4"], + "key": "mode", + "value": "time", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["IPv4"], + "key": "mode", + "value": "words", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["IPv4"], + "key": "mode", + "value": "quote", + "forced": { + "result": true + }, + "canSet": false + }, + { + "names": ["IPv4"], + "key": "mode", + "value": "custom", + "forced": { + "result": true + }, + "canSet": false + }, + { + "names": ["IPv4"], + "key": "mode", + "value": "zen", + "forced": { + "result": true + }, + "canSet": false + }, + { + "names": ["IPv4"], + "key": "words", + "value": 0, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["IPv4"], + "key": "words", + "value": 10, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["IPv4"], + "key": "time", + "value": 0, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["IPv4"], + "key": "time", + "value": 15, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["IPv4"], + "key": "punctuation", + "value": true, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["IPv4"], + "key": "punctuation", + "value": false, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["IPv4"], + "key": "numbers", + "value": true, + "forced": { + "result": false, + "forcedConfigs": [false] + }, + "canSet": false + }, + { + "names": ["IPv4"], + "key": "numbers", + "value": false, + "forced": { + "result": true, + "forcedConfigs": [false] + }, + "canSet": true + }, + { + "names": ["IPv4"], + "key": "highlightMode", + "value": "word", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["IPv4"], + "key": "highlightMode", + "value": "letter", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["IPv4"], + "key": "highlightMode", + "value": "off", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["IPv6"], + "key": "mode", + "value": "time", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["IPv6"], + "key": "mode", + "value": "words", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["IPv6"], + "key": "mode", + "value": "quote", + "forced": { + "result": true + }, + "canSet": false + }, + { + "names": ["IPv6"], + "key": "mode", + "value": "custom", + "forced": { + "result": true + }, + "canSet": false + }, + { + "names": ["IPv6"], + "key": "mode", + "value": "zen", + "forced": { + "result": true + }, + "canSet": false + }, + { + "names": ["IPv6"], + "key": "words", + "value": 0, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["IPv6"], + "key": "words", + "value": 10, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["IPv6"], + "key": "time", + "value": 0, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["IPv6"], + "key": "time", + "value": 15, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["IPv6"], + "key": "punctuation", + "value": true, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["IPv6"], + "key": "punctuation", + "value": false, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["IPv6"], + "key": "numbers", + "value": true, + "forced": { + "result": false, + "forcedConfigs": [false] + }, + "canSet": false + }, + { + "names": ["IPv6"], + "key": "numbers", + "value": false, + "forced": { + "result": true, + "forcedConfigs": [false] + }, + "canSet": true + }, + { + "names": ["IPv6"], + "key": "highlightMode", + "value": "word", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["IPv6"], + "key": "highlightMode", + "value": "letter", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["IPv6"], + "key": "highlightMode", + "value": "off", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["binary"], + "key": "mode", + "value": "time", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["binary"], + "key": "mode", + "value": "words", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["binary"], + "key": "mode", + "value": "quote", + "forced": { + "result": true + }, + "canSet": false + }, + { + "names": ["binary"], + "key": "mode", + "value": "custom", + "forced": { + "result": true + }, + "canSet": false + }, + { + "names": ["binary"], + "key": "mode", + "value": "zen", + "forced": { + "result": true + }, + "canSet": false + }, + { + "names": ["binary"], + "key": "words", + "value": 0, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["binary"], + "key": "words", + "value": 10, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["binary"], + "key": "time", + "value": 0, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["binary"], + "key": "time", + "value": 15, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["binary"], + "key": "punctuation", + "value": true, + "forced": { + "result": false, + "forcedConfigs": [false] + }, + "canSet": false + }, + { + "names": ["binary"], + "key": "punctuation", + "value": false, + "forced": { + "result": true, + "forcedConfigs": [false] + }, + "canSet": true + }, + { + "names": ["binary"], + "key": "numbers", + "value": true, + "forced": { + "result": false, + "forcedConfigs": [false] + }, + "canSet": false + }, + { + "names": ["binary"], + "key": "numbers", + "value": false, + "forced": { + "result": true, + "forcedConfigs": [false] + }, + "canSet": true + }, + { + "names": ["binary"], + "key": "highlightMode", + "value": "word", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["binary"], + "key": "highlightMode", + "value": "letter", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["binary"], + "key": "highlightMode", + "value": "off", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["hexadecimal"], + "key": "mode", + "value": "time", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["hexadecimal"], + "key": "mode", + "value": "words", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["hexadecimal"], + "key": "mode", + "value": "quote", + "forced": { + "result": true + }, + "canSet": false + }, + { + "names": ["hexadecimal"], + "key": "mode", + "value": "custom", + "forced": { + "result": true + }, + "canSet": false + }, + { + "names": ["hexadecimal"], + "key": "mode", + "value": "zen", + "forced": { + "result": true + }, + "canSet": false + }, + { + "names": ["hexadecimal"], + "key": "words", + "value": 0, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["hexadecimal"], + "key": "words", + "value": 10, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["hexadecimal"], + "key": "time", + "value": 0, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["hexadecimal"], + "key": "time", + "value": 15, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["hexadecimal"], + "key": "punctuation", + "value": true, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["hexadecimal"], + "key": "punctuation", + "value": false, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["hexadecimal"], + "key": "numbers", + "value": true, + "forced": { + "result": false, + "forcedConfigs": [false] + }, + "canSet": false + }, + { + "names": ["hexadecimal"], + "key": "numbers", + "value": false, + "forced": { + "result": true, + "forcedConfigs": [false] + }, + "canSet": true + }, + { + "names": ["hexadecimal"], + "key": "highlightMode", + "value": "word", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["hexadecimal"], + "key": "highlightMode", + "value": "letter", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["hexadecimal"], + "key": "highlightMode", + "value": "off", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["zipf"], + "key": "mode", + "value": "time", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["zipf"], + "key": "mode", + "value": "words", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["zipf"], + "key": "mode", + "value": "quote", + "forced": { + "result": true + }, + "canSet": false + }, + { + "names": ["zipf"], + "key": "mode", + "value": "custom", + "forced": { + "result": true + }, + "canSet": false + }, + { + "names": ["zipf"], + "key": "mode", + "value": "zen", + "forced": { + "result": true + }, + "canSet": false + }, + { + "names": ["zipf"], + "key": "words", + "value": 0, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["zipf"], + "key": "words", + "value": 10, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["zipf"], + "key": "time", + "value": 0, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["zipf"], + "key": "time", + "value": 15, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["zipf"], + "key": "punctuation", + "value": true, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["zipf"], + "key": "punctuation", + "value": false, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["zipf"], + "key": "numbers", + "value": true, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["zipf"], + "key": "numbers", + "value": false, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["zipf"], + "key": "highlightMode", + "value": "word", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["zipf"], + "key": "highlightMode", + "value": "letter", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["zipf"], + "key": "highlightMode", + "value": "off", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["morse"], + "key": "mode", + "value": "time", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["morse"], + "key": "mode", + "value": "words", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["morse"], + "key": "mode", + "value": "quote", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["morse"], + "key": "mode", + "value": "custom", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["morse"], + "key": "mode", + "value": "zen", + "forced": { + "result": true + }, + "canSet": false + }, + { + "names": ["morse"], + "key": "words", + "value": 0, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["morse"], + "key": "words", + "value": 10, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["morse"], + "key": "time", + "value": 0, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["morse"], + "key": "time", + "value": 15, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["morse"], + "key": "punctuation", + "value": true, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["morse"], + "key": "punctuation", + "value": false, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["morse"], + "key": "numbers", + "value": true, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["morse"], + "key": "numbers", + "value": false, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["morse"], + "key": "highlightMode", + "value": "word", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["morse"], + "key": "highlightMode", + "value": "letter", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["morse"], + "key": "highlightMode", + "value": "off", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["crt"], + "key": "mode", + "value": "time", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["crt"], + "key": "mode", + "value": "words", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["crt"], + "key": "mode", + "value": "quote", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["crt"], + "key": "mode", + "value": "custom", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["crt"], + "key": "mode", + "value": "zen", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["crt"], + "key": "words", + "value": 0, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["crt"], + "key": "words", + "value": 10, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["crt"], + "key": "time", + "value": 0, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["crt"], + "key": "time", + "value": 15, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["crt"], + "key": "punctuation", + "value": true, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["crt"], + "key": "punctuation", + "value": false, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["crt"], + "key": "numbers", + "value": true, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["crt"], + "key": "numbers", + "value": false, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["crt"], + "key": "highlightMode", + "value": "word", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["crt"], + "key": "highlightMode", + "value": "letter", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["crt"], + "key": "highlightMode", + "value": "off", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["backwards"], + "key": "mode", + "value": "time", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["backwards"], + "key": "mode", + "value": "words", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["backwards"], + "key": "mode", + "value": "quote", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["backwards"], + "key": "mode", + "value": "custom", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["backwards"], + "key": "mode", + "value": "zen", + "forced": { + "result": true + }, + "canSet": false + }, + { + "names": ["backwards"], + "key": "words", + "value": 0, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["backwards"], + "key": "words", + "value": 10, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["backwards"], + "key": "time", + "value": 0, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["backwards"], + "key": "time", + "value": 15, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["backwards"], + "key": "punctuation", + "value": true, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["backwards"], + "key": "punctuation", + "value": false, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["backwards"], + "key": "numbers", + "value": true, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["backwards"], + "key": "numbers", + "value": false, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["backwards"], + "key": "highlightMode", + "value": "word", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["backwards"], + "key": "highlightMode", + "value": "letter", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["backwards"], + "key": "highlightMode", + "value": "off", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["ddoouubblleedd"], + "key": "mode", + "value": "time", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["ddoouubblleedd"], + "key": "mode", + "value": "words", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["ddoouubblleedd"], + "key": "mode", + "value": "quote", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["ddoouubblleedd"], + "key": "mode", + "value": "custom", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["ddoouubblleedd"], + "key": "mode", + "value": "zen", + "forced": { + "result": true + }, + "canSet": false + }, + { + "names": ["ddoouubblleedd"], + "key": "words", + "value": 0, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["ddoouubblleedd"], + "key": "words", + "value": 10, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["ddoouubblleedd"], + "key": "time", + "value": 0, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["ddoouubblleedd"], + "key": "time", + "value": 15, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["ddoouubblleedd"], + "key": "punctuation", + "value": true, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["ddoouubblleedd"], + "key": "punctuation", + "value": false, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["ddoouubblleedd"], + "key": "numbers", + "value": true, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["ddoouubblleedd"], + "key": "numbers", + "value": false, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["ddoouubblleedd"], + "key": "highlightMode", + "value": "word", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["ddoouubblleedd"], + "key": "highlightMode", + "value": "letter", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["ddoouubblleedd"], + "key": "highlightMode", + "value": "off", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["instant_messaging"], + "key": "mode", + "value": "time", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["instant_messaging"], + "key": "mode", + "value": "words", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["instant_messaging"], + "key": "mode", + "value": "quote", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["instant_messaging"], + "key": "mode", + "value": "custom", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["instant_messaging"], + "key": "mode", + "value": "zen", + "forced": { + "result": true + }, + "canSet": false + }, + { + "names": ["instant_messaging"], + "key": "words", + "value": 0, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["instant_messaging"], + "key": "words", + "value": 10, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["instant_messaging"], + "key": "time", + "value": 0, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["instant_messaging"], + "key": "time", + "value": 15, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["instant_messaging"], + "key": "punctuation", + "value": true, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["instant_messaging"], + "key": "punctuation", + "value": false, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["instant_messaging"], + "key": "numbers", + "value": true, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["instant_messaging"], + "key": "numbers", + "value": false, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["instant_messaging"], + "key": "highlightMode", + "value": "word", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["instant_messaging"], + "key": "highlightMode", + "value": "letter", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["instant_messaging"], + "key": "highlightMode", + "value": "off", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["underscore_spaces"], + "key": "mode", + "value": "time", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["underscore_spaces"], + "key": "mode", + "value": "words", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["underscore_spaces"], + "key": "mode", + "value": "quote", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["underscore_spaces"], + "key": "mode", + "value": "custom", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["underscore_spaces"], + "key": "mode", + "value": "zen", + "forced": { + "result": true + }, + "canSet": false + }, + { + "names": ["underscore_spaces"], + "key": "words", + "value": 0, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["underscore_spaces"], + "key": "words", + "value": 10, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["underscore_spaces"], + "key": "time", + "value": 0, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["underscore_spaces"], + "key": "time", + "value": 15, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["underscore_spaces"], + "key": "punctuation", + "value": true, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["underscore_spaces"], + "key": "punctuation", + "value": false, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["underscore_spaces"], + "key": "numbers", + "value": true, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["underscore_spaces"], + "key": "numbers", + "value": false, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["underscore_spaces"], + "key": "highlightMode", + "value": "word", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["underscore_spaces"], + "key": "highlightMode", + "value": "letter", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["underscore_spaces"], + "key": "highlightMode", + "value": "off", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["ALL_CAPS"], + "key": "mode", + "value": "time", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["ALL_CAPS"], + "key": "mode", + "value": "words", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["ALL_CAPS"], + "key": "mode", + "value": "quote", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["ALL_CAPS"], + "key": "mode", + "value": "custom", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["ALL_CAPS"], + "key": "mode", + "value": "zen", + "forced": { + "result": true + }, + "canSet": false + }, + { + "names": ["ALL_CAPS"], + "key": "words", + "value": 0, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["ALL_CAPS"], + "key": "words", + "value": 10, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["ALL_CAPS"], + "key": "time", + "value": 0, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["ALL_CAPS"], + "key": "time", + "value": 15, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["ALL_CAPS"], + "key": "punctuation", + "value": true, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["ALL_CAPS"], + "key": "punctuation", + "value": false, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["ALL_CAPS"], + "key": "numbers", + "value": true, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["ALL_CAPS"], + "key": "numbers", + "value": false, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["ALL_CAPS"], + "key": "highlightMode", + "value": "word", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["ALL_CAPS"], + "key": "highlightMode", + "value": "letter", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["ALL_CAPS"], + "key": "highlightMode", + "value": "off", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["polyglot"], + "key": "mode", + "value": "time", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["polyglot"], + "key": "mode", + "value": "words", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["polyglot"], + "key": "mode", + "value": "quote", + "forced": { + "result": true + }, + "canSet": false + }, + { + "names": ["polyglot"], + "key": "mode", + "value": "custom", + "forced": { + "result": true + }, + "canSet": false + }, + { + "names": ["polyglot"], + "key": "mode", + "value": "zen", + "forced": { + "result": true + }, + "canSet": false + }, + { + "names": ["polyglot"], + "key": "words", + "value": 0, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["polyglot"], + "key": "words", + "value": 10, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["polyglot"], + "key": "time", + "value": 0, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["polyglot"], + "key": "time", + "value": 15, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["polyglot"], + "key": "punctuation", + "value": true, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["polyglot"], + "key": "punctuation", + "value": false, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["polyglot"], + "key": "numbers", + "value": true, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["polyglot"], + "key": "numbers", + "value": false, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["polyglot"], + "key": "highlightMode", + "value": "word", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["polyglot"], + "key": "highlightMode", + "value": "letter", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["polyglot"], + "key": "highlightMode", + "value": "off", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["asl"], + "key": "mode", + "value": "time", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["asl"], + "key": "mode", + "value": "words", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["asl"], + "key": "mode", + "value": "quote", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["asl"], + "key": "mode", + "value": "custom", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["asl"], + "key": "mode", + "value": "zen", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["asl"], + "key": "words", + "value": 0, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["asl"], + "key": "words", + "value": 10, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["asl"], + "key": "time", + "value": 0, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["asl"], + "key": "time", + "value": 15, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["asl"], + "key": "punctuation", + "value": true, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["asl"], + "key": "punctuation", + "value": false, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["asl"], + "key": "numbers", + "value": true, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["asl"], + "key": "numbers", + "value": false, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["asl"], + "key": "highlightMode", + "value": "word", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["asl"], + "key": "highlightMode", + "value": "letter", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["asl"], + "key": "highlightMode", + "value": "off", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["rot13"], + "key": "mode", + "value": "time", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["rot13"], + "key": "mode", + "value": "words", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["rot13"], + "key": "mode", + "value": "quote", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["rot13"], + "key": "mode", + "value": "custom", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["rot13"], + "key": "mode", + "value": "zen", + "forced": { + "result": true + }, + "canSet": false + }, + { + "names": ["rot13"], + "key": "words", + "value": 0, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["rot13"], + "key": "words", + "value": 10, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["rot13"], + "key": "time", + "value": 0, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["rot13"], + "key": "time", + "value": 15, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["rot13"], + "key": "punctuation", + "value": true, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["rot13"], + "key": "punctuation", + "value": false, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["rot13"], + "key": "numbers", + "value": true, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["rot13"], + "key": "numbers", + "value": false, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["rot13"], + "key": "highlightMode", + "value": "word", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["rot13"], + "key": "highlightMode", + "value": "letter", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["rot13"], + "key": "highlightMode", + "value": "off", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["no_quit"], + "key": "mode", + "value": "time", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["no_quit"], + "key": "mode", + "value": "words", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["no_quit"], + "key": "mode", + "value": "quote", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["no_quit"], + "key": "mode", + "value": "custom", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["no_quit"], + "key": "mode", + "value": "zen", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["no_quit"], + "key": "words", + "value": 0, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["no_quit"], + "key": "words", + "value": 10, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["no_quit"], + "key": "time", + "value": 0, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["no_quit"], + "key": "time", + "value": 15, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["no_quit"], + "key": "punctuation", + "value": true, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["no_quit"], + "key": "punctuation", + "value": false, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["no_quit"], + "key": "numbers", + "value": true, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["no_quit"], + "key": "numbers", + "value": false, + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["no_quit"], + "key": "highlightMode", + "value": "word", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["no_quit"], + "key": "highlightMode", + "value": "letter", + "forced": { + "result": true + }, + "canSet": true + }, + { + "names": ["no_quit"], + "key": "highlightMode", + "value": "off", + "forced": { + "result": true + }, + "canSet": true + } + ] +} From e72af4368f85afacd54bafd1678d18c1da206d06 Mon Sep 17 00:00:00 2001 From: Lakshmi Tanmay Date: Fri, 23 Oct 2026 04:31:12 +0530 Subject: [PATCH 38/93] Port complete funbox metadata and configuration compatibility rules --- crates/typing-engine/data/funboxes.json | 511 ++++++++++++++++++ crates/typing-engine/src/funboxes.rs | 214 ++++++++ crates/typing-engine/src/generation.rs | 43 +- crates/typing-engine/src/lib.rs | 1 + crates/typing-engine/src/session.rs | 20 +- crates/typing-engine/tests/funboxes.rs | 31 ++ docs/rust-engine/progress.md | 3 + .../typing-core/__test__/rust-oracle.spec.ts | 30 + 8 files changed, 802 insertions(+), 51 deletions(-) create mode 100644 crates/typing-engine/data/funboxes.json create mode 100644 crates/typing-engine/src/funboxes.rs create mode 100644 crates/typing-engine/tests/funboxes.rs diff --git a/crates/typing-engine/data/funboxes.json b/crates/typing-engine/data/funboxes.json new file mode 100644 index 000000000..17cacff2e --- /dev/null +++ b/crates/typing-engine/data/funboxes.json @@ -0,0 +1,511 @@ +[ + { + "name": "58008", + "properties": ["ignoresLanguage", "ignoresLayout", "noLetters"], + "frontendFunctions": [ + "getWord", + "punctuateWord", + "rememberSettings", + "getEmulatedChar" + ], + "frontendForcedConfig": { + "numbers": [false] + }, + "canGetPb": false, + "difficultyLevel": 1, + "cssModifications": [] + }, + { + "name": "mirror", + "properties": ["hasCssFile"], + "frontendFunctions": [], + "frontendForcedConfig": {}, + "canGetPb": true, + "difficultyLevel": 3, + "cssModifications": ["main"] + }, + { + "name": "upside_down", + "properties": ["hasCssFile"], + "frontendFunctions": [], + "frontendForcedConfig": {}, + "canGetPb": true, + "difficultyLevel": 3, + "cssModifications": ["main"] + }, + { + "name": "nausea", + "properties": ["hasCssFile", "ignoreReducedMotion"], + "frontendFunctions": [], + "frontendForcedConfig": {}, + "canGetPb": true, + "difficultyLevel": 2, + "cssModifications": ["typingTest"] + }, + { + "name": "round_round_baby", + "properties": ["hasCssFile", "ignoreReducedMotion"], + "frontendFunctions": [], + "frontendForcedConfig": {}, + "canGetPb": true, + "difficultyLevel": 3, + "cssModifications": ["typingTest"] + }, + { + "name": "simon_says", + "properties": ["hasCssFile", "changesWordsVisibility", "usesLayout"], + "frontendFunctions": ["applyConfig", "rememberSettings"], + "frontendForcedConfig": { + "highlightMode": ["letter", "off"] + }, + "canGetPb": true, + "difficultyLevel": 1, + "cssModifications": [] + }, + { + "name": "tts", + "properties": ["hasCssFile", "changesWordsVisibility", "speaks"], + "frontendFunctions": ["applyConfig", "rememberSettings", "toggleScript"], + "frontendForcedConfig": { + "highlightMode": ["letter", "off"] + }, + "canGetPb": true, + "difficultyLevel": 1, + "cssModifications": ["words"] + }, + { + "name": "choo_choo", + "properties": [ + "hasCssFile", + "noJoiningScript", + "conflictsWithSymmetricChars", + "ignoreReducedMotion" + ], + "frontendFunctions": [], + "frontendForcedConfig": {}, + "canGetPb": true, + "difficultyLevel": 2, + "cssModifications": ["words"] + }, + { + "name": "arrows", + "properties": [ + "ignoresLanguage", + "ignoresLayout", + "nospace", + "noLetters", + "symmetricChars" + ], + "frontendFunctions": [ + "getWord", + "rememberSettings", + "getEmulatedChar", + "getWordNode" + ], + "frontendForcedConfig": { + "punctuation": [false], + "numbers": [false], + "highlightMode": ["letter", "off"] + }, + "canGetPb": false, + "difficultyLevel": 1, + "cssModifications": [] + }, + { + "name": "rAnDoMcAsE", + "properties": ["changesCapitalisation"], + "frontendFunctions": ["alterText"], + "frontendForcedConfig": {}, + "canGetPb": false, + "difficultyLevel": 2, + "cssModifications": [] + }, + { + "name": "sPoNgEcAsE", + "properties": ["changesCapitalisation"], + "frontendFunctions": ["alterText"], + "frontendForcedConfig": {}, + "canGetPb": false, + "difficultyLevel": 2, + "cssModifications": [] + }, + { + "name": "capitals", + "properties": ["changesCapitalisation"], + "frontendFunctions": ["alterText"], + "frontendForcedConfig": {}, + "canGetPb": false, + "difficultyLevel": 1, + "cssModifications": [] + }, + { + "name": "layout_mirror", + "properties": ["changesLayout"], + "frontendFunctions": ["applyConfig", "rememberSettings"], + "frontendForcedConfig": {}, + "canGetPb": true, + "difficultyLevel": 3, + "cssModifications": [] + }, + { + "name": "layoutfluid", + "properties": ["changesLayout", "noInfiniteDuration"], + "frontendFunctions": [ + "applyConfig", + "rememberSettings", + "handleSpace", + "getResultContent" + ], + "frontendForcedConfig": {}, + "canGetPb": true, + "difficultyLevel": 1, + "cssModifications": [] + }, + { + "name": "earthquake", + "properties": ["hasCssFile", "noJoiningScript", "ignoreReducedMotion"], + "frontendFunctions": [], + "frontendForcedConfig": {}, + "canGetPb": true, + "difficultyLevel": 1, + "cssModifications": ["words"] + }, + { + "name": "space_balls", + "properties": ["hasCssFile", "ignoreReducedMotion"], + "frontendFunctions": [], + "frontendForcedConfig": {}, + "canGetPb": true, + "difficultyLevel": 0, + "cssModifications": ["body"] + }, + { + "name": "gibberish", + "properties": ["ignoresLanguage", "unspeakable"], + "frontendFunctions": ["getWord"], + "frontendForcedConfig": {}, + "canGetPb": false, + "difficultyLevel": 1, + "cssModifications": [] + }, + { + "name": "ascii", + "properties": ["ignoresLanguage", "noLetters", "unspeakable"], + "frontendFunctions": ["getWord"], + "frontendForcedConfig": { + "punctuation": [false], + "numbers": [false] + }, + "canGetPb": false, + "difficultyLevel": 1, + "cssModifications": [] + }, + { + "name": "specials", + "properties": ["ignoresLanguage", "noLetters", "unspeakable"], + "frontendFunctions": ["getWord"], + "frontendForcedConfig": { + "punctuation": [false], + "numbers": [false] + }, + "canGetPb": false, + "difficultyLevel": 1, + "cssModifications": [] + }, + { + "name": "plus_zero", + "properties": ["changesWordsVisibility", "toPush:1", "noInfiniteDuration"], + "frontendFunctions": [], + "frontendForcedConfig": {}, + "canGetPb": true, + "difficultyLevel": 1, + "cssModifications": [] + }, + { + "name": "plus_one", + "properties": ["changesWordsVisibility", "toPush:2", "noInfiniteDuration"], + "frontendFunctions": [], + "frontendForcedConfig": {}, + "canGetPb": true, + "difficultyLevel": 0, + "cssModifications": [] + }, + { + "name": "plus_two", + "properties": ["changesWordsVisibility", "toPush:3", "noInfiniteDuration"], + "frontendFunctions": [], + "frontendForcedConfig": {}, + "canGetPb": true, + "difficultyLevel": 0, + "cssModifications": [] + }, + { + "name": "plus_three", + "properties": ["changesWordsVisibility", "toPush:4", "noInfiniteDuration"], + "frontendFunctions": [], + "frontendForcedConfig": {}, + "canGetPb": true, + "difficultyLevel": 0, + "cssModifications": [] + }, + { + "name": "read_ahead_easy", + "properties": ["changesWordsVisibility", "hasCssFile"], + "frontendFunctions": ["rememberSettings", "handleKeydown"], + "frontendForcedConfig": { + "highlightMode": ["letter", "off"] + }, + "canGetPb": true, + "difficultyLevel": 1, + "cssModifications": [] + }, + { + "name": "read_ahead", + "properties": ["changesWordsVisibility", "hasCssFile"], + "frontendFunctions": ["rememberSettings", "handleKeydown"], + "frontendForcedConfig": { + "highlightMode": ["letter", "off"] + }, + "canGetPb": true, + "difficultyLevel": 2, + "cssModifications": [] + }, + { + "name": "read_ahead_hard", + "properties": ["changesWordsVisibility", "hasCssFile"], + "frontendFunctions": ["rememberSettings", "handleKeydown"], + "frontendForcedConfig": { + "highlightMode": ["letter", "off"] + }, + "canGetPb": true, + "difficultyLevel": 3, + "cssModifications": [] + }, + { + "name": "memory", + "properties": ["changesWordsVisibility", "noInfiniteDuration"], + "frontendFunctions": [ + "applyConfig", + "rememberSettings", + "start", + "restart" + ], + "frontendForcedConfig": { + "mode": ["words", "quote", "custom"] + }, + "canGetPb": true, + "difficultyLevel": 3, + "cssModifications": [] + }, + { + "name": "nospace", + "properties": ["nospace"], + "frontendFunctions": ["rememberSettings"], + "frontendForcedConfig": { + "highlightMode": ["letter", "off"] + }, + "canGetPb": false, + "difficultyLevel": 0, + "cssModifications": [] + }, + { + "name": "poetry", + "properties": ["noInfiniteDuration", "ignoresLanguage"], + "frontendFunctions": ["pullSection"], + "frontendForcedConfig": { + "punctuation": [false], + "numbers": [false] + }, + "canGetPb": false, + "difficultyLevel": 0, + "cssModifications": [] + }, + { + "name": "wikipedia", + "properties": ["noInfiniteDuration", "ignoresLanguage"], + "frontendFunctions": ["pullSection"], + "frontendForcedConfig": { + "punctuation": [false], + "numbers": [false] + }, + "canGetPb": false, + "difficultyLevel": 0, + "cssModifications": [] + }, + { + "name": "weakspot", + "properties": ["changesWordsFrequency"], + "frontendFunctions": ["getWord"], + "frontendForcedConfig": {}, + "canGetPb": false, + "difficultyLevel": 0, + "cssModifications": [] + }, + { + "name": "pseudolang", + "properties": ["unspeakable", "ignoresLanguage"], + "frontendFunctions": ["withWords"], + "frontendForcedConfig": {}, + "canGetPb": false, + "difficultyLevel": 0, + "cssModifications": [] + }, + { + "name": "IPv4", + "properties": ["ignoresLanguage", "ignoresLayout", "noLetters"], + "frontendFunctions": ["getWord", "punctuateWord", "rememberSettings"], + "frontendForcedConfig": { + "numbers": [false] + }, + "canGetPb": false, + "difficultyLevel": 1, + "cssModifications": [] + }, + { + "name": "IPv6", + "properties": ["ignoresLanguage", "ignoresLayout", "noLetters"], + "frontendFunctions": ["getWord", "punctuateWord", "rememberSettings"], + "frontendForcedConfig": { + "numbers": [false] + }, + "canGetPb": false, + "difficultyLevel": 1, + "cssModifications": [] + }, + { + "name": "binary", + "properties": ["ignoresLanguage", "ignoresLayout", "noLetters"], + "frontendFunctions": ["getWord"], + "frontendForcedConfig": { + "numbers": [false], + "punctuation": [false] + }, + "canGetPb": false, + "difficultyLevel": 1, + "cssModifications": [] + }, + { + "name": "hexadecimal", + "properties": ["ignoresLanguage", "ignoresLayout", "noLetters"], + "frontendFunctions": ["getWord", "punctuateWord", "rememberSettings"], + "frontendForcedConfig": { + "numbers": [false] + }, + "canGetPb": false, + "difficultyLevel": 1, + "cssModifications": [] + }, + { + "name": "zipf", + "properties": ["changesWordsFrequency"], + "frontendFunctions": ["getWordsFrequencyMode"], + "frontendForcedConfig": {}, + "canGetPb": false, + "difficultyLevel": 0, + "cssModifications": [] + }, + { + "name": "morse", + "properties": ["ignoresLanguage", "ignoresLayout", "noLetters", "nospace"], + "frontendFunctions": ["alterText"], + "frontendForcedConfig": {}, + "canGetPb": false, + "difficultyLevel": 1, + "cssModifications": [] + }, + { + "name": "crt", + "properties": ["hasCssFile", "noJoiningScript"], + "frontendFunctions": ["applyGlobalCSS", "clearGlobal"], + "frontendForcedConfig": {}, + "canGetPb": true, + "difficultyLevel": 0, + "cssModifications": ["body"] + }, + { + "name": "backwards", + "properties": [ + "hasCssFile", + "conflictsWithSymmetricChars", + "wordOrder:reverse", + "reverseDirection" + ], + "frontendFunctions": ["alterText"], + "frontendForcedConfig": {}, + "canGetPb": true, + "difficultyLevel": 3, + "cssModifications": ["words"] + }, + { + "name": "ddoouubblleedd", + "properties": ["noJoiningScript"], + "frontendFunctions": ["alterText"], + "frontendForcedConfig": {}, + "canGetPb": true, + "difficultyLevel": 1, + "cssModifications": [] + }, + { + "name": "instant_messaging", + "properties": ["changesCapitalisation"], + "frontendFunctions": ["alterText"], + "frontendForcedConfig": {}, + "canGetPb": false, + "difficultyLevel": 0, + "cssModifications": [] + }, + { + "name": "underscore_spaces", + "properties": ["ignoresLanguage", "ignoresLayout", "nospace"], + "frontendFunctions": ["alterText"], + "frontendForcedConfig": {}, + "canGetPb": false, + "difficultyLevel": 1, + "cssModifications": [] + }, + { + "name": "ALL_CAPS", + "properties": ["changesCapitalisation"], + "frontendFunctions": ["alterText"], + "frontendForcedConfig": {}, + "canGetPb": false, + "difficultyLevel": 1, + "cssModifications": [] + }, + { + "name": "polyglot", + "properties": ["ignoresLanguage"], + "frontendFunctions": ["withWords"], + "frontendForcedConfig": {}, + "canGetPb": false, + "difficultyLevel": 1, + "cssModifications": [] + }, + { + "name": "asl", + "properties": ["hasCssFile", "noJoiningScript"], + "frontendFunctions": [], + "frontendForcedConfig": {}, + "canGetPb": true, + "difficultyLevel": 1, + "cssModifications": ["words"] + }, + { + "name": "rot13", + "properties": [], + "frontendFunctions": ["alterText"], + "frontendForcedConfig": {}, + "canGetPb": true, + "difficultyLevel": 1, + "cssModifications": [] + }, + { + "name": "no_quit", + "properties": [], + "frontendFunctions": [], + "frontendForcedConfig": {}, + "canGetPb": true, + "difficultyLevel": 0, + "cssModifications": [] + } +] diff --git a/crates/typing-engine/src/funboxes.rs b/crates/typing-engine/src/funboxes.rs new file mode 100644 index 000000000..73bc5cdd9 --- /dev/null +++ b/crates/typing-engine/src/funboxes.rs @@ -0,0 +1,214 @@ +use serde::{Deserialize, Serialize}; +use serde_json::{Value, json}; +use std::{collections::BTreeMap, sync::OnceLock}; +use ts_rs::TS; +#[derive(Debug, Deserialize, Serialize, TS)] +#[serde(rename_all = "camelCase")] +pub struct Metadata { + pub name: String, + pub properties: Vec, + pub frontend_functions: Vec, + pub frontend_forced_config: BTreeMap>, + pub can_get_pb: bool, + pub difficulty_level: u8, + pub css_modifications: Vec, +} +impl Metadata { + fn property(&self, name: &str) -> bool { + self.properties.iter().any(|p| p == name) + } + fn function(&self, name: &str) -> bool { + self.frontend_functions.iter().any(|p| p == name) + } +} +pub fn all() -> &'static [Metadata] { + static METADATA: OnceLock> = OnceLock::new(); + METADATA.get_or_init(|| { + serde_json::from_str(include_str!("../data/funboxes.json")) + .expect("Checked immutable funbox metadata") + }) +} +pub fn get(name: &str) -> Option<&'static Metadata> { + all().iter().find(|f| f.name == name) +} +pub fn has_property(names: &[String], property: &str) -> bool { + names + .iter() + .filter_map(|n| get(n)) + .any(|f| f.property(property)) +} +pub fn has_function(names: &[String], function: &str) -> bool { + names + .iter() + .filter_map(|n| get(n)) + .any(|f| f.function(function)) +} +pub fn to_push(names: &[String]) -> Option { + names + .iter() + .filter_map(|n| get(n)) + .flat_map(|f| &f.properties) + .find_map(|p| p.strip_prefix("toPush:").and_then(|n| n.parse().ok())) +} +pub fn compatible(names: &[String]) -> bool { + let Some(funboxes): Option> = names.iter().map(|n| get(n)).collect() else { + return false; + }; + let count = + |predicate: &dyn Fn(&Metadata) -> bool| funboxes.iter().filter(|f| predicate(f)).count(); + let prop = |name: &str| count(&|f| f.property(name)); + let function = |name: &str| count(&|f| f.function(name)); + if count(&|f| f.function("getWord") || f.function("pullSection") || f.function("withWords")) > 1 + || count(&|f| f.properties.iter().any(|p| p.starts_with("wordOrder"))) > 1 + || (prop("changesLayout") > 0 && (prop("ignoresLayout") > 0 || prop("usesLayout") > 0)) + || count(&|f| { + f.properties + .iter() + .any(|p| p == "nospace" || p.starts_with("toPush")) + }) > 1 + || prop("changesWordsVisibility") > 1 + || prop("changesWordsFrequency") > 1 + || (prop("changesWordsFrequency") > 0 && prop("ignoresLanguage") > 0) + || (prop("noLetters") > 0 && prop("changesCapitalisation") > 0) + || (prop("conflictsWithSymmetricChars") > 0 && prop("symmetricChars") > 0) + || prop("speaks") > 1 + || !(prop("speaks") == 0 + || (prop("speaks") == 1 && prop("unspeakable") == 0) + || prop("ignoresLanguage") == 0) + || count(&|f| { + f.properties.iter().any(|p| p.starts_with("toPush:")) || f.function("pullSection") + }) > 1 + || function("punctuateWord") > 1 + || function("getEmulatedChar") > 1 + || function("getWordNode") > 1 + || prop("changesCapitalisation") > 1 + { + return false; + } + let mut css = vec![]; + let mut allowed: BTreeMap<&str, &Vec> = BTreeMap::new(); + for funbox in funboxes { + for element in &funbox.css_modifications { + if css.contains(element) { + return false; + } + css.push(element.clone()); + } + for (key, values) in &funbox.frontend_forced_config { + if let Some(previous) = allowed.get(key.as_str()) { + // Preserve the original pairwise-against-first check. + if !previous.iter().any(|v| values.contains(v)) { + return false; + } + } else { + allowed.insert(key, values); + } + } + } + true +} +#[derive(Serialize, TS)] +#[serde(rename_all = "camelCase")] +pub struct Forced { + pub result: bool, + #[serde(skip_serializing_if = "Option::is_none")] + #[ts(optional)] + pub forced_configs: Option>, +} +pub fn forced(key: &str, value: &Value, names: &[String]) -> Result { + let funboxes: Vec<_> = names + .iter() + .map(|n| get(n).ok_or_else(|| format!("Invalid funbox name: {n}"))) + .collect::>()?; + if key == "words" || key == "time" { + if value.as_f64() == Some(0.0) && funboxes.iter().any(|f| f.property("noInfiniteDuration")) + { + return Ok(Forced { + result: false, + forced_configs: Some(vec![json!(if key == "words" { 10 } else { 15 })]), + }); + } + return Ok(Forced { + result: true, + forced_configs: None, + }); + } + let mut allowed: BTreeMap<&str, Vec> = BTreeMap::new(); + for funbox in funboxes { + for (key, values) in &funbox.frontend_forced_config { + if let Some(previous) = allowed.get_mut(key.as_str()) { + if values.len() > previous.len() { + let mut next = values.clone(); + next.retain(|v| previous.contains(v)); + *previous = next; + } else { + previous.retain(|v| values.contains(v)); + } + let mut seen = vec![]; + previous.retain(|v| { + if seen.contains(v) { + false + } else { + seen.push(v.clone()); + true + } + }); + } else { + allowed.insert(key, values.clone()); + } + } + } + match allowed.remove(key) { + Some(values) if values.is_empty() => Err("No intersection of forced configs".into()), + Some(values) => Ok(Forced { + result: values.contains(value), + forced_configs: Some(values), + }), + None => Ok(Forced { + result: true, + forced_configs: None, + }), + } +} +pub fn can_set(key: &str, value: &Value, names: &[String]) -> Result { + if key == "mode" { + for name in names { + let f = get(name).ok_or("Invalid funbox name")?; + if f.frontend_forced_config + .get("mode") + .is_some_and(|modes| !modes.contains(value)) + { + return Ok(false); + } + let mode = value.as_str().unwrap_or_default(); + if mode == "zen" + && (f.frontend_functions.iter().any(|name| { + ["getWord", "pullSection", "alterText", "withWords"].contains(&name.as_str()) + }) || f.properties.iter().any(|name| { + [ + "changesCapitalisation", + "nospace", + "changesWordsVisibility", + "speaks", + "changesLayout", + "changesWordsFrequency", + ] + .contains(&name.as_str()) + || name.starts_with("toPush:") + })) + { + return Ok(false); + } + if ["quote", "custom"].contains(&mode) + && (f + .frontend_functions + .iter() + .any(|name| ["getWord", "pullSection", "withWords"].contains(&name.as_str())) + || f.property("changesWordsFrequency")) + { + return Ok(false); + } + } + } + Ok(forced(key, value, names)?.result) +} diff --git a/crates/typing-engine/src/generation.rs b/crates/typing-engine/src/generation.rs index f2ebd90c0..29618cfcb 100644 --- a/crates/typing-engine/src/generation.rs +++ b/crates/typing-engine/src/generation.rs @@ -156,40 +156,8 @@ impl Generator { assets: &mut Assets, ) -> Result { config.validate()?; - if config.funbox.iter().any(|name| { - ![ - "polyglot", - "morse", - "arrows", - "weakspot", - "pseudolang", - "IPv4", - "IPv6", - "58008", - "binary", - "hexadecimal", - "gibberish", - "ascii", - "specials", - "backwards", - "ALL_CAPS", - "capitals", - "sPoNgEcAsE", - "rAnDoMcAsE", - "rot13", - "ddoouubblleedd", - "instant_messaging", - "underscore_spaces", - "nospace", - "plus_zero", - "plus_one", - "plus_two", - "plus_three", - "zipf", - ] - .contains(&name.as_str()) - }) { - return Err("Requested generator transform is not yet ported".into()); + if crate::funboxes::has_function(&config.funbox, "pullSection") { + return Err("Section content protocol is not yet available".into()); } if config.mode == "custom" && custom.is_none() { return Err("Missing custom text".into()); @@ -698,7 +666,12 @@ impl Generator { let mut committed = word.clone(); if committed.0.last() != Some(&10) && !self.config.funbox.iter().any(|name| { - ["nospace", "underscore_spaces", "arrows", "morse"].contains(&name.as_str()) + crate::funboxes::get(name).is_some_and(|metadata| { + metadata + .properties + .iter() + .any(|property| property == "nospace") + }) }) { committed.0.push(32); diff --git a/crates/typing-engine/src/lib.rs b/crates/typing-engine/src/lib.rs index ba683b9fb..500d772fb 100644 --- a/crates/typing-engine/src/lib.rs +++ b/crates/typing-engine/src/lib.rs @@ -1,5 +1,6 @@ //! Platform-independent typing rules. Hosts provide content, observations and time. pub mod analysis; +pub mod funboxes; mod generated_funboxes; pub mod generation; pub mod language_rules; diff --git a/crates/typing-engine/src/session.rs b/crates/typing-engine/src/session.rs index a3678f5c5..d8c90128e 100644 --- a/crates/typing-engine/src/session.rs +++ b/crates/typing-engine/src/session.rs @@ -145,7 +145,7 @@ impl Config { || self .funbox .iter() - .any(|name| name.is_empty() || name.len() > 64) + .any(|name| crate::funboxes::get(name).is_none()) { return Err("Invalid funbox list".into()); } @@ -427,18 +427,8 @@ impl Session { } } fn refill(&mut self) -> Result<(), String> { - let bound = self - .config - .funbox - .iter() - .find_map(|name| match name.as_str() { - "plus_zero" => Some(0), - "plus_one" => Some(1), - "plus_two" => Some(2), - "plus_three" => Some(3), - _ => None, - }) - .unwrap_or(100); + let bound = crate::funboxes::to_push(&self.config.funbox) + .map_or(100, |count| count.saturating_sub(1)); if self.all_generated || self.words.len().saturating_sub(self.index + 1) > bound { return Ok(()); } @@ -483,9 +473,7 @@ impl Session { self.words.get(self.index).cloned().unwrap_or_default() } fn nospace(&self) -> bool { - self.config.funbox.iter().any(|name| { - ["nospace", "arrows", "morse", "underscore_spaces"].contains(&name.as_str()) - }) + crate::funboxes::has_property(&self.config.funbox, "nospace") } fn record(&mut self, kind: &str, now: f64, data: Value) { self.events.push(Recorded { diff --git a/crates/typing-engine/tests/funboxes.rs b/crates/typing-engine/tests/funboxes.rs new file mode 100644 index 000000000..b0593bc8c --- /dev/null +++ b/crates/typing-engine/tests/funboxes.rs @@ -0,0 +1,31 @@ +use serde_json::Value; +#[test] +fn all_funbox_pairs_and_forced_configuration_match_frozen_typescript() { + let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR")) + .join("../../packages/typing-fixtures/oracle/funboxes.json"); + let cases: Value = serde_json::from_slice(&std::fs::read(root).unwrap()).unwrap(); + for case in cases["combinations"].as_array().unwrap() { + let names: Vec = serde_json::from_value(case["names"].clone()).unwrap(); + assert_eq!( + typing_engine::funboxes::compatible(&names), + case["compatible"].as_bool().unwrap(), + "{names:?}" + ); + } + for case in cases["forced"].as_array().unwrap() { + let names: Vec = serde_json::from_value(case["names"].clone()).unwrap(); + let key = case["key"].as_str().unwrap(); + let value = &case["value"]; + assert_eq!( + serde_json::to_value(typing_engine::funboxes::forced(key, value, &names).unwrap()) + .unwrap(), + case["forced"], + "{case}" + ); + assert_eq!( + typing_engine::funboxes::can_set(key, value, &names).unwrap(), + case["canSet"].as_bool().unwrap(), + "{case}" + ); + } +} diff --git a/docs/rust-engine/progress.md b/docs/rust-engine/progress.md index 0a54a479e..5da49ade4 100644 --- a/docs/rust-engine/progress.md +++ b/docs/rust-engine/progress.md @@ -101,3 +101,6 @@ do not rerun completed checks without a new reason. - Session now owns an attached generator, incremental lookahead refill, final separator stripping, quote identity, custom time/infinite limits and explicit bailout state. Full snapshots are explicit; per-command word changes are incremental. Recorder deltas signal invalidation when origin changes or finalization/reset changes earlier events. - Five new native session tests cover 150-word continuation, plus-zero refill/completion, custom time/infinite limits, recorder rebasing/finalization, and pre-mutation malformed config / oversized commits / bounded timer catchup. Browser integration of these new APIs still pending. + +- New immutable funbox rule metadata and Rust compatibility/forced-config checks match all 2,304 ordered pairs and 768 setting cases captured once from TypeScript. A mechanical TypeScript guard checks metadata equality. Visual-only funboxes are accepted by generation; section funboxes remain an explicit missing-protocol error until the content lifecycle is implemented. +- Optimized Cloudflare proof at ccb7db67f passes all 3 tests after Morse reduction/session additions; asset 753.41 kB raw / 285.44 kB gzip (Vite report), within frozen payload budgets. New funbox code has native verification only so far. diff --git a/packages/typing-core/__test__/rust-oracle.spec.ts b/packages/typing-core/__test__/rust-oracle.spec.ts index fb62126bf..85dadca8c 100644 --- a/packages/typing-core/__test__/rust-oracle.spec.ts +++ b/packages/typing-core/__test__/rust-oracle.spec.ts @@ -1,6 +1,7 @@ import { createHash } from "node:crypto"; import { readdirSync, readFileSync } from "node:fs"; import { resolve } from "node:path"; +import { getAllFunboxes } from "@oxytype/funbox"; import { expect, it } from "vite-plus/test"; import { readCommandFixture, runOracle } from "../scripts/rust-oracle"; @@ -52,3 +53,32 @@ it("original and neutral reference fixtures retain the frozen bytes", () => { } } }); + +it("Rust funbox metadata stays mechanically equal to the website contract", () => { + const actual = JSON.parse( + readFileSync( + resolve(root, "../../crates/typing-engine/data/funboxes.json"), + "utf8", + ), + ) as unknown; + const expected = getAllFunboxes().map( + ({ + name, + properties = [], + frontendFunctions = [], + frontendForcedConfig = {}, + canGetPb, + difficultyLevel, + cssModifications = [], + }) => ({ + name, + properties, + frontendFunctions, + frontendForcedConfig, + canGetPb, + difficultyLevel, + cssModifications, + }), + ); + expect(actual).toEqual(expected); +}); From 9b15687f4881a61f383bf8a8e8b28f3897bccf40 Mon Sep 17 00:00:00 2001 From: Lakshmi Tanmay Date: Fri, 23 Oct 2026 04:32:59 +0530 Subject: [PATCH 39/93] Generate shared defaults and mechanically check website preference parity --- crates/typing-engine/src/bin/export-bindings.rs | 11 ++++++++++- packages/typing-core/__test__/rust-oracle.spec.ts | 14 ++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/crates/typing-engine/src/bin/export-bindings.rs b/crates/typing-engine/src/bin/export-bindings.rs index 03a0050cd..d48a1b7a6 100644 --- a/crates/typing-engine/src/bin/export-bindings.rs +++ b/crates/typing-engine/src/bin/export-bindings.rs @@ -7,7 +7,16 @@ use typing_engine::{ fn main() -> Result<(), Box> { let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR")) .join("../../packages/typing-engine/generated/types"); - let config = ts_rs::Config::new().with_out_dir(root); + let config = ts_rs::Config::new().with_out_dir(root.clone()); + std::fs::create_dir_all(root.parent().unwrap())?; + std::fs::write( + root.parent().unwrap().join("defaults.json"), + serde_json::to_vec_pretty(&typing_engine::Config::default())?, + )?; + std::fs::write( + root.parent().unwrap().join("funboxes.json"), + serde_json::to_vec_pretty(typing_engine::funboxes::all())?, + )?; Command::export_all(&config)?; Config::export_all(&config)?; State::export_all(&config)?; diff --git a/packages/typing-core/__test__/rust-oracle.spec.ts b/packages/typing-core/__test__/rust-oracle.spec.ts index 85dadca8c..d43ced490 100644 --- a/packages/typing-core/__test__/rust-oracle.spec.ts +++ b/packages/typing-core/__test__/rust-oracle.spec.ts @@ -1,6 +1,7 @@ import { createHash } from "node:crypto"; import { readdirSync, readFileSync } from "node:fs"; import { resolve } from "node:path"; +import { getDefaultConfig } from "../src/config/default-config"; import { getAllFunboxes } from "@oxytype/funbox"; import { expect, it } from "vite-plus/test"; import { readCommandFixture, runOracle } from "../scripts/rust-oracle"; @@ -82,3 +83,16 @@ it("Rust funbox metadata stays mechanically equal to the website contract", () = ); expect(actual).toEqual(expected); }); + +it("Rust engine defaults stay mechanically equal to supported website preferences", () => { + const defaults = JSON.parse( + readFileSync( + resolve(root, "../typing-engine/generated/defaults.json"), + "utf8", + ), + ) as Record; + const website: Record = getDefaultConfig(); + for (const [key, value] of Object.entries(defaults)) { + expect(value, key).toEqual(website[key]); + } +}); From 2e7e9e5b08fa69bd277c6c09e977045608970fbd Mon Sep 17 00:00:00 2001 From: Lakshmi Tanmay Date: Fri, 23 Oct 2026 04:39:43 +0530 Subject: [PATCH 40/93] Freeze 2048 deterministic input decisions with lossless shared defaults --- .../scripts/capture-input-oracle.ts | 116 + .../typing-fixtures/oracle/input-rules.json | 77569 ++++++++++++++++ 2 files changed, 77685 insertions(+) create mode 100644 packages/typing-core/scripts/capture-input-oracle.ts create mode 100644 packages/typing-fixtures/oracle/input-rules.json diff --git a/packages/typing-core/scripts/capture-input-oracle.ts b/packages/typing-core/scripts/capture-input-oracle.ts new file mode 100644 index 000000000..edef118a8 --- /dev/null +++ b/packages/typing-core/scripts/capture-input-oracle.ts @@ -0,0 +1,116 @@ +import { writeFileSync } from "node:fs"; +import { resolve } from "node:path"; +import type { Language } from "@oxytype/schemas/languages"; +import { getDefaultConfig } from "../src/config/default-config"; +import { + evaluateInsert, + shouldBlockInsertion, + canDelete, +} from "../src/input/engine"; +import { mulberry32 } from "./lib/keystroke-plan"; +import { units } from "./rust-oracle"; +const random = mulberry32(49); +const pick = (items: readonly T[]): T => + items[Math.floor(random() * items.length)] as T; +const output: { + config: ReturnType; + input: Record; + state: Record; + blocked: boolean; + canDelete: boolean; + decision: Record; +}[] = []; +for (let index = 0; index < 2048; index++) { + const config = { + ...getDefaultConfig(), + mode: pick(["words", "zen"] as const), + difficulty: pick(["normal", "expert", "master"] as const), + strictSpace: pick([true, false]), + stopOnError: pick(["off", "letter", "word"] as const), + deleteOnError: pick([ + "off", + "letter", + "word", + "letter_hard", + "word_hard", + ] as const), + freedomMode: pick([true, false]), + confidenceMode: pick(["off", "on", "max"] as const), + blindMode: pick([true, false]), + language: pick([ + "english", + "russian", + "russian_1k", + "russian_phonetic", + "dutch", + ] as Language[]), + }; + const options = { + data: pick(["a", " ", "\n", "\u3000", "é", "…", "😀", "\ud83d", "e", "ab"]), + inputValue: pick(["", "a", "ab", "xx", "a ", "x".repeat(32), "\ud83d"]), + targetWord: pick([ + "a", + "ab ", + "e ", + "е ", + "ab\n", + "😀", + "a\t", + "\ud83d", + " ", + ]), + nospace: pick([true, false]), + correctShiftUsed: pick([true, false, null]), + hasNewline: pick([true, false]), + }; + const state = { + inputValue: options.inputValue, + wordIndex: pick([0, 1, 3]), + previousWordCorrect: pick([true, false]), + previousWordAvailable: pick([true, false]), + }; + const decision = evaluateInsert(config, options); + const neutral = { + ...options, + data: units(options.data), + inputValue: units(options.inputValue), + targetWord: units(options.targetWord), + }; + output.push({ + config, + input: neutral, + state: { ...state, inputValue: units(state.inputValue) }, + blocked: shouldBlockInsertion(config, options), + canDelete: canDelete(config, state), + decision: { + ...decision, + data: units(decision.data), + inputValue: units(decision.inputValue), + ...(decision.visualInputOverride === undefined + ? {} + : { visualInputOverride: units(decision.visualInputOverride) }), + }, + }); +} +const first = output[0]; +if (first === undefined) throw new Error("Missing input references"); +const defaults = Object.fromEntries( + Object.entries(first.config).filter(([key, value]) => + output.every( + (item) => + JSON.stringify(item.config[key as keyof typeof item.config]) === + JSON.stringify(value), + ), + ), +); +const cases = output.map((item) => ({ + ...item, + config: Object.fromEntries( + Object.entries(item.config).filter(([key]) => !(key in defaults)), + ), +})); +writeFileSync( + resolve(import.meta.dirname, "../../typing-fixtures/oracle/input-rules.json"), + `${JSON.stringify({ defaults, cases }, null, 2)}\n`, + { flag: "wx" }, +); diff --git a/packages/typing-fixtures/oracle/input-rules.json b/packages/typing-fixtures/oracle/input-rules.json new file mode 100644 index 000000000..442ccd1c0 --- /dev/null +++ b/packages/typing-fixtures/oracle/input-rules.json @@ -0,0 +1,77569 @@ +{ + "defaults": { + "theme": "rust", + "themeLight": "serika", + "themeDark": "rust", + "autoSwitchTheme": false, + "customTheme": false, + "customThemeColors": [ + "#180d0d", + "#e8692e", + "#e8692e", + "#9c867a", + "#271616", + "#f3e7dc", + "#ff4d6d", + "#b8344f", + "#ff4d6d", + "#b8344f" + ], + "favThemes": [], + "showKeyTips": false, + "smoothCaret": "medium", + "codeUnindentOnBackspace": false, + "quickRestart": "off", + "punctuation": false, + "numbers": false, + "words": 50, + "time": 30, + "quoteLength": [1], + "fontSize": 2, + "quickEnd": false, + "caretStyle": "default", + "paceCaretStyle": "default", + "flipTestColors": false, + "layout": "default", + "funbox": [], + "indicateTypos": "off", + "compositionDisplay": "replace", + "timerStyle": "mini", + "liveSpeedStyle": "off", + "liveAccStyle": "off", + "liveBurstStyle": "off", + "colorfulMode": false, + "randomTheme": "off", + "timerColor": "main", + "timerOpacity": "1", + "showAllLines": false, + "keymapMode": "off", + "keymapStyle": "staggered", + "keymapLegendStyle": "lowercase", + "keymapLayout": "overrideSync", + "keymapKeys": "minimal", + "keymapSize": 1, + "fontFamily": "Roboto_Mono", + "smoothLineScroll": false, + "alwaysShowDecimalPlaces": false, + "alwaysShowWordsHistory": false, + "singleListCommandLine": "on", + "commandPaletteHotkey": "Mod+K", + "capsLockWarning": true, + "playSoundOnError": "off", + "playSoundOnClick": "off", + "soundVolume": 0.5, + "startGraphsAtZero": true, + "showOutOfFocusWarning": true, + "showTestModesNotice": true, + "paceCaret": "off", + "paceCaretCustomSpeed": 100, + "repeatedPace": true, + "accountChart": ["on", "on", "on", "on"], + "minWpm": "off", + "minWpmCustomSpeed": 100, + "highlightMode": "letter", + "typedEffect": "keep", + "typingSpeedUnit": "wpm", + "hideExtraLetters": false, + "minAcc": "off", + "minAccCustom": 90, + "monkey": false, + "repeatQuotes": "off", + "resultSaving": true, + "oppositeShiftMode": "off", + "customBackground": "", + "customBackgroundSize": "cover", + "customBackgroundFilter": [0, 1, 1, 1], + "customLayoutfluid": ["qwerty", "dvorak", "colemak"], + "customPolyglot": ["english", "spanish", "french", "german"], + "monkeyPowerLevel": "off", + "minBurst": "off", + "minBurstCustomSpeed": 100, + "burstHeatmap": false, + "britishEnglish": false, + "lazyMode": false, + "showAverage": "off", + "showPb": false, + "tapeMode": "off", + "tapeMargin": 50, + "maxLineWidth": 0, + "playTimeWarning": "off" + }, + "cases": [ + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [97, 32], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [97, 98], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 32] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [55357, 56832] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [120, 120], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 97] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [97], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [97, 32], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 55357] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [97, 32], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [8230] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [120, 120], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 101] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [97], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [10], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 10] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [97, 98], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [120, 120], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 8230] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [97, 98], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 32 + ] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [55357], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [233], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [55357], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [55357], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [55357], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [55357, 97] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [97, 98], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [120, 120], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [120, 120], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [97, 98], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [32], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [97, 32], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [97], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [55357], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [120, 120], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 101] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [120, 120], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [55357], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [55357, 55357, 56832] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [8230], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [97, 32], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [10], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 10] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [], + "visualInputOverride": [55357, 56832] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [97, 32], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 32, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [10], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 10 + ] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [97, 32], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [97, 98], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 55357, 56832] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [97, 98], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [120, 120], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 8230] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "visualInputOverride": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 55357, 56832 + ] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [97, 98], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [97, 98], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 98, 10] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [97, 98], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [233], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [120, 120], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [120, 120, 32] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [97, 98], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [97, 32], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 32, 10] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [97, 32], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 55357] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [97], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [55357], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [32], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [233], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 233 + ] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [120, 120], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [97, 98], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 55357, 56832] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [55357], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [55357, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [101], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 101 + ] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [55357], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [97], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 97 + ] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [10], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [97], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 8230] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [97], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 97] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [97, 32], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 55357, 56832] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [55357], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [233], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [97, 98], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 55357] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [97, 98], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [55357], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [55357, 55357, 56832] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [97, 98], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [97, 32], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 32, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [8230], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 8230 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [55357], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [8230], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [10], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [97], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [10], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [120, 120], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [97], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [120, 120], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 97] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [120, 120], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [120, 120], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [97], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 55357, 56832] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 32 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [101], + "inputValue": [120, 120], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 101] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [97, 32], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [120, 120], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [10], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [120, 120], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 55357, 56832] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [97, 32], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 233] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [101], + "inputValue": [97, 32], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [55357], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [97], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [55357, 97] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [97, 32], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [10], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [55357], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [97, 98], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [55357], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [55357], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [55357, 97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [55357], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [55357, 8230] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [120, 120], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [97, 32], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 32, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [55357], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [97], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [55357, 97] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [233], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 233 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [97, 98], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [8230], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [101], + "inputValue": [97, 32], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 101] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [97, 98], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 98], + "visualInputOverride": [97, 98, 233] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [97, 98], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [10], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 98], + "visualInputOverride": [97, 98, 10] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [97], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 55357, 56832] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [97, 98], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 233] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [120, 120], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [97, 32], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [8230], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 8230] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [97], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [97, 32], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 10] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [120, 120], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [8230], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [101], + "inputValue": [55357], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [55357, 101] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [97, 98], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 98, 97] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [97], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [97], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [97], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 55357] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [97, 32], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [10], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 32, 10] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [101], + "inputValue": [120, 120], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [97, 32], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [55357], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [55357, 97] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [97], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [120, 120], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 233] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [97], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [97, 98], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [120, 120], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [120, 120], + "visualInputOverride": [120, 120, 10] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [101], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [101], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 101 + ] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [101] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [97], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 55357] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [55357], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [233], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [97], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [97, 98], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [97, 98], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [120, 120], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [120, 120], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [120, 120, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 97, 98 + ] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [120, 120], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [97, 98], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 55357] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [97], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 97] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [97, 32], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 97] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [32], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [233], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 233 + ] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [8230], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 8230 + ] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [97, 98], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [55357], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [55357, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [97, 98], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [120, 120], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [97, 32], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [97, 32], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [101], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [97, 98], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 8230] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [97], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [97, 32], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 32, 10] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [8230], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 8230 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [97, 98], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [97], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 97] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [101], + "inputValue": [97, 32], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [101], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 101] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 97 + ] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [97], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [97, 98], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 98, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [97, 32], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 55357, 56832] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 97, 98 + ] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [97], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [55357], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [55357, 8230] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [101], + "inputValue": [97], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 101] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [55357], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [55357, 32] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [97, 32], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [10], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32], + "visualInputOverride": [97, 32, 10] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [97, 98], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [32], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [32] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [55357], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [55357, 233] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [97, 98], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 8230] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [32], + "correct": false, + "stopped": false, + "advance": true, + "inputValue": [32] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [55357], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [97, 32], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 10] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [97, 98], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [120, 120], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [32], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [97, 32], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [97], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [10], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 10] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [55357], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [97, 32], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 55357] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [97, 32], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 55357] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [97], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 55357] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [97, 98], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [55357], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [55357], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [55357, 55357] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [101], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 101 + ] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [120, 120], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 8230] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [97, 32], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 55357, 56832] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [97, 32], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 55357, 56832] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [55357], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [120, 120], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 233] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [55357], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [55357], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [55357, 55357] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [120, 120], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 55357] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [55357], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [55357, 97] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [55357], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [97, 98], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [97], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 55357] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [97], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [101], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 101] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [97], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [97, 32], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [97, 98], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [10], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [97, 32], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [32], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [97, 98], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 98, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [120, 120], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [97, 98], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [8230], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 8230] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [97, 98], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 55357] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [120, 120], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 233] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [32], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [97, 98], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 97, 98 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [120, 120], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [8230], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 8230] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 10 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [97], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 101] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [120, 120], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [97, 98], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [97, 98], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [97, 98], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [97, 32], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 55357] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [55357], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [97, 32], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 32] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [97], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [97, 98], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 32 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [97], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [55357], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [101], + "inputValue": [97, 98], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [55357], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [55357], + "visualInputOverride": [55357, 55357] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [32], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 32 + ] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [97, 32], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32], + "visualInputOverride": [97, 32, 233] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [97], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 55357, 56832] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [120, 120], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 233] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [120, 120], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [97, 98], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [8230], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [97], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [97, 98], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 98], + "visualInputOverride": [97, 98, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [97, 98], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [120, 120], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [97], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 97 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [97], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 55357, 56832] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [97, 98], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [55357], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [55357, 10] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [97, 98], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 98, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [120, 120], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [97, 98], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [55357], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 55357 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [8230], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 8230 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [101], + "inputValue": [120, 120], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [32], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [97], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [97, 98], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [32], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [97, 98], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [8230], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 8230] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [97], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [97, 32], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [97], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [97, 32], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [120, 120], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 55357] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [97, 98], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [97, 98], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [55357], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 55357 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [233], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 233 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [97, 98], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 98, 10] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [101], + "inputValue": [97, 98], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 101] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [55357], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [55357, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [97, 98], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 55357] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [101], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [101] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [55357], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [8230], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [8230] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [97, 98], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [97, 32], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [101], + "inputValue": [], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [101], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [101] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [97, 32], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [97, 32], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 32, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [8230], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [8230] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [97], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [101], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 101 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [55357], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [97], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [97], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [32], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 97 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [97], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 55357, 56832 + ] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [97, 32], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32], + "visualInputOverride": [97, 32, 97] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [101], + "inputValue": [55357], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [101], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [97, 32], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [55357], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [55357, 10] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [97], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [97, 98], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [32] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [97, 32], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [97, 98], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 55357] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [97, 98], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [32] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [97], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [97, 32], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [97], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [97], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [55357], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 55357 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [120, 120], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [120, 120, 10] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [97, 98], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 8230] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [97, 32], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 32, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [8230], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 8230 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [120, 120], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 55357, 56832] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [97, 32], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 32, 10] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [120, 120], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [97, 98], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 55357] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [97, 32], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 97] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [97, 98], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [10], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 98, 10] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [120, 120], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [10], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 32 + ] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [10], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [120, 120], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [120, 120], + "visualInputOverride": [120, 120, 55357, 56832] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [55357], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [55357], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [10], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [55357, 10] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [97, 32], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [97, 32], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 32] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [101], + "inputValue": [120, 120], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [55357], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [55357], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 55357 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [97], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [97], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [97], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [233], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 233] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [97, 32], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 8230] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [97, 98], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 98], + "visualInputOverride": [97, 98, 55357, 56832] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [55357], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [97], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 55357] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [8230], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 8230 + ] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [97, 98], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 233] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [120, 120], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [32], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [97, 32], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [97, 98], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 98, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [120, 120], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [97, 32], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [97, 98], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 8230] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [97], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [97], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [10], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [120, 120], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [97, 98], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [120, 120], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [97, 32], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [97], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 8230] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [101], + "inputValue": [55357], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [55357, 101] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [97, 32], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 32, 8230] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [120, 120], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [97], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 97] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [97, 98], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [10], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 98, 10] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [97, 98], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 98, 10] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [97, 98], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [101], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [101], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [97, 32], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 55357] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [97], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 32 + ] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [55357], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [97, 32], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [97, 98], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [120, 120], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 97] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [10], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 10 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [10], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [10] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 32 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [8230], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [55357], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [55357, 32] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "visualInputOverride": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 32 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [55357], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 55357 + ] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [55357], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [101], + "inputValue": [97], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [120, 120], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [97, 32], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [55357], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [97, 98], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [97], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 97] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [97, 98], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [101], + "inputValue": [97, 98], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 101] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [233], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [233] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [120, 120], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 55357] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 97 + ] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [97, 32], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [97, 32], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [120, 120], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [120, 120], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [8230], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [8230] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [97, 32], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [55357], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 55357 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [8230], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 8230 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 32 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [120, 120], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 97] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [97, 98], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 55357, 56832] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [97], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 97] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [97, 32], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 32, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [97], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [97, 98], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [97, 32], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 101] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [97], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [120, 120], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [55357], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [97], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [10], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [97, 32], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [55357], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [55357, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [97], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 101] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [97], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [97, 32], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [32], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [101], + "inputValue": [97], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 101] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [120, 120], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [120, 120, 10] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [97], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 55357, 56832] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [10], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 10 + ] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [97, 32], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 233] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [55357], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [55357, 97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [97, 98], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 98], + "visualInputOverride": [97, 98, 55357, 56832] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [97], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 233] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [120, 120], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [8230], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [120, 120, 8230] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [101], + "inputValue": [97], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 101] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [97], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [97], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [55357], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [55357, 97] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [55357], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [55357, 101] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [97, 32], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 55357, 56832] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [55357], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [55357], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [55357, 32] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [8230], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 8230 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [55357, 56832] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [97, 32], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 233] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [55357], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [55357, 55357, 56832] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [97], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [97], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [97], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [55357], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [55357, 101] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [97, 32], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 32] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [55357], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [10], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [10] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [120, 120], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 97] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [97], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [55357], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [55357, 55357] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 32 + ] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [97, 98], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [120, 120], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [55357], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [120, 120], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [120, 120], + "visualInputOverride": [120, 120, 55357] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [97], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [97, 32], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [32], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [97, 32], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 32, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [233], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [233] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [55357], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [55357, 10] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [233], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 233 + ] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [55357], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 97, 98 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [55357, 56832] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [97], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [97, 32], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 55357] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [97, 32], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [120, 120], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [55357], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "visualInputOverride": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 55357 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [120, 120], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [97, 98], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 55357] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [101], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [101], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 101 + ] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [55357], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 55357 + ] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [97, 98], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [55357], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [55357, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [55357], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [55357, 55357] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [97], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [10], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [120, 120], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 55357, 56832] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [97, 32], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 55357] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [97, 32], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 8230] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [97, 32], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 55357, 56832] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [233], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 233 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [120, 120], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [120, 120, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [32], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [97, 98], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [120, 120], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 233] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [101], + "inputValue": [120, 120], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 101] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [55357], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [97, 98], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [55357], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 55357 + ] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [8230], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 8230 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [97, 98], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 55357] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [55357], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [101], + "inputValue": [], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [101], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [101] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [97, 32], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 55357, 56832] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [97], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [97, 32], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 32, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [97, 98], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [120, 120], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [97], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [97, 98], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [120, 120], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [97, 32], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32], + "visualInputOverride": [97, 32, 8230] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [97], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [55357], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 55357 + ] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [55357], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [32], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [55357, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [120, 120], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 8230] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [120, 120], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 8230] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [97, 32], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [55357], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [97], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [55357, 56832] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [101], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [101] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [233], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 233 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [120, 120], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 233] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [120, 120], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [97, 32], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [32] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [233], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 55357, 56832 + ] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [120, 120], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [120, 120], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [97], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [97], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 97] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [97, 32], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [32], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [120, 120], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [120, 120, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [10], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [120, 120], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [10], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 10] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [97], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 101] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [97], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [8230], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 8230] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [97, 32], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [97], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [32], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [120, 120], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [120, 120, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [97, 32], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 55357] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [101], + "inputValue": [97, 32], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [101], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32], + "visualInputOverride": [97, 32, 101] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [120, 120], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [97], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [97, 98], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 8230] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [101], + "inputValue": [97, 32], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 32, 101] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [97], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [101], + "inputValue": [120, 120], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [101], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [120, 120, 101] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [55357], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [55357, 55357, 56832] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [97, 98], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 98, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [55357], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [55357, 97] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 97, 98 + ] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [97, 98], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 98, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [97, 98], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 98, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [97], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [97, 32], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [120, 120], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [55357], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [97, 32], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 233] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [120, 120], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [97, 98], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [10], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [120, 120], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 97] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [97, 98], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 8230] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [55357], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [55357], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 55357 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [97], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [55357, 56832] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [101], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 101 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [233], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 233 + ] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [97, 32], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [55357], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [55357, 55357] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [97, 32], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [120, 120], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [10], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [120, 120, 10] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [120, 120], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [97], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [10], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [10] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "visualInputOverride": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 97, 98 + ] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [97, 32], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [97, 98], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 97] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [120, 120], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 32 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 32 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [55357], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 55357 + ] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [97, 98], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [97, 32], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 32, 32] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [55357], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [55357, 8230] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [120, 120], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [97, 98], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [8230], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [8230] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [97, 98], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 55357] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [8230], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [120, 120], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [8230], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 8230 + ] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [101], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [101] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [8230], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 8230 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [97, 32], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 32, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [97], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [97, 32], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [55357], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [55357, 55357] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [97, 32], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32], + "visualInputOverride": [97, 32, 101] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [97, 32], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 233] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [97, 98], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 55357] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [55357], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [97], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [55357, 56832] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [97, 32], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32], + "visualInputOverride": [97, 32, 97] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [97, 32], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [120, 120], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [55357], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [55357], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [97, 32], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 32, 32] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [55357], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [55357, 8230] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [97], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 97] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [97, 98], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [10], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [97, 98], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [97, 32], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 97 + ] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [10], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [101], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 101 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [97], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [97, 32], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [55357], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [55357, 55357, 56832] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [97, 98], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [97, 98], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [55357], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [8230], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [97, 98], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 98, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [120, 120], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [233], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [120, 120, 233] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [55357], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [10], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [97, 98], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [97, 32], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 10] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [97], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [55357], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [55357, 97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [97], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [55357], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [97, 32], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32], + "visualInputOverride": [97, 32, 97] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [97], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [233], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [233] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [55357], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [55357, 10] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [120, 120], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [], + "visualInputOverride": [55357] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [97, 32], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [97, 98], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 97] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [97, 98], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 55357, 56832] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [8230], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [97, 98], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 98], + "visualInputOverride": [97, 98, 233] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [55357], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [55357, 8230] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [97, 32], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 97] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [233], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [55357], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 97, 98 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [97], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [97, 98], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 8230] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [97, 32], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 55357] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [97, 32], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [97], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 97] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [97, 98], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 98], + "visualInputOverride": [97, 98, 97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [97, 32], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 55357] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [101], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [120, 120], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [55357], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [97, 98], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [10], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 10] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [120, 120], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [97, 98], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [97, 98], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [97], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [120, 120], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 55357] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [97], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 55357] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [97, 32], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [97, 32], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32], + "visualInputOverride": [97, 32, 32] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [120, 120], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": false, + "advance": true, + "inputValue": [120, 120, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [55357], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [97], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [10], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97], + "visualInputOverride": [97, 10] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [120, 120], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 55357, 56832] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [97, 32], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 233] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [8230], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 8230 + ] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [10], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [], + "visualInputOverride": [10] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [97], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [120, 120], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 8230] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [97], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 55357, 56832] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 32 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [97], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [97], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 97] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [97, 98], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 98, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [97, 32], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [10], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 32, 10] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [97, 98], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 233] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [120, 120], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [10], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [120, 120, 10] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [97], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [233] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [97, 98], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [32], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [120, 120], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [97, 98], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [8230], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 8230] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [97, 32], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 32, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 55357, 56832 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [97], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [55357], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [55357, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 32 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [233], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 233 + ] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [97], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97], + "visualInputOverride": [97, 32] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [55357], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [97], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 32 + ] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [97, 98], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [120, 120], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 8230] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [97, 98], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [97, 32], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [97], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [97, 32], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 10] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [97, 32], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 97] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [97, 98], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [120, 120], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 8230] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [120, 120], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 55357] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [101], + "inputValue": [97, 32], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [120, 120], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 101] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [120, 120], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 32] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [97, 32], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 97] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [97, 98], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [120, 120], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 97] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [8230], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [120, 120], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [97], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 55357] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [97, 98], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 8230] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [55357], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [97, 32], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [97], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 55357, 56832] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [55357], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [55357, 32] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [55357], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [55357], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [97], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [97, 98], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [97, 98], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [55357], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [97, 32], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32], + "visualInputOverride": [97, 32, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [97, 32], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [97], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 101] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [97, 32], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [32] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [233], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [101], + "inputValue": [55357], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [55357, 101] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 32 + ] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [97, 32], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 233] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [120, 120], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 55357, 56832] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [97, 32], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 10] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [8230], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 8230 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [55357], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [55357, 55357] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [55357], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [97, 32], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 233] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [55357], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [55357], + "visualInputOverride": [55357, 233] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [97], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [97, 98], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [101], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [120, 120], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [101], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [97], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [233], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 233] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [120, 120], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [120, 120, 10] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [120, 120], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [97], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 97] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [10], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [10] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [55357], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [55357, 101] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 32 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [120, 120], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [97, 98], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 8230] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [120, 120], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [120, 120], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [120, 120], + "visualInputOverride": [120, 120, 101] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [55357], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [120, 120], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 233] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [120, 120], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 55357, 56832 + ] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "visualInputOverride": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 97, 98 + ] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [97, 32], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [101], + "inputValue": [97, 98], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 101] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [97, 98], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 98, 32] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [97, 98], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [97, 98], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 97] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [97], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [97, 32], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 8230] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [97, 98], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [97], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [97], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97], + "visualInputOverride": [97, 55357] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [120, 120], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [97, 32], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 55357, 56832] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [97], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 101] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [120, 120], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [97], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "visualInputOverride": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 55357, 56832 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [120, 120], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [120, 120, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [97, 98], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 101] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [97], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [101], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [101], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 101 + ] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [97, 98], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 97] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [97], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [10], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [97, 32], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 32, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [97], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [120, 120], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 233] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [101], + "inputValue": [97, 32], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 101] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [97, 98], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [97, 32], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 32, 10] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [120, 120], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 97, 98 + ] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [55357], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [101], + "inputValue": [97, 98], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [97], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [120, 120], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [55357], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [8230], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 8230 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [97], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 55357] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [97, 32], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [97, 32], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 101] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [97, 32], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": false, + "advance": true, + "inputValue": [97, 32, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [10], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 10 + ] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [55357], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [97, 98], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 8230] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [97, 98], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [97], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [10], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 10] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [120, 120], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 97] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [97, 98], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [97, 32], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [97, 98], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": false, + "advance": true, + "inputValue": [97, 98, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [97], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [120, 120], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 55357] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [97], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [97, 32], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 8230] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [55357], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [55357, 55357, 56832] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [55357], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [55357, 8230] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [32], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [32] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [97, 98], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [97, 32], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 32, 10] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [97], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 10] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 97, 98 + ] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [55357], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [55357, 97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [97], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 97 + ] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [97, 32], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [97], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [101], + "inputValue": [97, 32], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 101] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [120, 120], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [97, 32], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [97, 32], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [55357], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [10], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [120, 120], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [8230], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 8230] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [97], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 8230] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [97], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [97, 98], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": false, + "advance": true, + "inputValue": [97, 98, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [55357], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [97, 32], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [55357], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [55357, 8230] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [55357], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [55357, 97] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [101], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [55357], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [55357], + "visualInputOverride": [55357, 101] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [101], + "inputValue": [97, 98], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [101], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [120, 120], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [97], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [97, 32], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [233], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [55357, 56832] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [97, 98], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 98], + "visualInputOverride": [97, 98, 233] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [97, 98], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [97, 32], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [120, 120], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [120, 120, 10] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [55357], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [55357, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [97], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [8230], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [55357], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [97, 32], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 32, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [97, 32], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 32, 55357] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [97], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [97, 32], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [233], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [120, 120], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [10] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 32 + ] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [120, 120], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [97], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 10] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [32], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [32] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [55357], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [55357, 8230] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [97, 32], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [10], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [97], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [120, 120], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": false, + "advance": true, + "inputValue": [120, 120, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 97, 98 + ] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [97, 32], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [97], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 97, 98 + ] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [32], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [32] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [97, 32], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 32, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [97, 32], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 97] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [97], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [97, 98], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 98, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [97], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [101], + "inputValue": [97, 32], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 101] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [233], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [97], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [10], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 10 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [32], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [97, 32], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [97, 98], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [120, 120], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [97, 98], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [97, 32], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [97, 98], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [55357], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [55357, 97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [97, 98], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [55357], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 55357 + ] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [97, 32], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [10], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [97, 98], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 8230] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [97, 32], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [10], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32], + "visualInputOverride": [97, 32, 10] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [55357], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [55357, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [55357], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [55357, 97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [55357], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [55357, 55357] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [120, 120], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 8230] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 32 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [120, 120], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [97, 32], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [97], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [97, 98], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [233], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 233 + ] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [97], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [101], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 101] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [120, 120], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [55357], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [55357, 97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [97, 98], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [55357], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [55357, 233] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [120, 120], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [120, 120], + "visualInputOverride": [120, 120, 97] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [97, 32], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 32, 10] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [120, 120], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 233] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [97, 98], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 233] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [97, 98], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [101], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [101], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [97, 32], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [97], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [97, 98], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 98, 32] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [101], + "inputValue": [97, 32], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [101], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 101] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [8230], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [101], + "inputValue": [97, 32], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 101] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [97], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [55357, 56832] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [97, 32], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [97], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [97], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 97] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [233], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [120, 120], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 8230] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [97, 98], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [97], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [97, 98], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [233], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 233] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [97], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [97, 98], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 55357, 56832] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [233] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [120, 120], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 55357, 56832] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [120, 120], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 55357, 56832] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [97], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "visualInputOverride": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 32 + ] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [32], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [32] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [233], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [233] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [97, 32], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 101] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [10], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [10] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [120, 120], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 97] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [120, 120], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [120, 120, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [97, 32], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 55357, 56832] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [97, 32], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [233], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [97, 98], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 98, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [97, 32], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 55357, 56832 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [101], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [101], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [97, 98], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 97] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [97, 98], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 97] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 32 + ] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [97], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [8230], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 8230] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [97, 98], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [8230], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 8230] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [97, 98], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [97, 98], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [97], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [10], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [10] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [8230], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [8230] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [97, 32], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [101], + "inputValue": [120, 120], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [8230], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [8230] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [97], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [55357], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [55357, 97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [97], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 233] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [120, 120], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [120, 120], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [120, 120], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [55357], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [55357, 233] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [55357], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [97, 98], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [233], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 32 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 97, 98 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [97, 98], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [97], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [120, 120], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [120, 120], + "visualInputOverride": [120, 120, 97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [101], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 101 + ] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [101], + "inputValue": [97, 98], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [101], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 101] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [97, 32], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 55357] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [55357], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [97, 98], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 98], + "visualInputOverride": [97, 98, 10] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [233] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [97, 32], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 32, 10] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [233], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [233] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [55357, 56832] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [97], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 32 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [97, 98], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [8230], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [8230], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [97, 98], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [32], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [55357], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 97, 98 + ] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [97], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [120, 120], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [97, 98], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 98, 32] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [97], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97], + "visualInputOverride": [97, 8230] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [101], + "inputValue": [55357], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [101], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [97, 98], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 101] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [97], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 101] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [97, 32], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [97, 32], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 55357] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [55357], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [55357, 233] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [120, 120], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [120, 120, 32] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [97, 32], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [8230], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 8230 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [233], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [233] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [120, 120], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [8230], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 8230 + ] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [55357], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [55357, 233] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [97, 98], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [10], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [1077], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [1077] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [55357], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [10], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [55357, 10] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [55357], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [55357, 55357] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [55357], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [10], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [10] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [97, 98], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [55357], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [97], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357], + "correct": false, + "stopped": false, + "advance": true, + "inputValue": [97, 55357] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [97, 32], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 55357] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [120, 120], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 97] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [97], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 8230] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [101], + "inputValue": [120, 120], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [97], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [97, 32], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [8230], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [101], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 101 + ] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [55357], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [55357, 55357, 56832] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [97, 32], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [97, 32], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [32], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 10 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [97, 32], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [97], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 97 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [97], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 10] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [120, 120], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [97, 32], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 32, 233] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [97, 98], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 55357, 56832] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [55357], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [10], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [55357, 10] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [97], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [55357], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 55357 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [55357], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [55357, 8230] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [55357], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [55357, 233] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [97, 98], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 98, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [55357], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [97, 32], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 97] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [55357], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [55357, 55357] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [120, 120], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [233], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 233 + ] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [97, 98], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [97], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [233], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 233] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [97], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [97, 98], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [55357], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [55357, 97] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [97, 98], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 55357, 56832] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [97, 98], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 97] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [55357], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [97], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [97], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [55357], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [120, 120], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 55357] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [10], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [10] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [97], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 55357, 56832] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [120, 120], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [233], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [120, 120], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [233], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [97], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 97] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [97], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 97 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [97, 98], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [120, 120], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [32], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [32] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [97], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 55357, 56832] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [55357], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [97, 98], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 55357, 56832] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [97], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [32] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 97, 98 + ] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [97, 32], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 97] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [97, 98], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [101], + "inputValue": [97], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 101] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [8230], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 8230 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [97, 32], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 32, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [97, 32], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 101] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [97, 32], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 97] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [97, 32], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 97] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [8230] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [10] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [97, 32], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 233] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [120, 120], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 55357] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [233], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [233] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [120, 120], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [10], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [55357], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [55357, 55357] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [97, 98], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [97, 98], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 8230] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [97, 32], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32], + "visualInputOverride": [97, 32, 55357, 56832] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [97], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [101], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 101 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [120, 120], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [97, 98], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [55357], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [55357, 55357, 56832] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [55357], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [233] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [32] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [55357], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [32], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [55357, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [97], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 10] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [32] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [97, 98], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 55357, 56832] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [120, 120], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 55357] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [55357], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 32 + ] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [8230], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 8230 + ] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [97], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "visualInputOverride": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 55357, 56832 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [55357, 56832] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [101], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [101], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 101 + ] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [97, 32], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 233] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [97, 32], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [8230], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 8230] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [97, 32], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [97, 98], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [97, 98], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 55357, 56832] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [8230], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [97], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [55357], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [97, 32], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [233], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 233] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [97], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [55357], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [55357, 55357, 56832] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [10], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [97], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 97 + ] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [97], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 97 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [97], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [97, 98], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [10], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 98, 10] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [55357], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [97, 98], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 98, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [8230], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [97, 98], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 98, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [120, 120], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 55357] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [97, 32], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [8230], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [97, 32], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [10], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 10] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [55357], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [97, 32], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [55357], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [55357], + "visualInputOverride": [55357, 32] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [101], + "inputValue": [55357], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [101], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [120, 120], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [8230], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "visualInputOverride": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 8230 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [55357], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [55357, 97] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [97, 32], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [120, 120], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [120, 120, 10] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [120, 120], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [10], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [97], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 233] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [97, 32], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [233], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [233] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [97, 98], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [55357], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [55357, 233] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [97], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [8230], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 8230] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [120, 120], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 55357, 56832] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [97, 98], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 98], + "visualInputOverride": [97, 98, 55357] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [8230], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 8230 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [55357], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [32] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [55357, 56832] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [55357], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [55357, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [120, 120], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 97] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 55357, 56832 + ] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [97, 98], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [97], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [55357], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [55357, 8230] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [55357], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [97, 98], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 98, 10] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [233] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [8230], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [97], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 97] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [97, 32], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 55357, 56832] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [101], + "inputValue": [97, 32], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [97], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [97, 98], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [55357], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [8230], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [55357, 8230] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [55357], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [55357, 55357, 56832] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [55357], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [97], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 10] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [101], + "inputValue": [97], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [8230], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [], + "visualInputOverride": [8230] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [97, 32], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [55357], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [97], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 233] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [55357], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [55357, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [8230], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [120, 120], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 233] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [97], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [97, 32], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 32, 233] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [97, 98], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 98, 55357] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [97, 98], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [97, 32], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 97] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [97, 32], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [55357], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [97], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [97, 98], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 55357, 56832] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [120, 120], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 97] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [97, 32], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [101], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [101], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [101], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 101 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [120, 120], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [120, 120, 10] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [97, 98], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 233] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [101], + "inputValue": [97, 32], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32], + "visualInputOverride": [97, 32, 101] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [55357], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [97, 32], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [10], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 10] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [55357], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [55357, 10] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [97], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [233], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [97, 32], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [97], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 32 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [97], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [8230], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 8230 + ] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [55357], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": false, + "stopped": false, + "advance": true, + "inputValue": [55357, 8230] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [97, 32], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 97] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 32 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [32], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [55357], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [55357, 97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [97, 98], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 8230] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [101], + "inputValue": [97, 32], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [233], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [97, 32], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 32, 55357] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [97], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [101], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [101], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [32] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [97, 98], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 55357, 56832] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [55357], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [120, 120], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [32], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [55357], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [55357, 233] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [32] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [97, 98], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 98, 8230] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [101], + "inputValue": [55357], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [55357], + "visualInputOverride": [55357, 101] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [97, 98], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 233] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [55357], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 55357 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [32] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [8230], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 8230 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [97], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [8230], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 8230] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [97], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [97], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 55357, 56832] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [97], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [97], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 97] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [97], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [120, 120], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [120, 120, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [55357], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [55357, 101] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [97, 98], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 97 + ] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [97, 98], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [97], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 10] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 55357, 56832 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [55357], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [55357, 55357, 56832] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [32] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [97, 32], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [55357], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [55357, 97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [97], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [55357], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [55357, 97] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [101], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [101], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "visualInputOverride": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 101 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [233], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 233 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [120, 120], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [97, 32], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 55357] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [10], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 10 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [97, 98], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [], + "visualInputOverride": [32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [233], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [233] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [120, 120], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [97, 32], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [97, 32], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 233] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 97, 98 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [32], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [97], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [97, 32], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 32, 32] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [97], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 97] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [97, 32], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357], + "correct": false, + "stopped": false, + "advance": true, + "inputValue": [97, 32, 55357] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [10], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [10] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [97, 98], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 98], + "visualInputOverride": [97, 98, 97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [97, 98], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [97], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [97, 98], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [97, 98], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 55357, 56832] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [233], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 233 + ] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [97, 32], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [120, 120], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [120, 120, 10] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [55357], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [55357, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [97], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 233] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [97, 98], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 98, 10] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [101], + "inputValue": [97, 32], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [97, 98], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [97], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 55357] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [97], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [97], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 55357] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 32 + ] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [8230], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 8230 + ] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [97, 32], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 8230] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [97, 98], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 55357, 56832] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [32] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [], + "visualInputOverride": [8230] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 10 + ] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [97, 32], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [32] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [101] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [101], + "inputValue": [97, 98], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 101] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [101], + "inputValue": [97], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [97, 32], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [55357], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [120, 120], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 97] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [97, 98], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [97, 32], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [97], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 233] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [55357], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [55357, 8230] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [97, 98], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [97, 98], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 233] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [55357], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [55357, 97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [97], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [101], + "inputValue": [120, 120], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [97], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [120, 120], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 55357, 56832] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [8230], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [120, 120], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [97, 98], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [10], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 10 + ] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [97], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 55357] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [97], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 233] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [55357], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [101], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [55357], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [55357, 55357] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [120, 120], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [8230], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [], + "visualInputOverride": [8230] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [120, 120], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [120, 120], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [97], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 101] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [233], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [233] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [55357], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [55357, 55357] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [97], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 55357] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [97, 98], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [55357], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [120, 120], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [97, 32], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 32, 10] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [97, 32], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [233], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 233] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [97, 98], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 98, 32] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [8230], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [8230] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [10], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [10] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [97, 98], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 55357] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [97, 32], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [101], + "inputValue": [97], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [8230], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [8230] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [97], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [8230], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 8230 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [97, 32], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [97], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [97], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [97, 98], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [55357], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [101], + "inputValue": [], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [101], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [101] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [97, 98], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [97, 98], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [120, 120], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [120, 120], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 233] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [120, 120], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [10], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [120, 120, 10] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [55357], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [97], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [55357, 97] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [55357], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [55357, 8230] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [97], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [55357], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [8230], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [120, 120], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 233] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [97, 32], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [97, 32], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 8230] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [120, 120], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [55357], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [55357, 97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [97, 32], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 32, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [55357], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [97, 98], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [32], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [32] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 32 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [10] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [97, 98], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [97, 98], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 98, 10] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [101], + "inputValue": [97], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [101], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 101] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [97], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 233] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [97, 98], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [97, 98], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 98], + "visualInputOverride": [97, 98, 233] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [55357], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [55357, 55357] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [101], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 101 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [97], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97], + "visualInputOverride": [97, 55357] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [97, 98], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 98, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [97], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 10] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [97], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 55357] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [97], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 97] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [97, 32], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 32, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [97], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 97] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [97, 32], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 8230] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [233], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [233] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [97, 98], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 32 + ] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [55357], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [97, 98], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [55357], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [233], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [55357, 233] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [97, 32], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [55357], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [55357, 55357, 56832] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [97], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [10], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 10] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [97], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [97, 98], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 97] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [8230], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [97], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [32], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [120, 120], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [97, 98], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 55357] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [97, 98], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [8230], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [97, 32], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [97, 98], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 98, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [97, 32], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [101], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [97, 98], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 98, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [97, 32], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [97, 32], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [55357], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [55357, 55357, 56832] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [97], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [10], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [97, 32], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 233] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [120, 120], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 233] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [97, 32], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [120, 120], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [120, 120, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [55357], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [97], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [55357, 97] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [55357, 56832] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [233], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [], + "visualInputOverride": [233] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [10], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "visualInputOverride": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 10 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [120, 120], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [120, 120, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [55357], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [55357, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [101], + "inputValue": [97, 98], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 98, 101] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [97], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 233] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [120, 120], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [97, 98], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 97] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [97, 32], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 55357, 56832] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [97], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [101], + "inputValue": [97], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [101], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 10 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [97, 32], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [97, 32], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [233], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32], + "visualInputOverride": [97, 32, 233] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [8230], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [97, 32], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [120, 120], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [120, 120, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [55357], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [55357, 8230] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [97], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [97, 32], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [120, 120], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [10], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [97, 32], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [97], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97], + "visualInputOverride": [97, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [97, 32], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 32, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [120, 120], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [10], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [120, 120], + "visualInputOverride": [120, 120, 10] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [97, 98], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [10], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [10] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [55357], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [8230], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [55357], + "visualInputOverride": [55357, 8230] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [120, 120], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [55357], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 55357 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [97, 98], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 55357, 56832] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [97, 32], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 101] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": false, + "advance": true, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 32 + ] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [120, 120], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 55357, 56832] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [97], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 8230] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [55357], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [55357, 97] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [97], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 97 + ] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [97, 98], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 55357] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [97, 32], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 101] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [97, 32], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [10], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [55357], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [10], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [97], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [120, 120], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [32] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [120, 120], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [97, 98], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 98, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [97], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 101] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [8230], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 8230 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [55357], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [55357, 55357] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [97, 98], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [10], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 10] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [55357], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 55357 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [97, 98], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 55357, 56832 + ] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [120, 120], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [97], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 97] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [97, 98], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 97] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [55357], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [55357, 8230] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [97, 32], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [], + "visualInputOverride": [32] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [233], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [101], + "inputValue": [120, 120], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [97, 98], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 98, 55357] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [120, 120], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": false, + "advance": true, + "inputValue": [120, 120, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [55357], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [97, 32], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [97], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [97], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [120, 120], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [120, 120], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [32], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 32] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [32] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [97], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 97] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 32 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [97, 98], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [55357], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [55357, 32] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [233], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [97, 98], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 98, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [55357], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [55357], + "visualInputOverride": [55357, 101] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [97, 32], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [120, 120], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 97] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [55357], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [55357, 10] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [97, 98], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 55357] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [97, 98], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [8230], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 8230] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [97, 98], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 97] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [120, 120], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [97, 98], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 98, 101] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [55357], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 55357 + ] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [97], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [97, 32], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 8230] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [120, 120], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [120, 120, 32] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [101], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [101], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 101 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [120, 120], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 97, 98 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [97, 98], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 97] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [97], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [120, 120], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [97], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [55357], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [55357, 97] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [97], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [55357], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 32 + ] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [55357], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [55357, 233] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [10], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [55357], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [55357, 233] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [55357], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [55357, 97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [10] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [97], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [55357], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [233], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [55357, 233] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [97, 32], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32], + "visualInputOverride": [97, 32, 55357, 56832] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [55357], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [55357, 8230] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [120, 120], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [97], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 97] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [101], + "inputValue": [55357], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [55357, 101] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [97], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [8230], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 8230] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [97, 32], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 32, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [97, 32], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [101], + "inputValue": [97], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [97, 32], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 97] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [97], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [233], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 233] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [55357], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [120, 120], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 55357] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [32], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [97], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [97], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 233] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [55357], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [97], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [55357], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [55357, 97] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [32], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [97, 98], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 55357] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [97, 32], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 233] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [97], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [97], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 97] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [120, 120], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 233] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [8230], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [97, 98], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [55357], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [55357, 55357, 56832] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [97], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [97], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [55357], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [10], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [97], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 97 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [120, 120], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 55357, 56832] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [97, 98], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 101] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [97, 32], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [101], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [101], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 101 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [97, 32], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [97, 98], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 98, 10] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [233], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [233] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [97, 32], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [97], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 97] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [120, 120], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [55357], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [55357, 8230] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [97], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 97] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [8230], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [8230] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [97, 98], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 98, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [97], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [55357], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [55357, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [120, 120], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [97], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [97], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 97] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [233], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [101], + "inputValue": [], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [101], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [233], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [97], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [8230], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 8230 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [97, 32], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [55357], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [55357, 55357] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 97, 98 + ] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [120, 120], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [10], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 10] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [97, 98], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 97] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [97], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 55357] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [55357], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 32 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 55357, 56832 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [97, 32], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [101], + "inputValue": [120, 120], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 101] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [97, 98], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [10], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 10] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [55357], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [55357, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [120, 120], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [97, 32], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 101] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [120, 120], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 97] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [97], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 101] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [120, 120], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 55357] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 32 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [97, 32], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 97] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [97], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 55357] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [120, 120], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [120, 120, 8230] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [97], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [101], + "inputValue": [], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [101], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [101] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [97, 32], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": false, + "stopped": false, + "advance": true, + "inputValue": [97, 32, 10] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [55357], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [55357, 55357, 56832] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [97, 98], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 233] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [97], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97], + "visualInputOverride": [97, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [55357], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357], + "correct": false, + "stopped": false, + "advance": true, + "inputValue": [55357, 55357] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [97, 98], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [55357], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [97, 98], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 233] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 32 + ] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [55357], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [120, 120], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 55357, 56832] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [97, 32], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [55357], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [55357], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [97], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [233], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [97, 98], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 98, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [120, 120], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 8230] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [120, 120], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [120, 120, 10] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [55357], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [55357, 97] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [97], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [120, 120], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [55357], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [55357, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [97, 32], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [55357], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [97, 32], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 55357] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [55357], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [120, 120], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 55357] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [120, 120], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 233] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [55357], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [55357, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [97], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [101] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [120, 120], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [97, 32], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [10], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32], + "visualInputOverride": [97, 32, 10] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [97], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 233] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [97], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [10], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 10] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [97, 98], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 8230] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [97], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 55357] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [55357], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 55357 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [97, 32], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 32, 10] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [55357], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [55357], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [55357, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [8230], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [97, 32], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 32, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 10 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [97], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 101] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [120, 120], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 55357, 56832] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [97, 32], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [97], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [97], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 97] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [101], + "inputValue": [97, 32], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [101], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [101], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 101 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [55357], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [55357, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [55357], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [97, 32], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [233], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [97], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [97, 32], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 8230] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [97], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 32 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [97, 98], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 97] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [120, 120], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [55357], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 55357, 56832 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [97], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [97, 32], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 233] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [97, 98], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [233], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [55357], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [97, 32], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32], + "visualInputOverride": [97, 32, 97] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [97, 32], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [97], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 101] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [97, 32], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [97, 32], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32], + "visualInputOverride": [97, 32, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [97, 32], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [120, 120], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [120, 120, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [120, 120], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [97, 98], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 98, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [55357], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [55357, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [97, 98], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 8230] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [101], + "inputValue": [120, 120], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 101] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [97, 98], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [120, 120], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [10], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [97], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [101], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [97, 98], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 98], + "visualInputOverride": [97, 98, 233] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [55357], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "visualInputOverride": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 55357 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [97, 32], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [97, 32], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [97], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 233] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [120, 120], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [120, 120], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [97], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [97], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 97 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [97, 32], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [32], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [55357], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [97], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [55357], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [55357, 97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [32], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [97], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [120, 120], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [97], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": false, + "stopped": false, + "advance": true, + "inputValue": [97, 10] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [101], + "inputValue": [97, 32], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 101] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 32 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [101], + "inputValue": [55357], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 55357, 56832 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [55357], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [55357, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [97], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [120, 120], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [97], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [120, 120], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [120, 120], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [97], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 101] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [97], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 101] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [97, 32], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [97, 32], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32], + "visualInputOverride": [97, 32, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [120, 120], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [10], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 10] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [120, 120], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [120, 120, 32] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [120, 120], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [97], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [97, 98], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [97], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [55357], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [55357, 97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [101], + "inputValue": [], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [101], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [101] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [101], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [101], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 101 + ] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [55357], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [55357, 10] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [97, 32], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 32, 233] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [97, 98], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 98, 32] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [55357], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [97], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [55357], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 32 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [55357], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [55357, 10] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [97, 32], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 55357, 56832] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [97, 98], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 55357] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [97], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 10] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [55357], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [97], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [101], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [101], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 101 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [120, 120], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 55357, 56832] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [97, 98], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 55357, 56832] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [97, 98], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [97, 98], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 97, 98 + ] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [120, 120], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [101], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [101], + "inputValue": [120, 120], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [101], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 101] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [97, 32], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [32], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 32] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "visualInputOverride": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 97, 98 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [8230], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [8230] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [97, 98], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 98, 8230] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [97, 98], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [97, 98], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 55357, 56832] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [97, 32], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 32, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [97], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [97, 32], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 8230] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [55357], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [55357, 10] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [97, 32], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [97, 98], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [97, 98], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [120, 120], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [55357], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [120, 120], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [97], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [120, 120], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 55357, 56832] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [97, 32], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [55357], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [55357], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [55357, 233] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [233] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 55357, 56832 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [55357], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 55357 + ] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [8230], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [97, 32], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 97] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [55357], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [55357, 55357, 56832] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [55357], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [55357], + "visualInputOverride": [55357, 55357] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [97, 98], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 8230] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [120, 120], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [10], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [97, 32], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [101], + "inputValue": [120, 120], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 101] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [97, 98], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 98], + "visualInputOverride": [97, 98, 97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [97], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [97, 32], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [97, 32], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 97, 98 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [32] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [55357], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [97, 32], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 32, 32] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [55357], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [32], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [55357, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [97], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [55357], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [55357, 10] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [8230], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [8230] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [97, 98], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [97, 32], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 32, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [101], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 101 + ] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [97, 32], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [32] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [32], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [97], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [101], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 101] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [97, 98], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [120, 120], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [97, 32], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [97, 32], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [97, 98], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [97], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [233], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 233] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [97, 32], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 55357] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 55357, 56832 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [97], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 55357, 56832] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [97, 98], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [97], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 97] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [101], + "inputValue": [97, 32], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [97, 98], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 98, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [97], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [55357], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [55357, 55357] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [97, 32], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [120, 120], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [97], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 97 + ] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [97, 98], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 55357, 56832] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [101], + "inputValue": [], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [101], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [101] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [55357], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [55357, 97] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [97], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [233], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 233] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [97], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [97], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 97, 98 + ] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [97], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 55357, 56832] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 97, 98 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [55357], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [8230], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [55357, 8230] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [97], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 55357, 56832] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [97], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [55357], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [120, 120], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 233] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [101], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [101] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [97], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [97, 32], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [120, 120], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 8230] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [55357], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [55357, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [55357, 56832] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [120, 120], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [120, 120, 101] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [101], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [101] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [8230], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 8230 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [120, 120], + "targetWord": [101, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [32], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [97, 98], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 98, 10] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [55357], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "visualInputOverride": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 32 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [120, 120], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [233], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [55357], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [97], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [97, 32], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 32, 10] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [55357], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [55357, 55357, 56832] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [97, 32], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [120, 120], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 55357] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [], + "visualInputOverride": [55357, 56832] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [97, 98], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [101], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 101] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [101], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 101 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [120, 120], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 55357] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [32], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [32] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [97, 32], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [120, 120], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 55357, 56832] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [97], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 55357] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [120, 120], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [233], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [55357], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [55357, 8230] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [55357], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [55357, 97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [10], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 10 + ] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [120, 120], + "targetWord": [32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [97], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 55357] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [97, 32], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [97], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 97] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [8230], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 8230 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [97], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [120, 120], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [120, 120, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [101], + "inputValue": [97, 32], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [101], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 101] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [97], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [], + "visualInputOverride": [55357, 56832] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [55357], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [55357], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 55357 + ] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [97, 32], + "targetWord": [97, 98, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter_hard", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [120, 120], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [32], + "inputValue": [55357], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [55357, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [120, 120], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [120, 120, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 97 + ] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [97, 98], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [101], + "inputValue": [97, 32], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [101], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 101] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [10], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [10] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [97], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 97, 98] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 97, 98 + ] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [97], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [55357], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [97, 32], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [12288], + "inputValue": [97, 32], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 32, 32] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [97, 32], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [97], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [10], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [10] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": false + }, + "input": { + "data": [233], + "inputValue": [120, 120], + "targetWord": [55357], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [97], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [55357], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [55357, 10] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [12288], + "inputValue": [120, 120], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [32], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [97], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [97, 10] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [120, 120], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 233] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [10], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [10] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [97], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [10], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 10] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [97, 32], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 32], + "visualInputOverride": [97, 32, 55357] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [120, 120], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 55357] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [55357, 56832], + "inputValue": [55357], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [120, 120], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 55357] + } + }, + { + "config": { + "mode": "zen", + "language": "english", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [10], + "inputValue": [97, 32], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [10], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_1k", + "freedomMode": false, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [97], + "inputValue": [55357], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [97], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [55357, 97] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "off", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [97, 98], + "targetWord": [1077, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 55357, 56832] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [97], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [32], + "inputValue": [], + "targetWord": [55357, 56832], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [32], + "correct": true, + "stopped": false, + "advance": true, + "inputValue": [32] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [55357], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [10], + "inputValue": [120, 120], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [10], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [120, 120] + } + }, + { + "config": { + "mode": "zen", + "language": "russian", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [97], + "targetWord": [101, 32], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 3, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 55357, 56832] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "off", + "stopOnError": "letter", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [55357], + "targetWord": [1077, 32], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [55357, 97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [], + "targetWord": [97], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [97], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97] + } + }, + { + "config": { + "mode": "words", + "language": "russian", + "freedomMode": true, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "off", + "strictSpace": false + }, + "input": { + "data": [55357], + "inputValue": [97, 98], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [8230], + "inputValue": [97], + "targetWord": [32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [97], + "visualInputOverride": [97, 8230] + } + }, + { + "config": { + "mode": "words", + "language": "russian_1k", + "freedomMode": true, + "difficulty": "master", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [120, 120], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [120, 120], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [120, 120, 97] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "off", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [97, 32], + "targetWord": [55357, 56832], + "nospace": false, + "correctShiftUsed": false, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 3, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [97, 32] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": false + }, + "input": { + "data": [97, 98], + "inputValue": [55357], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [55357, 97, 98] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": false, + "difficulty": "expert", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [55357], + "inputValue": [], + "targetWord": [97, 98, 10], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [] + } + }, + { + "config": { + "mode": "zen", + "language": "russian_phonetic", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [97], + "inputValue": [97], + "targetWord": [97, 98, 32], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [97], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [97], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 97] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "expert", + "blindMode": false, + "confidenceMode": "on", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": true + }, + "input": { + "data": [233], + "inputValue": [55357], + "targetWord": [97, 9], + "nospace": true, + "correctShiftUsed": false, + "hasNewline": true + }, + "state": { + "inputValue": [55357], + "wordIndex": 1, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [233], + "correct": true, + "stopped": true, + "advance": false, + "inputValue": [55357] + } + }, + { + "config": { + "mode": "zen", + "language": "dutch", + "freedomMode": true, + "difficulty": "master", + "blindMode": true, + "confidenceMode": "max", + "stopOnError": "off", + "deleteOnError": "letter", + "strictSpace": false + }, + "input": { + "data": [8230], + "inputValue": [97, 98], + "targetWord": [97, 9], + "nospace": false, + "correctShiftUsed": null, + "hasNewline": false + }, + "state": { + "inputValue": [97, 98], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [8230], + "correct": true, + "stopped": false, + "advance": false, + "inputValue": [97, 98, 8230] + } + }, + { + "config": { + "mode": "words", + "language": "russian_phonetic", + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "confidenceMode": "max", + "stopOnError": "word", + "deleteOnError": "letter_hard", + "strictSpace": true + }, + "input": { + "data": [97, 98], + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "targetWord": [97, 98, 10], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120 + ], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": true + }, + "blocked": true, + "canDelete": false, + "decision": { + "data": [97, 98], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [ + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 97, 98 + ] + } + }, + { + "config": { + "mode": "words", + "language": "dutch", + "freedomMode": false, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "off", + "stopOnError": "word", + "deleteOnError": "word", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [97, 32], + "targetWord": [97], + "nospace": true, + "correctShiftUsed": true, + "hasNewline": false + }, + "state": { + "inputValue": [97, 32], + "wordIndex": 0, + "previousWordCorrect": false, + "previousWordAvailable": false + }, + "blocked": false, + "canDelete": true, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": false, + "advance": false, + "inputValue": [97, 32, 55357, 56832] + } + }, + { + "config": { + "mode": "words", + "language": "english", + "freedomMode": true, + "difficulty": "normal", + "blindMode": true, + "confidenceMode": "on", + "stopOnError": "letter", + "deleteOnError": "word_hard", + "strictSpace": true + }, + "input": { + "data": [55357, 56832], + "inputValue": [], + "targetWord": [55357], + "nospace": false, + "correctShiftUsed": true, + "hasNewline": true + }, + "state": { + "inputValue": [], + "wordIndex": 0, + "previousWordCorrect": true, + "previousWordAvailable": true + }, + "blocked": false, + "canDelete": false, + "decision": { + "data": [55357, 56832], + "correct": false, + "stopped": true, + "advance": false, + "inputValue": [] + } + } + ] +} From aee7d7a7be7cb45db801bfd9b1efa56c60c35bdb Mon Sep 17 00:00:00 2001 From: Lakshmi Tanmay Date: Fri, 23 Oct 2026 04:39:45 +0530 Subject: [PATCH 41/93] Record browser progression before multi-character difficulty failure --- frontend/e2e/engine-input-contract.migration.ts | 6 ++++++ frontend/src/features/typing/runtime.client.tsx | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/frontend/e2e/engine-input-contract.migration.ts b/frontend/e2e/engine-input-contract.migration.ts index 3290007e5..851fb235b 100644 --- a/frontend/e2e/engine-input-contract.migration.ts +++ b/frontend/e2e/engine-input-contract.migration.ts @@ -31,4 +31,10 @@ test("master failure waits for the final unit of a multi-character browser commi .join(""), ).toBe(batch); expect(inputs.at(-1)?.data.wordIndex).toBe(0); + const state = await page.evaluate(() => + ( + window as unknown as { engineMigrationState: () => { wordIndex: number } } + ).engineMigrationState(), + ); + expect(state.wordIndex).toBe(1); }); diff --git a/frontend/src/features/typing/runtime.client.tsx b/frontend/src/features/typing/runtime.client.tsx index 738b25c1b..6534df4d8 100644 --- a/frontend/src/features/typing/runtime.client.tsx +++ b/frontend/src/features/typing/runtime.client.tsx @@ -5,6 +5,7 @@ import { useQuoteEvents } from "../../ts/controllers/quotes-controller"; import { useSoundEvents } from "../../ts/controllers/sound-controller"; import { getLastEventLog, + getActiveWordIndex, initializeTypingState, useKeymapEvents, } from "../../ts/states/test"; @@ -38,6 +39,11 @@ export function TypingRuntime(): null { Result.toggleSmoothedBurst(); }, lastEventLog: getLastEventLog, + ...(import.meta.env.OXYTYPE_ENGINE_PROOF + ? { + engineMigrationState: () => ({ wordIndex: getActiveWordIndex() }), + } + : {}), currentEventLog: buildEventLog, }), ); From 0b4946887a03cb40003e50ff79fe71bbcf846ea6 Mon Sep 17 00:00:00 2001 From: Lakshmi Tanmay Date: Fri, 23 Oct 2026 04:40:35 +0530 Subject: [PATCH 42/93] Centralize input decisions, browser overflow preflight and batch checkpoints --- crates/typing-engine-wasm/src/lib.rs | 9 + .../typing-engine/src/bin/export-bindings.rs | 2 + crates/typing-engine/src/input.rs | 157 ++++++++++++++++ crates/typing-engine/src/lib.rs | 1 + crates/typing-engine/src/session.rs | 168 +++++++++++++----- .../typing-engine/tests/generated_session.rs | 4 +- crates/typing-engine/tests/input.rs | 124 +++++++++++++ docs/rust-engine/progress.md | 4 + packages/typing-engine/src/browser.client.ts | 12 +- 9 files changed, 431 insertions(+), 50 deletions(-) create mode 100644 crates/typing-engine/src/input.rs create mode 100644 crates/typing-engine/tests/input.rs diff --git a/crates/typing-engine-wasm/src/lib.rs b/crates/typing-engine-wasm/src/lib.rs index 8d386d654..cbcd8f8aa 100644 --- a/crates/typing-engine-wasm/src/lib.rs +++ b/crates/typing-engine-wasm/src/lib.rs @@ -37,6 +37,15 @@ impl WasmSession { .map_err(|message| JsError::new(&message))?; Ok(serde_json::to_string(&update)?) } + pub fn preflight(&self, data: &str, layout: &str) -> Result { + let data = serde_json::from_str(data)?; + let layout = serde_json::from_str(layout)?; + let session = self + .session + .as_ref() + .ok_or_else(|| JsError::new("Session disposed"))?; + Ok(serde_json::to_string(&session.preflight(data, &layout))?) + } pub fn snapshot(&self) -> Result { let session = self .session diff --git a/crates/typing-engine/src/bin/export-bindings.rs b/crates/typing-engine/src/bin/export-bindings.rs index d48a1b7a6..91ad3fefe 100644 --- a/crates/typing-engine/src/bin/export-bindings.rs +++ b/crates/typing-engine/src/bin/export-bindings.rs @@ -20,6 +20,8 @@ fn main() -> Result<(), Box> { Command::export_all(&config)?; Config::export_all(&config)?; State::export_all(&config)?; + typing_engine::session::Preflight::export_all(&config)?; + typing_engine::session::InsertionLayout::export_all(&config)?; typing_engine::session::Snapshot::export_all(&config)?; Update::export_all(&config)?; typing_engine::stats::Log::export_all(&config)?; diff --git a/crates/typing-engine/src/input.rs b/crates/typing-engine/src/input.rs new file mode 100644 index 000000000..e5e92662e --- /dev/null +++ b/crates/typing-engine/src/input.rs @@ -0,0 +1,157 @@ +use crate::{ + Config, Text, + text::{is_space, normalize}, +}; +use serde::{Deserialize, Serialize}; +use ts_rs::TS; +#[derive(Clone, Deserialize, Serialize, TS)] +#[serde(rename_all = "camelCase")] +pub struct Input { + pub data: Text, + pub input_value: Text, + pub target_word: Text, + pub nospace: bool, + pub correct_shift_used: Option, +} +#[derive(Clone, Serialize, Deserialize, TS)] +#[serde(rename_all = "camelCase")] +pub struct Decision { + pub data: Text, + pub correct: bool, + pub stopped: bool, + pub advance: bool, + pub input_value: Text, + #[serde(skip_serializing_if = "Option::is_none")] + #[ts(optional)] + pub visual_input_override: Option, +} +fn space(data: &Text) -> bool { + data.len() == 1 && is_space(data.0[0]) +} +pub fn normalize_data(config: &Config, input: &Input) -> Text { + if input.data.len() == 1 { + Text(vec![normalize( + input.data.0[0], + input.target_word.0.get(input.input_value.len()).copied(), + &config.language, + )]) + } else { + input.data.clone() + } +} +#[derive(Clone, Copy, PartialEq, Eq)] +pub enum Commit { + Separator, + Nospace, +} +pub fn commit(input: &Input) -> Option { + if space(&input.data) || input.data.0 == [10] { + Some(Commit::Separator) + } else if input.nospace && input.input_value.len() + input.data.len() == input.target_word.len() + { + Some(Commit::Nospace) + } else { + None + } +} +pub fn should_advance(config: &Config, input: &Input) -> bool { + let Some(commit) = commit(input) else { + return false; + }; + if config.mode == "zen" { + return true; + } + if input.input_value.is_empty() + && commit == Commit::Separator + && (config.strict_space || config.difficulty != "normal") + { + return false; + } + let mut next = input.input_value.clone(); + next.0.extend_from_slice(&input.data.0); + next == input.target_word || (config.stop_on_error == "off" && config.delete_on_error == "off") +} +pub fn evaluate(config: &Config, input: &Input) -> Decision { + let data = normalize_data(config, input); + let input = Input { + data: data.clone(), + ..input.clone() + }; + let correct = config.mode == "zen" + || (input.correct_shift_used != Some(false) + && input.data.len() == 1 + && input.target_word.0.get(input.input_value.len()) == input.data.0.first()); + let stopped = + (config.stop_on_error == "letter" && !correct) || input.correct_shift_used == Some(false); + let mut next = input.input_value.clone(); + next.0.extend_from_slice(&data.0); + Decision { + data, + correct, + stopped, + advance: !stopped && should_advance(config, &input), + input_value: if stopped { + input.input_value.clone() + } else { + next.clone() + }, + visual_input_override: if stopped + && input.correct_shift_used != Some(false) + && !config.blind_mode + { + Some(next) + } else { + None + }, + } +} +pub fn blocked(config: &Config, input: &Input, has_newline: bool) -> bool { + if input.data.0 == [10] && !has_newline && config.mode != "zen" { + return true; + } + if space(&input.data) && input.nospace { + return true; + } + let data = normalize_data(config, input); + let hard = config.mode != "zen" && config.delete_on_error.contains("hard"); + if space(&data) + && input.input_value.is_empty() + && !config.strict_space + && config.difficulty == "normal" + && !hard + { + return true; + } + let limit = if config.mode == "zen" { + 30 + } else { + input.target_word.len() + 20 + }; + input.input_value.len() >= limit + && !should_advance( + config, + &Input { + data, + ..input.clone() + }, + ) +} +pub fn can_delete( + config: &Config, + input: &Text, + index: usize, + previous_correct: bool, + previous_available: bool, +) -> bool { + let empty = input.is_empty(); + if empty && (index == 0 || !previous_available) { + return false; + } + if config.freedom_mode { + return true; + } + if config.confidence_mode == "max" { + return false; + } + !(empty && (config.confidence_mode == "on" || previous_correct)) +} diff --git a/crates/typing-engine/src/lib.rs b/crates/typing-engine/src/lib.rs index 500d772fb..dd17cffc0 100644 --- a/crates/typing-engine/src/lib.rs +++ b/crates/typing-engine/src/lib.rs @@ -3,6 +3,7 @@ pub mod analysis; pub mod funboxes; mod generated_funboxes; pub mod generation; +pub mod input; pub mod language_rules; pub mod pace; pub mod practice; diff --git a/crates/typing-engine/src/session.rs b/crates/typing-engine/src/session.rs index d8c90128e..99b9c6ba1 100644 --- a/crates/typing-engine/src/session.rs +++ b/crates/typing-engine/src/session.rs @@ -22,6 +22,7 @@ pub struct Config { pub freedom_mode: bool, pub confidence_mode: String, pub blind_mode: bool, + pub hide_extra_letters: bool, pub code_unindent_on_backspace: bool, pub funbox: Vec, pub show_all_lines: bool, @@ -54,6 +55,7 @@ impl Default for Config { freedom_mode: false, confidence_mode: "off".into(), blind_mode: false, + hide_extra_letters: false, code_unindent_on_backspace: false, funbox: vec![], show_all_lines: false, @@ -171,6 +173,9 @@ pub enum Command { automatic: Option, #[serde(default)] is_composition_ending: Option, + #[serde(default)] + #[ts(optional)] + layout: Option, }, #[serde(rename_all = "camelCase")] Delete { @@ -252,6 +257,20 @@ pub enum Command { }, } +#[derive(Clone, Debug, Default, Serialize, Deserialize, TS)] +#[serde(default, rename_all = "camelCase")] +pub struct InsertionLayout { + pub slow_timer: bool, + pub wrapped: bool, + pub final_in_batch: Option, +} +#[derive(Serialize, TS)] +#[serde(rename_all = "camelCase")] +pub struct Preflight { + pub blocked: bool, + pub measure_overflow: bool, + pub normalized: Text, +} #[derive(Clone, Debug, Serialize, Deserialize, TS)] #[serde(rename_all = "camelCase")] pub struct State { @@ -283,6 +302,12 @@ pub struct Update { pub words: Vec, #[serde(rename = "logInvalidated")] pub log_invalidated: bool, + #[serde( + rename = "visualInputOverride", + skip_serializing_if = "Option::is_none" + )] + #[ts(optional)] + pub visual_input_override: Option, } #[derive(Clone, Serialize, TS)] #[serde(rename_all = "camelCase")] @@ -337,6 +362,7 @@ pub struct Session { sections: Vec, word_changes: Vec, bailed_out: bool, + visual_input_override: Option, } impl Session { @@ -378,6 +404,7 @@ impl Session { sections, word_changes: vec![], bailed_out: false, + visual_input_override: None, }) } @@ -402,6 +429,43 @@ impl Session { session.word_changes.clear(); Ok(session) } + pub fn preflight(&self, data: Text, layout: &InsertionLayout) -> Preflight { + let target = self.target(); + let input = self.current(); + let observation = crate::input::Input { + data, + input_value: input.clone(), + target_word: target.clone(), + nospace: self.nospace(), + correct_shift_used: None, + }; + let normalized = crate::input::normalize_data(&self.config, &observation); + let observation = crate::input::Input { + data: normalized.clone(), + ..observation + }; + let blocked = self.ended + || crate::input::blocked( + &self.config, + &observation, + self.words.iter().any(|word| word.0.contains(&10)), + ); + let display_length = target.len() - usize::from(target.0.last() == Some(&32)); + let measure_overflow = !blocked + && !layout.slow_timer + && observation.data.len() == 1 + && !self.config.blind_mode + && !self.config.hide_extra_letters + && !self.config.delete_on_error.contains("hard") + && input.len() >= display_length + && !crate::input::should_advance(&self.config, &observation) + && self.config.mode != "zen"; + Preflight { + blocked: blocked || (measure_overflow && layout.wrapped), + measure_overflow, + normalized, + } + } pub fn snapshot(&self) -> Snapshot { Snapshot { state: self.state(), @@ -559,6 +623,7 @@ impl Session { let previous_origin = self.origin(); let invalidated = matches!(command, Command::Finalize { .. } | Command::Reset { .. }); self.word_changes.clear(); + self.visual_input_override = None; match command { Command::Insert { data, @@ -567,10 +632,23 @@ impl Session { correct_shift_used, automatic, is_composition_ending, + layout, } => { if is_composition_ending == Some(true) { self.composing = false; } + let layout = layout.unwrap_or_default(); + if self.preflight(data.clone(), &layout).blocked { + return Ok(Update { + state: self.state(), + events: vec![], + generation: self.generation, + automatic_pending: !self.automatic.is_empty(), + words: vec![], + log_invalidated: false, + visual_input_override: None, + }); + } let length = data.len(); for (index, unit) in data.0.into_iter().enumerate() { if self.ended { @@ -578,7 +656,9 @@ impl Session { } // The browser checks failure/finish at the final unit of a // committed input batch, not after each surrogate/character. - self.completion_checkpoint = index + 1 == length && !self.composing; + self.completion_checkpoint = index + 1 == length + && !self.composing + && layout.final_in_batch != Some(false); self.insert( unit, now, @@ -697,6 +777,7 @@ impl Session { automatic_pending: !self.automatic.is_empty(), words: self.word_changes.clone(), log_invalidated: invalidated || previous_origin != origin, + visual_input_override: self.visual_input_override.clone(), }) } @@ -738,13 +819,18 @@ impl Session { let input = self.current(); let target = self.target(); let nospace = self.nospace(); - if unit == 10 - && self.config.mode != "zen" - && !self.words.iter().any(|word| word.0.contains(&10)) - { - return; - } - if is_space(unit) && nospace { + let observation = crate::input::Input { + data: Text(vec![unit]), + input_value: input.clone(), + target_word: target.clone(), + nospace, + correct_shift_used: shift, + }; + if crate::input::blocked( + &self.config, + &observation, + self.words.iter().any(|word| word.0.contains(&10)), + ) { return; } let unit = normalize( @@ -763,49 +849,32 @@ impl Session { None }; if let Some(replacement) = replacement { - for unit in replacement.encode_utf16() { + let checkpoint = self.completion_checkpoint; + let replacement: Vec<_> = replacement.encode_utf16().collect(); + for (index, unit) in replacement.iter().enumerate() { + self.completion_checkpoint = checkpoint && index + 1 == replacement.len(); if !self.ended { - self.insert(unit, now, date, shift, automatic, composition); + self.insert(*unit, now, date, shift, automatic, composition); } } + self.completion_checkpoint = checkpoint; return; } - let commit = is_space(unit) || unit == 10 || (nospace && input.len() + 1 == target.len()); - if is_space(unit) - && input.is_empty() - && !self.config.strict_space - && self.config.difficulty == "normal" - && !self.config.delete_on_error.contains("hard") - { - return; - } - let mut next = input.clone(); - next.0.push(unit); - let word_correct = next == target; - let advance = commit - && (self.config.mode == "zen" - || (!(input.is_empty() - && !nospace - && (self.config.strict_space || self.config.difficulty != "normal")) - && (word_correct - || (self.config.stop_on_error == "off" - && self.config.delete_on_error == "off")))); - let limit = if self.config.mode == "zen" { - 30 - } else { - target.len() + 20 + let observation = crate::input::Input { + data: Text(vec![unit]), + ..observation }; - if input.len() >= limit && !advance { - return; - } + let commit = crate::input::commit(&observation).is_some(); + let decision = crate::input::evaluate(&self.config, &observation); + let mut raw_next = input.clone(); + raw_next.0.push(unit); + let word_correct = raw_next == target; + let next = decision.input_value; + self.visual_input_override = decision.visual_input_override; + let correct = decision.correct; + let stopped = decision.stopped; + let advance = decision.advance; self.start(now, date); - let correct = self.config.mode == "zen" - || (shift != Some(false) && target.0.get(input.len()) == Some(&unit)); - let stopped = (self.config.stop_on_error == "letter" && !correct) || shift == Some(false); - if stopped { - next = input.clone(); - } - let advance = advance && !stopped; let mut data = json!({"inputType":"insertText", "data":Text(vec![unit]), "correct":correct, "wordIndex":self.index, "charIndex":input.len(), "inputValue":next}); for (key, yes) in [ @@ -983,10 +1052,13 @@ impl Session { .cloned() .unwrap_or_default(); let previous_correct = self.index > 0 && self.words.get(self.index - 1) == Some(&prev); - if !self.config.freedom_mode - && (self.config.confidence_mode == "max" - || (input.is_empty() && (self.config.confidence_mode == "on" || previous_correct))) - { + if !crate::input::can_delete( + &self.config, + &input, + self.index, + previous_correct, + self.index > self.earliest_visible_word, + ) { return; } let after = observed.unwrap_or_else(|| { diff --git a/crates/typing-engine/tests/generated_session.rs b/crates/typing-engine/tests/generated_session.rs index 6a568a325..d4d99ef98 100644 --- a/crates/typing-engine/tests/generated_session.rs +++ b/crates/typing-engine/tests/generated_session.rs @@ -28,6 +28,7 @@ fn insert(session: &mut Session, data: Text, now: f64) -> typing_engine::session correct_shift_used: None, automatic: None, is_composition_ending: None, + layout: None, }) .unwrap() } @@ -180,7 +181,8 @@ fn invalid_configuration_and_overdue_work_leave_session_unchanged() { date: 0.0, correct_shift_used: None, automatic: None, - is_composition_ending: None + is_composition_ending: None, + layout: None, }) .is_err() ); diff --git a/crates/typing-engine/tests/input.rs b/crates/typing-engine/tests/input.rs new file mode 100644 index 000000000..f713293d6 --- /dev/null +++ b/crates/typing-engine/tests/input.rs @@ -0,0 +1,124 @@ +use serde_json::Value; +#[test] +fn input_decisions_match_frozen_typescript_matrix() { + let path = std::path::Path::new(env!("CARGO_MANIFEST_DIR")) + .join("../../packages/typing-fixtures/oracle/input-rules.json"); + let reference: Value = serde_json::from_slice(&std::fs::read(path).unwrap()).unwrap(); + for (index, case) in reference["cases"].as_array().unwrap().iter().enumerate() { + let mut config = reference["defaults"].as_object().unwrap().clone(); + config.extend(case["config"].as_object().unwrap().clone()); + let config = serde_json::from_value(Value::Object(config)).unwrap(); + let input = serde_json::from_value(case["input"].clone()).unwrap(); + let decision = typing_engine::input::evaluate(&config, &input); + assert_eq!( + serde_json::to_value(decision).unwrap(), + case["decision"], + "decision case {index}" + ); + assert_eq!( + typing_engine::input::blocked( + &config, + &input, + case["input"]["hasNewline"].as_bool().unwrap() + ), + case["blocked"].as_bool().unwrap(), + "blocked case {index}" + ); + let state = &case["state"]; + let input = serde_json::from_value(state["inputValue"].clone()).unwrap(); + assert_eq!( + typing_engine::input::can_delete( + &config, + &input, + state["wordIndex"].as_u64().unwrap() as usize, + state["previousWordCorrect"].as_bool().unwrap(), + state["previousWordAvailable"].as_bool().unwrap() + ), + case["canDelete"].as_bool().unwrap(), + "deletion case {index}" + ); + } +} + +#[test] +fn layout_preflight_controls_wrapping_without_host_rule_decisions() { + use typing_engine::{Command, Config, Session, Text, session::InsertionLayout}; + let mut session = Session::new( + Config { + mode: "words".into(), + words: 0, + ..Config::default() + }, + vec![Text::from("a ")], + ) + .unwrap(); + session + .apply( + serde_json::from_value(serde_json::json!({"op":"insert","data":[120],"now":0})) + .unwrap(), + ) + .unwrap(); + let layout = InsertionLayout { + wrapped: true, + ..InsertionLayout::default() + }; + let check = session.preflight(Text::from("x"), &layout); + assert!(check.measure_overflow && check.blocked); + let log = session.log(); + session + .apply( + serde_json::from_value( + serde_json::json!({"op":"insert","data":[120],"now":1,"layout":{"wrapped":true}}), + ) + .unwrap(), + ) + .unwrap(); + assert_eq!(log, session.log()); + let mut config = Config { + mode: "words".into(), + words: 0, + blind_mode: true, + ..Config::default() + }; + session + .apply(Command::Configure { + now: 2.0, + config: Box::new(config.clone()), + }) + .unwrap(); + assert!(!session.preflight(Text::from("x"), &layout).blocked); + config.blind_mode = false; + config.hide_extra_letters = true; + session + .apply(Command::Configure { + now: 3.0, + config: Box::new(config), + }) + .unwrap(); + assert!(!session.preflight(Text::from("x"), &layout).measure_overflow); +} +#[test] +fn explicit_batch_checkpoint_and_typography_expansion_preserve_full_commit() { + use typing_engine::{Config, Session, Text}; + let config = Config { + mode: "words".into(), + words: 2, + difficulty: "master".into(), + ..Config::default() + }; + let mut session = + Session::new(config.clone(), vec![Text::from("a "), Text::from("b")]).unwrap(); + session.apply(serde_json::from_value(serde_json::json!({"op":"insert","data":[120],"now":0,"layout":{"finalInBatch":false}})).unwrap()).unwrap(); + assert!(!session.state().ended); + session.apply(serde_json::from_value(serde_json::json!({"op":"insert","data":[121],"now":1,"layout":{"finalInBatch":true}})).unwrap()).unwrap(); + assert!(session.state().ended); + let mut session = Session::new(config, vec![Text::from("a "), Text::from("b")]).unwrap(); + session + .apply( + serde_json::from_value(serde_json::json!({"op":"insert","data":[8230],"now":0})) + .unwrap(), + ) + .unwrap(); + assert!(session.state().ended); + assert_eq!(session.state().input_value, Text::from("...")); +} diff --git a/docs/rust-engine/progress.md b/docs/rust-engine/progress.md index 5da49ade4..b951d3da3 100644 --- a/docs/rust-engine/progress.md +++ b/docs/rust-engine/progress.md @@ -104,3 +104,7 @@ do not rerun completed checks without a new reason. - New immutable funbox rule metadata and Rust compatibility/forced-config checks match all 2,304 ordered pairs and 768 setting cases captured once from TypeScript. A mechanical TypeScript guard checks metadata equality. Visual-only funboxes are accepted by generation; section funboxes remain an explicit missing-protocol error until the content lifecycle is implemented. - Optimized Cloudflare proof at ccb7db67f passes all 3 tests after Morse reduction/session additions; asset 753.41 kB raw / 285.44 kB gzip (Vite report), within frozen payload budgets. New funbox code has native verification only so far. + +- Input primitives now pass 2,048 deterministic TypeScript decision/blocking/deletion cases. New reference corpus factors identical config values into `defaults`; expansion was checked lossless before freezing (canonical expanded SHA256 0ea729f86f027243777e34ab66fbbba34fe466487772218d863a7a09264dd83a). No prior fixture changed. +- Rust preflight owns overflow eligibility/blocking and visual override policy; hosts provide measured wrapping. Explicit final-in-batch observation supports mechanical browser splitting; typography expansions now defer failure until their final unit. Native layout/batch tests added. +- Opt-in browser instrumentation proves master failure after a multi-character commit has already advanced website word index to 1. Rust still reports index 0 for this failure path. Must reconcile this known headless/web difference, including min-burst, before gate 1.4; never overwrite the old oracle to hide it. diff --git a/packages/typing-engine/src/browser.client.ts b/packages/typing-engine/src/browser.client.ts index be340a29c..08baa6ca6 100644 --- a/packages/typing-engine/src/browser.client.ts +++ b/packages/typing-engine/src/browser.client.ts @@ -16,9 +16,11 @@ import type { NextWord } from "../generated/types/NextWord"; import type { Quote } from "../generated/types/Quote"; import type { Query } from "../generated/types/Query"; import type { Response } from "../generated/types/Response"; +import type { Preflight } from "../generated/types/Preflight"; +import type { InsertionLayout } from "../generated/types/InsertionLayout"; import type { Snapshot } from "../generated/types/Snapshot"; import type { Log } from "../generated/types/Log"; -export type { Query, Response, Log, Snapshot }; +export type { Query, Response, Log, Snapshot, Preflight, InsertionLayout }; export type { Command, @@ -80,6 +82,14 @@ export class BrowserSession { } return JSON.parse(this.handle.command(JSON.stringify(command))) as Update; } + preflight(data: Text, layout: Partial = {}): Preflight { + if (this.handle === undefined) { + throw new Error("Typing engine session disposed"); + } + return JSON.parse( + this.handle.preflight(JSON.stringify(data), JSON.stringify(layout)), + ) as Preflight; + } snapshot(): Snapshot { if (this.handle === undefined) { throw new Error("Typing engine session disposed"); From 0f7f2c13c2e15ec7617c5da1425ee377bf95b5eb Mon Sep 17 00:00:00 2001 From: Lakshmi Tanmay Date: Fri, 23 Oct 2026 04:42:16 +0530 Subject: [PATCH 43/93] Match recorded browser progression before commit failure; retain headless reference --- crates/typing-engine/src/session.rs | 20 ++++++----- crates/typing-engine/tests/recordings.rs | 2 ++ crates/typing-engine/tests/slice.rs | 17 +++++++++- docs/rust-engine/decisions.md | 4 +++ docs/rust-engine/features.md | 2 +- docs/rust-engine/progress.md | 2 ++ .../e2e/engine-input-contract.migration.ts | 33 +++++++++++++++++++ frontend/e2e/engine-proof.migration.ts | 20 +++++++++-- .../browser-contract-overrides.json | 21 ++++++++++++ 9 files changed, 108 insertions(+), 13 deletions(-) create mode 100644 packages/typing-fixtures/browser-contract-overrides.json diff --git a/crates/typing-engine/src/session.rs b/crates/typing-engine/src/session.rs index 99b9c6ba1..a1f0fec7b 100644 --- a/crates/typing-engine/src/session.rs +++ b/crates/typing-engine/src/session.rs @@ -918,6 +918,13 @@ impl Session { self.end(now, date, Some("word generation error".into())); return; } + // The actual browser navigates before checking difficulty/minimum burst. + let committed_index = self.index; + let progressed = + advance && (self.index + 1 < self.words.len() || self.config.mode == "zen"); + if progressed { + self.index += 1; + } if self.completion_checkpoint && self.config.mode != "zen" && ((self.config.difficulty == "master" && !correct) @@ -929,14 +936,11 @@ impl Session { self.end(now, date, Some("difficulty".into())); return; } - if self.completion_checkpoint - && advance - && (self.index + 1 < self.words.len() || self.config.mode == "zen") - && self.config.min_burst != "off" - { + if self.completion_checkpoint && progressed && self.config.min_burst != "off" { let log: crate::stats::Log = serde_json::from_value(self.log()).expect("Engine log is valid"); - let burst = crate::stats::word_burst(&log, self.index, now - self.start.unwrap_or(now)); + let burst = + crate::stats::word_burst(&log, committed_index, now - self.start.unwrap_or(now)); let length = if self.config.mode == "zen" { input.len() + 1 } else { @@ -963,13 +967,11 @@ impl Session { && self.config.stop_on_error == "off" && self.config.delete_on_error == "off"; if self.completion_checkpoint - && self.index + 1 >= self.words.len() + && committed_index + 1 >= self.words.len() && finite && (word_correct || quick || advance) { self.end(now, date, None); - } else if advance && (self.index + 1 < self.words.len() || self.config.mode == "zen") { - self.index += 1; } if !self.ended && correct diff --git a/crates/typing-engine/tests/recordings.rs b/crates/typing-engine/tests/recordings.rs index 004914ec3..5c0589cad 100644 --- a/crates/typing-engine/tests/recordings.rs +++ b/crates/typing-engine/tests/recordings.rs @@ -149,6 +149,8 @@ fn master_failure_checks_final_unit_as_recorded_by_actual_browser_handler() { .filter(|e| e["type"] == "input") .collect(); assert_eq!(inputs.len(), 5); + assert_eq!(session.state().word_index, 1); + assert!(session.state().input_value.is_empty()); assert_eq!(session.state().reason.as_deref(), Some("difficulty")); assert!(session.state().ended); } diff --git a/crates/typing-engine/tests/slice.rs b/crates/typing-engine/tests/slice.rs index 814c3aa30..35c7459cc 100644 --- a/crates/typing-engine/tests/slice.rs +++ b/crates/typing-engine/tests/slice.rs @@ -54,10 +54,25 @@ fn finite_slice_matches_frozen_intermediate_oracle() { &std::fs::read(root.join(format!("commands/{name}.json"))).unwrap(), ) .unwrap(); - let expected: Value = serde_json::from_slice( + let mut expected: Value = serde_json::from_slice( &std::fs::read(root.join(format!("oracle/{name}.json"))).unwrap(), ) .unwrap(); + let differences: Vec = serde_json::from_slice( + &std::fs::read(root.join("browser-contract-overrides.json")).unwrap(), + ) + .unwrap(); + for difference in differences + .iter() + .filter(|difference| difference["fixture"] == name) + { + let frame = difference["frame"].as_u64().unwrap() as usize; + assert_eq!( + expected["frames"][frame]["state"], difference["headlessState"], + "Classified original must stay frozen" + ); + expected["frames"][frame]["state"] = difference["browserState"].clone(); + } let mut session = Session::new(fixture.config, fixture.words).unwrap(); let mut frames = vec![]; for command in fixture.commands { diff --git a/docs/rust-engine/decisions.md b/docs/rust-engine/decisions.md index 43ff5a83e..d1d05bbf7 100644 --- a/docs/rust-engine/decisions.md +++ b/docs/rust-engine/decisions.md @@ -22,3 +22,7 @@ Quote queues belong to an explicit `Quotes` instance; loading the same language Pseudolang retains UTF-16 prefix and JS property enumeration semantics. Its legacy unbounded loop is bounded at 65,536 draws with an explicit error; no random fallback. Individual source words and generation requests are bounded at 1,000,000 UTF-16 units/words. Hosts must surface failures; never silently alter generated content. `accents.json` and `british-english.json` are mechanical copies of existing GPL-3.0 project transformation tables, not regenerated reference snapshots. Morse uses a compact immutable mapping mechanically captured from the original Node 24 / Unicode 17.0 algorithm. The native exhaustive oracle covers every Unicode scalar and every isolated UTF-16 unit (1,182,044 output units, digest cbd60b424acd4b15). This preserves normalization behavior without bundling unrelated Unicode normalization tables. + +## Browser progression precedes commit failure + +Actual built-Cloudflare browser tests show both master and minimum-burst failures advance the word before finishing when the input commits it. The headless oracle checks failure first. Rust follows the website: advancement precedes failure checks, while burst is calculated for the committed word. `browser-contract-overrides.json` explicitly classifies the one existing min-burst frame whose state differs; its original bytes stay unchanged and tests assert the original state before comparing the separately recorded browser state. Input events and scores retain exact equality. No score rule changes. diff --git a/docs/rust-engine/features.md b/docs/rust-engine/features.md index dc91d4fbd..fcabf80c6 100644 --- a/docs/rust-engine/features.md +++ b/docs/rust-engine/features.md @@ -1,6 +1,6 @@ # Feature parity checklist -Every entry must pass before Stage 1 / web cutover. Pending means unported. +Every entry must pass before Stage 1 / web cutover. Pending means its complete acceptance gate remains open; partial native/browser evidence is tracked in progress.md. | Feature | Rust | Native / Wasm / web evidence | | --- | --- | --- | diff --git a/docs/rust-engine/progress.md b/docs/rust-engine/progress.md index b951d3da3..b17998063 100644 --- a/docs/rust-engine/progress.md +++ b/docs/rust-engine/progress.md @@ -108,3 +108,5 @@ do not rerun completed checks without a new reason. - Input primitives now pass 2,048 deterministic TypeScript decision/blocking/deletion cases. New reference corpus factors identical config values into `defaults`; expansion was checked lossless before freezing (canonical expanded SHA256 0ea729f86f027243777e34ab66fbbba34fe466487772218d863a7a09264dd83a). No prior fixture changed. - Rust preflight owns overflow eligibility/blocking and visual override policy; hosts provide measured wrapping. Explicit final-in-batch observation supports mechanical browser splitting; typography expansions now defer failure until their final unit. Native layout/batch tests added. - Opt-in browser instrumentation proves master failure after a multi-character commit has already advanced website word index to 1. Rust still reports index 0 for this failure path. Must reconcile this known headless/web difference, including min-burst, before gate 1.4; never overwrite the old oracle to hide it. + +- Reconciled commit-failure progression with the actual website. Built-browser master and minimum-burst tests both pass (word index advances before failure). Native tests pass against all 19 command fixtures; exactly one old headless min-burst frame is explicitly classified in `browser-contract-overrides.json`. Its original state/bytes are asserted unchanged before comparing the browser state. Events/scores retain exact equality. Current-head Wasm rerun pending. diff --git a/frontend/e2e/engine-input-contract.migration.ts b/frontend/e2e/engine-input-contract.migration.ts index 851fb235b..0fbf794e1 100644 --- a/frontend/e2e/engine-input-contract.migration.ts +++ b/frontend/e2e/engine-input-contract.migration.ts @@ -38,3 +38,36 @@ test("master failure waits for the final unit of a multi-character browser commi ); expect(state.wordIndex).toBe(1); }); + +test.describe("minimum burst progression", () => { + test.use({ + config: { + mode: "words", + words: 15, + difficulty: "normal", + minBurst: "fixed", + minBurstCustomSpeed: 1_000_000, + smoothCaret: "off", + }, + }); + test("advances before reporting a committed word's minimum burst failure", async ({ + page, + }) => { + await openTest(page); + const first = await page.locator("#words .word").first().textContent(); + if (first === null || first.length === 0) throw new Error("Missing target"); + await page.locator("#words").click(); + await page.keyboard.insertText(first[0] as string); + await page.waitForTimeout(100); + await page.keyboard.insertText(`${first.slice(1)} `); + await expect(page.locator("#result")).toBeVisible(); + const state = await page.evaluate(() => + ( + window as unknown as { + engineMigrationState: () => { wordIndex: number }; + } + ).engineMigrationState(), + ); + expect(state.wordIndex).toBe(1); + }); +}); diff --git a/frontend/e2e/engine-proof.migration.ts b/frontend/e2e/engine-proof.migration.ts index 01e92c598..17e319813 100644 --- a/frontend/e2e/engine-proof.migration.ts +++ b/frontend/e2e/engine-proof.migration.ts @@ -98,9 +98,25 @@ test("native fixture matches browser Wasm through built Cloudflare assets", asyn .fill(await readFile(resolve(root, "commands", name), "utf8")); await page.getByRole("button", { name: "Run fixture" }).click(); await expect(page.getByTestId("engine-proof-result")).not.toBeEmpty(); - const frozen: unknown = JSON.parse( + const frozen = JSON.parse( await readFile(resolve(root, "oracle", name), "utf8"), - ); + ) as { name: string; frames: { state: unknown }[] }; + const differences = JSON.parse( + await readFile(resolve(root, "browser-contract-overrides.json"), "utf8"), + ) as { + fixture: string; + frame: number; + headlessState: unknown; + browserState: unknown; + }[]; + for (const difference of differences.filter( + (item) => `${item.fixture}.json` === name, + )) { + const frame = frozen.frames[difference.frame]; + if (frame === undefined) throw new Error("Missing classified frame"); + expect(frame.state).toEqual(difference.headlessState); + frame.state = difference.browserState; + } expect( JSON.parse(await page.getByTestId("engine-proof-result").innerText()), name, diff --git a/packages/typing-fixtures/browser-contract-overrides.json b/packages/typing-fixtures/browser-contract-overrides.json new file mode 100644 index 000000000..476bd2845 --- /dev/null +++ b/packages/typing-fixtures/browser-contract-overrides.json @@ -0,0 +1,21 @@ +[ + { + "fixture": "min-burst", + "frame": 1, + "headlessState": { + "wordIndex": 0, + "inputValue": [111, 110, 101, 32], + "active": false, + "ended": true, + "reason": "min burst" + }, + "browserState": { + "wordIndex": 1, + "inputValue": [], + "active": false, + "ended": true, + "reason": "min burst" + }, + "evidence": "frontend/e2e/engine-input-contract.migration.ts: minimum burst progression; actual built Cloudflare browser advances before failure" + } +] From bb9aa9ff72a310103b840c24c5ef0a2776c657d1 Mon Sep 17 00:00:00 2001 From: Lakshmi Tanmay Date: Fri, 23 Oct 2026 04:47:22 +0530 Subject: [PATCH 44/93] Freeze poetry and extracted Wikipedia cleanup contracts --- .../scripts/capture-content-oracle.ts | 67 +++ packages/typing-fixtures/oracle/content.json | 455 ++++++++++++++++++ 2 files changed, 522 insertions(+) create mode 100644 packages/typing-core/scripts/capture-content-oracle.ts create mode 100644 packages/typing-fixtures/oracle/content.json diff --git a/packages/typing-core/scripts/capture-content-oracle.ts b/packages/typing-core/scripts/capture-content-oracle.ts new file mode 100644 index 000000000..bc3d5ec21 --- /dev/null +++ b/packages/typing-core/scripts/capture-content-oracle.ts @@ -0,0 +1,67 @@ +import { writeFileSync } from "node:fs"; +import { resolve } from "node:path"; +import { getPoem } from "../src/poetry"; +import { getSection } from "../src/wikipedia"; +import type { LanguageObject } from "@oxytype/schemas/languages"; +import { units } from "./rust-oracle"; +const cases = []; +for (const lines of [ + [" The— _cat sat.", "and naïve😀 words "], + ["_ —", "poem\tline", "“quote” …"], + Array.from({ length: 110 }, (_, i) => `word${i}`), +]) { + const result = await getPoem(async () => [ + { title: "Poem", author: "Author", lines }, + ]); + if (result === false) throw new Error("Poem fixture failed"); + cases.push({ + english: true, + source: { + kind: "poem", + title: units("Poem"), + author: units("Author"), + lines: lines.map(units), + }, + expected: { + title: units(result.title), + author: units(result.author), + words: result.words.map(units), + }, + }); +} +for (const language of ["en", "fr"]) { + for (const text of [ + " A[1] naïve…— “word” «end»\nNext\tline ", + "a\u200bb[20] c\ufeffd\u200ce\u200df", + "„hello᾽ \u1680world\r\nnext", + "Ångström\u00a0a\u2007b\u202fc — end", + ]) { + const result = await getSection("english", { + getLanguage: async () => ({ bcp47: language }) as LanguageObject, + fetchJson: async (url) => + url.includes("/w/api.php") + ? { query: { pages: { "1": { extract: "html" } } } } + : { title: "Article", author: "Writer", pageid: 1 }, + htmlToText: () => text, + }); + cases.push({ + english: language === "en", + source: { + kind: "article", + title: units("Article"), + author: units("Writer"), + plainText: units(text), + }, + expected: { + title: units(result.title), + author: units(result.author), + words: result.words.map(units), + }, + }); + } +} +writeFileSync( + resolve(import.meta.dirname, "../../typing-fixtures/oracle/content.json"), + `${JSON.stringify(cases, null, 2)}\n`, + { flag: "wx" }, +); diff --git a/packages/typing-fixtures/oracle/content.json b/packages/typing-fixtures/oracle/content.json new file mode 100644 index 000000000..5c186974c --- /dev/null +++ b/packages/typing-fixtures/oracle/content.json @@ -0,0 +1,455 @@ +[ + { + "english": true, + "source": { + "kind": "poem", + "title": [80, 111, 101, 109], + "author": [65, 117, 116, 104, 111, 114], + "lines": [ + [ + 32, 32, 84, 104, 101, 8212, 32, 95, 99, 97, 116, 32, 32, 115, 97, 116, + 46 + ], + [ + 97, 110, 100, 32, 110, 97, 239, 118, 101, 55357, 56832, 32, 119, 111, + 114, 100, 115, 32, 32 + ] + ] + }, + "expected": { + "title": [80, 111, 101, 109], + "author": [65, 117, 116, 104, 111, 114], + "words": [ + [84, 104, 101], + [99, 97, 116], + [115, 97, 116, 46], + [97, 110, 100], + [110, 97, 239, 118, 101, 55357, 56832], + [119, 111, 114, 100, 115] + ] + } + }, + { + "english": true, + "source": { + "kind": "poem", + "title": [80, 111, 101, 109], + "author": [65, 117, 116, 104, 111, 114], + "lines": [ + [95, 32, 8212], + [112, 111, 101, 109, 9, 108, 105, 110, 101], + [8220, 113, 117, 111, 116, 101, 8221, 32, 8230] + ] + }, + "expected": { + "title": [80, 111, 101, 109], + "author": [65, 117, 116, 104, 111, 114], + "words": [ + [112, 111, 101, 109, 9, 108, 105, 110, 101], + [8220, 113, 117, 111, 116, 101, 8221], + [8230] + ] + } + }, + { + "english": true, + "source": { + "kind": "poem", + "title": [80, 111, 101, 109], + "author": [65, 117, 116, 104, 111, 114], + "lines": [ + [119, 111, 114, 100, 48], + [119, 111, 114, 100, 49], + [119, 111, 114, 100, 50], + [119, 111, 114, 100, 51], + [119, 111, 114, 100, 52], + [119, 111, 114, 100, 53], + [119, 111, 114, 100, 54], + [119, 111, 114, 100, 55], + [119, 111, 114, 100, 56], + [119, 111, 114, 100, 57], + [119, 111, 114, 100, 49, 48], + [119, 111, 114, 100, 49, 49], + [119, 111, 114, 100, 49, 50], + [119, 111, 114, 100, 49, 51], + [119, 111, 114, 100, 49, 52], + [119, 111, 114, 100, 49, 53], + [119, 111, 114, 100, 49, 54], + [119, 111, 114, 100, 49, 55], + [119, 111, 114, 100, 49, 56], + [119, 111, 114, 100, 49, 57], + [119, 111, 114, 100, 50, 48], + [119, 111, 114, 100, 50, 49], + [119, 111, 114, 100, 50, 50], + [119, 111, 114, 100, 50, 51], + [119, 111, 114, 100, 50, 52], + [119, 111, 114, 100, 50, 53], + [119, 111, 114, 100, 50, 54], + [119, 111, 114, 100, 50, 55], + [119, 111, 114, 100, 50, 56], + [119, 111, 114, 100, 50, 57], + [119, 111, 114, 100, 51, 48], + [119, 111, 114, 100, 51, 49], + [119, 111, 114, 100, 51, 50], + [119, 111, 114, 100, 51, 51], + [119, 111, 114, 100, 51, 52], + [119, 111, 114, 100, 51, 53], + [119, 111, 114, 100, 51, 54], + [119, 111, 114, 100, 51, 55], + [119, 111, 114, 100, 51, 56], + [119, 111, 114, 100, 51, 57], + [119, 111, 114, 100, 52, 48], + [119, 111, 114, 100, 52, 49], + [119, 111, 114, 100, 52, 50], + [119, 111, 114, 100, 52, 51], + [119, 111, 114, 100, 52, 52], + [119, 111, 114, 100, 52, 53], + [119, 111, 114, 100, 52, 54], + [119, 111, 114, 100, 52, 55], + [119, 111, 114, 100, 52, 56], + [119, 111, 114, 100, 52, 57], + [119, 111, 114, 100, 53, 48], + [119, 111, 114, 100, 53, 49], + [119, 111, 114, 100, 53, 50], + [119, 111, 114, 100, 53, 51], + [119, 111, 114, 100, 53, 52], + [119, 111, 114, 100, 53, 53], + [119, 111, 114, 100, 53, 54], + [119, 111, 114, 100, 53, 55], + [119, 111, 114, 100, 53, 56], + [119, 111, 114, 100, 53, 57], + [119, 111, 114, 100, 54, 48], + [119, 111, 114, 100, 54, 49], + [119, 111, 114, 100, 54, 50], + [119, 111, 114, 100, 54, 51], + [119, 111, 114, 100, 54, 52], + [119, 111, 114, 100, 54, 53], + [119, 111, 114, 100, 54, 54], + [119, 111, 114, 100, 54, 55], + [119, 111, 114, 100, 54, 56], + [119, 111, 114, 100, 54, 57], + [119, 111, 114, 100, 55, 48], + [119, 111, 114, 100, 55, 49], + [119, 111, 114, 100, 55, 50], + [119, 111, 114, 100, 55, 51], + [119, 111, 114, 100, 55, 52], + [119, 111, 114, 100, 55, 53], + [119, 111, 114, 100, 55, 54], + [119, 111, 114, 100, 55, 55], + [119, 111, 114, 100, 55, 56], + [119, 111, 114, 100, 55, 57], + [119, 111, 114, 100, 56, 48], + [119, 111, 114, 100, 56, 49], + [119, 111, 114, 100, 56, 50], + [119, 111, 114, 100, 56, 51], + [119, 111, 114, 100, 56, 52], + [119, 111, 114, 100, 56, 53], + [119, 111, 114, 100, 56, 54], + [119, 111, 114, 100, 56, 55], + [119, 111, 114, 100, 56, 56], + [119, 111, 114, 100, 56, 57], + [119, 111, 114, 100, 57, 48], + [119, 111, 114, 100, 57, 49], + [119, 111, 114, 100, 57, 50], + [119, 111, 114, 100, 57, 51], + [119, 111, 114, 100, 57, 52], + [119, 111, 114, 100, 57, 53], + [119, 111, 114, 100, 57, 54], + [119, 111, 114, 100, 57, 55], + [119, 111, 114, 100, 57, 56], + [119, 111, 114, 100, 57, 57], + [119, 111, 114, 100, 49, 48, 48], + [119, 111, 114, 100, 49, 48, 49], + [119, 111, 114, 100, 49, 48, 50], + [119, 111, 114, 100, 49, 48, 51], + [119, 111, 114, 100, 49, 48, 52], + [119, 111, 114, 100, 49, 48, 53], + [119, 111, 114, 100, 49, 48, 54], + [119, 111, 114, 100, 49, 48, 55], + [119, 111, 114, 100, 49, 48, 56], + [119, 111, 114, 100, 49, 48, 57] + ] + }, + "expected": { + "title": [80, 111, 101, 109], + "author": [65, 117, 116, 104, 111, 114], + "words": [ + [119, 111, 114, 100, 48], + [119, 111, 114, 100, 49], + [119, 111, 114, 100, 50], + [119, 111, 114, 100, 51], + [119, 111, 114, 100, 52], + [119, 111, 114, 100, 53], + [119, 111, 114, 100, 54], + [119, 111, 114, 100, 55], + [119, 111, 114, 100, 56], + [119, 111, 114, 100, 57], + [119, 111, 114, 100, 49, 48], + [119, 111, 114, 100, 49, 49], + [119, 111, 114, 100, 49, 50], + [119, 111, 114, 100, 49, 51], + [119, 111, 114, 100, 49, 52], + [119, 111, 114, 100, 49, 53], + [119, 111, 114, 100, 49, 54], + [119, 111, 114, 100, 49, 55], + [119, 111, 114, 100, 49, 56], + [119, 111, 114, 100, 49, 57], + [119, 111, 114, 100, 50, 48], + [119, 111, 114, 100, 50, 49], + [119, 111, 114, 100, 50, 50], + [119, 111, 114, 100, 50, 51], + [119, 111, 114, 100, 50, 52], + [119, 111, 114, 100, 50, 53], + [119, 111, 114, 100, 50, 54], + [119, 111, 114, 100, 50, 55], + [119, 111, 114, 100, 50, 56], + [119, 111, 114, 100, 50, 57], + [119, 111, 114, 100, 51, 48], + [119, 111, 114, 100, 51, 49], + [119, 111, 114, 100, 51, 50], + [119, 111, 114, 100, 51, 51], + [119, 111, 114, 100, 51, 52], + [119, 111, 114, 100, 51, 53], + [119, 111, 114, 100, 51, 54], + [119, 111, 114, 100, 51, 55], + [119, 111, 114, 100, 51, 56], + [119, 111, 114, 100, 51, 57], + [119, 111, 114, 100, 52, 48], + [119, 111, 114, 100, 52, 49], + [119, 111, 114, 100, 52, 50], + [119, 111, 114, 100, 52, 51], + [119, 111, 114, 100, 52, 52], + [119, 111, 114, 100, 52, 53], + [119, 111, 114, 100, 52, 54], + [119, 111, 114, 100, 52, 55], + [119, 111, 114, 100, 52, 56], + [119, 111, 114, 100, 52, 57], + [119, 111, 114, 100, 53, 48], + [119, 111, 114, 100, 53, 49], + [119, 111, 114, 100, 53, 50], + [119, 111, 114, 100, 53, 51], + [119, 111, 114, 100, 53, 52], + [119, 111, 114, 100, 53, 53], + [119, 111, 114, 100, 53, 54], + [119, 111, 114, 100, 53, 55], + [119, 111, 114, 100, 53, 56], + [119, 111, 114, 100, 53, 57], + [119, 111, 114, 100, 54, 48], + [119, 111, 114, 100, 54, 49], + [119, 111, 114, 100, 54, 50], + [119, 111, 114, 100, 54, 51], + [119, 111, 114, 100, 54, 52], + [119, 111, 114, 100, 54, 53], + [119, 111, 114, 100, 54, 54], + [119, 111, 114, 100, 54, 55], + [119, 111, 114, 100, 54, 56], + [119, 111, 114, 100, 54, 57], + [119, 111, 114, 100, 55, 48], + [119, 111, 114, 100, 55, 49], + [119, 111, 114, 100, 55, 50], + [119, 111, 114, 100, 55, 51], + [119, 111, 114, 100, 55, 52], + [119, 111, 114, 100, 55, 53], + [119, 111, 114, 100, 55, 54], + [119, 111, 114, 100, 55, 55], + [119, 111, 114, 100, 55, 56], + [119, 111, 114, 100, 55, 57], + [119, 111, 114, 100, 56, 48], + [119, 111, 114, 100, 56, 49], + [119, 111, 114, 100, 56, 50], + [119, 111, 114, 100, 56, 51], + [119, 111, 114, 100, 56, 52], + [119, 111, 114, 100, 56, 53], + [119, 111, 114, 100, 56, 54], + [119, 111, 114, 100, 56, 55], + [119, 111, 114, 100, 56, 56], + [119, 111, 114, 100, 56, 57], + [119, 111, 114, 100, 57, 48], + [119, 111, 114, 100, 57, 49], + [119, 111, 114, 100, 57, 50], + [119, 111, 114, 100, 57, 51], + [119, 111, 114, 100, 57, 52], + [119, 111, 114, 100, 57, 53], + [119, 111, 114, 100, 57, 54], + [119, 111, 114, 100, 57, 55], + [119, 111, 114, 100, 57, 56], + [119, 111, 114, 100, 57, 57] + ] + } + }, + { + "english": true, + "source": { + "kind": "article", + "title": [65, 114, 116, 105, 99, 108, 101], + "author": [87, 114, 105, 116, 101, 114], + "plainText": [ + 32, 32, 65, 91, 49, 93, 32, 110, 97, 239, 118, 101, 8230, 8212, 32, + 8220, 119, 111, 114, 100, 8221, 32, 171, 101, 110, 100, 187, 10, 78, + 101, 120, 116, 9, 108, 105, 110, 101, 32, 32 + ] + }, + "expected": { + "title": [65, 114, 116, 105, 99, 108, 101], + "author": [87, 114, 105, 116, 101, 114], + "words": [ + [65], + [110, 97, 118, 101, 46, 46, 46, 45], + [34, 119, 111, 114, 100, 34], + [60, 60, 101, 110, 100, 62, 62, 78, 101, 120, 116, 108, 105, 110, 101] + ] + } + }, + { + "english": true, + "source": { + "kind": "article", + "title": [65, 114, 116, 105, 99, 108, 101], + "author": [87, 114, 105, 116, 101, 114], + "plainText": [ + 97, 8203, 98, 91, 50, 48, 93, 32, 99, 65279, 100, 8204, 101, 8205, 102 + ] + }, + "expected": { + "title": [65, 114, 116, 105, 99, 108, 101], + "author": [87, 114, 105, 116, 101, 114], + "words": [ + [97, 98], + [99, 100, 101, 102] + ] + } + }, + { + "english": true, + "source": { + "kind": "article", + "title": [65, 114, 116, 105, 99, 108, 101], + "author": [87, 114, 105, 116, 101, 114], + "plainText": [ + 8222, 104, 101, 108, 108, 111, 8125, 32, 5760, 119, 111, 114, 108, 100, + 13, 10, 110, 101, 120, 116 + ] + }, + "expected": { + "title": [65, 114, 116, 105, 99, 108, 101], + "author": [87, 114, 105, 116, 101, 114], + "words": [ + [104, 101, 108, 108, 111], + [119, 111, 114, 108, 100, 110, 101, 120, 116] + ] + } + }, + { + "english": true, + "source": { + "kind": "article", + "title": [65, 114, 116, 105, 99, 108, 101], + "author": [87, 114, 105, 116, 101, 114], + "plainText": [ + 197, 110, 103, 115, 116, 114, 246, 109, 160, 97, 8199, 98, 8239, 99, 32, + 8212, 32, 101, 110, 100 + ] + }, + "expected": { + "title": [65, 114, 116, 105, 99, 108, 101], + "author": [87, 114, 105, 116, 101, 114], + "words": [ + [110, 103, 115, 116, 114, 109], + [97], + [98], + [99], + [45], + [101, 110, 100] + ] + } + }, + { + "english": false, + "source": { + "kind": "article", + "title": [65, 114, 116, 105, 99, 108, 101], + "author": [87, 114, 105, 116, 101, 114], + "plainText": [ + 32, 32, 65, 91, 49, 93, 32, 110, 97, 239, 118, 101, 8230, 8212, 32, + 8220, 119, 111, 114, 100, 8221, 32, 171, 101, 110, 100, 187, 10, 78, + 101, 120, 116, 9, 108, 105, 110, 101, 32, 32 + ] + }, + "expected": { + "title": [65, 114, 116, 105, 99, 108, 101], + "author": [87, 114, 105, 116, 101, 114], + "words": [ + [65], + [110, 97, 239, 118, 101, 46, 46, 46, 45], + [34, 119, 111, 114, 100, 34], + [60, 60, 101, 110, 100, 62, 62], + [78, 101, 120, 116], + [108, 105, 110, 101] + ] + } + }, + { + "english": false, + "source": { + "kind": "article", + "title": [65, 114, 116, 105, 99, 108, 101], + "author": [87, 114, 105, 116, 101, 114], + "plainText": [ + 97, 8203, 98, 91, 50, 48, 93, 32, 99, 65279, 100, 8204, 101, 8205, 102 + ] + }, + "expected": { + "title": [65, 114, 116, 105, 99, 108, 101], + "author": [87, 114, 105, 116, 101, 114], + "words": [ + [97, 98], + [99, 100, 101, 102] + ] + } + }, + { + "english": false, + "source": { + "kind": "article", + "title": [65, 114, 116, 105, 99, 108, 101], + "author": [87, 114, 105, 116, 101, 114], + "plainText": [ + 8222, 104, 101, 108, 108, 111, 8125, 32, 5760, 119, 111, 114, 108, 100, + 13, 10, 110, 101, 120, 116 + ] + }, + "expected": { + "title": [65, 114, 116, 105, 99, 108, 101], + "author": [87, 114, 105, 116, 101, 114], + "words": [ + [8222, 104, 101, 108, 108, 111, 8125], + [119, 111, 114, 108, 100], + [110, 101, 120, 116] + ] + } + }, + { + "english": false, + "source": { + "kind": "article", + "title": [65, 114, 116, 105, 99, 108, 101], + "author": [87, 114, 105, 116, 101, 114], + "plainText": [ + 197, 110, 103, 115, 116, 114, 246, 109, 160, 97, 8199, 98, 8239, 99, 32, + 8212, 32, 101, 110, 100 + ] + }, + "expected": { + "title": [65, 114, 116, 105, 99, 108, 101], + "author": [87, 114, 105, 116, 101, 114], + "words": [ + [197, 110, 103, 115, 116, 114, 246, 109], + [97], + [98], + [99], + [45], + [101, 110, 100] + ] + } + } +] From f44fa7d16ee6c3cff95aa6094b23df8510a9e297 Mon Sep 17 00:00:00 2001 From: Lakshmi Tanmay Date: Fri, 23 Oct 2026 04:47:24 +0530 Subject: [PATCH 45/93] Port section cleanup and bounded cancellable content-request ownership --- crates/typing-engine/src/content.rs | 227 ++++++++++++++++++++++++++ crates/typing-engine/src/lib.rs | 1 + crates/typing-engine/tests/content.rs | 66 ++++++++ docs/rust-engine/progress.md | 4 + 4 files changed, 298 insertions(+) create mode 100644 crates/typing-engine/src/content.rs create mode 100644 crates/typing-engine/tests/content.rs diff --git a/crates/typing-engine/src/content.rs b/crates/typing-engine/src/content.rs new file mode 100644 index 000000000..f3cbd28fd --- /dev/null +++ b/crates/typing-engine/src/content.rs @@ -0,0 +1,227 @@ +//! Host fetches and extracts HTML. Rust owns section cleanup and request ordering. +use crate::{Text, text::js_whitespace}; +use serde::{Deserialize, Serialize}; +use std::collections::VecDeque; +use ts_rs::TS; +#[derive(Clone, Debug, Deserialize, Serialize, TS)] +#[serde(tag = "kind", rename_all = "camelCase")] +pub enum Source { + Poem { + title: Text, + author: Text, + lines: Vec, + }, + #[serde(rename_all = "camelCase")] + Article { + title: Text, + author: Option, + plain_text: Text, + }, +} +#[derive(Clone, Debug, Deserialize, Serialize, TS)] +pub struct Section { + pub title: Text, + pub author: Option, + pub words: Vec, +} +impl Source { + pub fn kind(&self) -> &'static str { + match self { + Self::Poem { .. } => "poetry", + Self::Article { .. } => "wikipedia", + } + } + pub fn normalize(self, english: bool) -> Result { + match self { + Self::Poem { + title, + author, + lines, + } => { + if lines.len() > 1_000_000 || lines.iter().map(Text::len).sum::() > 5_000_000 + { + return Err("Poem exceeds content bound".into()); + } + let mut words = vec![]; + 'lines: for line in lines { + for word in line.0.split(|unit| *unit == 32) { + let word = Text( + word.iter() + .copied() + .filter(|unit| ![0x2014, 95, 32].contains(unit)) + .collect(), + ); + if word.is_empty() { + continue; + } + words.push(word); + if words.len() == 100 { + break 'lines; + } + } + } + Ok(Section { + title, + author: Some(author), + words, + }) + } + Self::Article { + title, + author, + plain_text, + } => { + if plain_text.len() > 5_000_000 { + return Err("Article exceeds content bound".into()); + } + let mut filtered = vec![]; + let mut index = 0; + while index < plain_text.len() { + let unit = plain_text.0[index]; + if unit == 91 { + let mut end = index + 1; + while plain_text + .0 + .get(end) + .is_some_and(|unit| (48..=57).contains(unit)) + { + end += 1; + } + if end > index + 1 && plain_text.0.get(end) == Some(&93) { + index = end + 1; + continue; + } + } + if ![0x200b, 0x200c, 0x200d, 0xfeff].contains(&unit) { + let replacement = match unit { + 0x201c | 0x201d => Some("\""), + 0x2019 | 0x2018 => Some("'"), + 0x2014 | 0x2013 => Some("-"), + 0x2026 => Some("..."), + 0xab => Some("<<"), + 0xbb => Some(">>"), + 0x2007 | 0x202f | 0xa0 => Some(" "), + _ => None, + }; + if let Some(value) = replacement { + filtered.extend(value.encode_utf16()); + } else { + filtered.push(unit); + } + } + index += 1; + } + if english { + filtered.retain(|unit| (0x20..=0x7e).contains(unit)); + } + let mut collapsed = vec![]; + for unit in filtered { + if js_whitespace(unit) { + if !collapsed.is_empty() && collapsed.last() != Some(&32) { + collapsed.push(32); + } + } else { + collapsed.push(unit); + } + } + if collapsed.last() == Some(&32) { + collapsed.pop(); + } + let words = if collapsed.is_empty() { + vec![] + } else { + collapsed + .split(|unit| *unit == 32) + .map(|word| Text(word.to_vec())) + .collect() + }; + Ok(Section { + title, + author, + words, + }) + } + } + } +} +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize, TS)] +#[serde(rename_all = "camelCase")] +pub struct Request { + pub id: u32, + pub generation: u32, + pub kind: String, + pub language: String, +} +/// One outstanding request, bounded prefetch; resets invalidate all prior tokens. +pub struct Queue { + kind: String, + language: String, + english: bool, + generation: u32, + next_id: u32, + pending: Option, + ready: VecDeque
, +} +impl Queue { + pub fn new(kind: String, language: String, english: bool) -> Self { + Self { + kind, + language, + english, + generation: 0, + next_id: 0, + pending: None, + ready: VecDeque::new(), + } + } + pub fn prefetch(&mut self, source: Source) -> Result<(), String> { + if source.kind() != self.kind { + return Err("Unexpected section source kind".into()); + } + if self.ready.len() >= 16 { + return Err("Section prefetch bound exceeded".into()); + } + let section = source.normalize(self.english)?; + if section.words.is_empty() { + return Err("Section has no words".into()); + } + self.ready.push_back(section); + Ok(()) + } + pub fn request(&mut self) -> Request { + if let Some(request) = &self.pending { + return request.clone(); + } + self.next_id = self.next_id.wrapping_add(1); + let request = Request { + id: self.next_id, + generation: self.generation, + kind: self.kind.clone(), + language: self.language.clone(), + }; + self.pending = Some(request.clone()); + request + } + pub fn supply(&mut self, request: &Request, source: Source) -> Result<(), String> { + if self.pending.as_ref() != Some(request) { + return Err("Stale or mismatched content request".into()); + } + self.prefetch(source)?; + self.pending = None; + Ok(()) + } + pub fn pop(&mut self) -> Option
{ + self.ready.pop_front() + } + pub fn is_empty(&self) -> bool { + self.ready.is_empty() + } + pub fn pending(&self) -> Option<&Request> { + self.pending.as_ref() + } + pub fn reset(&mut self, generation: u32) { + self.generation = generation; + self.pending = None; + self.ready.clear(); + } +} diff --git a/crates/typing-engine/src/lib.rs b/crates/typing-engine/src/lib.rs index dd17cffc0..45aaa544d 100644 --- a/crates/typing-engine/src/lib.rs +++ b/crates/typing-engine/src/lib.rs @@ -1,5 +1,6 @@ //! Platform-independent typing rules. Hosts provide content, observations and time. pub mod analysis; +pub mod content; pub mod funboxes; mod generated_funboxes; pub mod generation; diff --git a/crates/typing-engine/tests/content.rs b/crates/typing-engine/tests/content.rs new file mode 100644 index 000000000..2f3bf3fca --- /dev/null +++ b/crates/typing-engine/tests/content.rs @@ -0,0 +1,66 @@ +use serde_json::Value; +use typing_engine::{ + Text, + content::{Queue, Source}, +}; +#[test] +fn poetry_and_extracted_article_cleanup_match_unchanged_typescript() { + let path = std::path::Path::new(env!("CARGO_MANIFEST_DIR")) + .join("../../packages/typing-fixtures/oracle/content.json"); + let references: Vec = serde_json::from_slice(&std::fs::read(path).unwrap()).unwrap(); + for (index, reference) in references.into_iter().enumerate() { + let source: Source = serde_json::from_value(reference["source"].clone()).unwrap(); + let section = source + .normalize(reference["english"].as_bool().unwrap()) + .unwrap(); + assert_eq!( + serde_json::to_value(section).unwrap(), + reference["expected"], + "content case {index}" + ); + } +} +fn poem(word: &str) -> Source { + Source::Poem { + title: Text::from("Title"), + author: Text::from("Author"), + lines: vec![Text::from(word)], + } +} +#[test] +fn section_requests_are_owned_bounded_and_cancelled_by_generation() { + let mut queue = Queue::new("poetry".into(), "english".into(), true); + let first = queue.request(); + assert_eq!(queue.request(), first); + assert!( + queue + .supply( + &first, + Source::Article { + title: Text::default(), + author: None, + plain_text: Text::from("text") + } + ) + .is_err() + ); + assert_eq!(queue.pending(), Some(&first)); + queue.reset(1); + let second = queue.request(); + assert_ne!(first, second); + assert!(queue.supply(&first, poem("stale")).is_err()); + let mut forged = second.clone(); + forged.language = "french".into(); + assert!(queue.supply(&forged, poem("forged")).is_err()); + queue.supply(&second, poem("fresh")).unwrap(); + assert!(queue.pending().is_none()); + assert_eq!(queue.pop().unwrap().words, vec![Text::from("fresh")]); + for _ in 0..16 { + queue.prefetch(poem("word")).unwrap(); + } + assert!(queue.prefetch(poem("overflow")).is_err()); + for _ in 0..16 { + assert_eq!(queue.pop().unwrap().words, vec![Text::from("word")]); + } + assert!(queue.is_empty()); +} diff --git a/docs/rust-engine/progress.md b/docs/rust-engine/progress.md index b17998063..1051c4f35 100644 --- a/docs/rust-engine/progress.md +++ b/docs/rust-engine/progress.md @@ -110,3 +110,7 @@ do not rerun completed checks without a new reason. - Opt-in browser instrumentation proves master failure after a multi-character commit has already advanced website word index to 1. Rust still reports index 0 for this failure path. Must reconcile this known headless/web difference, including min-burst, before gate 1.4; never overwrite the old oracle to hide it. - Reconciled commit-failure progression with the actual website. Built-browser master and minimum-burst tests both pass (word index advances before failure). Native tests pass against all 19 command fixtures; exactly one old headless min-burst frame is explicitly classified in `browser-contract-overrides.json`. Its original state/bytes are asserted unchanged before comparing the browser state. Events/scores retain exact equality. Current-head Wasm rerun pending. + +- Current 0f7f2c13c Cloudflare build succeeds: 787.06 kB raw / 293.88 kB gzip (Vite), within payload budgets. Full browser proof and both actual-browser input-contract tests pass together: 5 tests, 19.7 seconds. + +- Poetry cleanup and extracted Wikipedia text normalization match 11 newly captured TypeScript cases. Rust owns cleanup; hosts retain network and HTML extraction. Bounded section queue tests cover one outstanding token, source-kind validation, 16-section prefetch, generation cancellation and rejection of stale/forged replies. These are not yet connected to generator/session continuation. From 0dac213412f66b22585202d7937dd2e7271e78a6 Mon Sep 17 00:00:00 2001 From: Lakshmi Tanmay Date: Fri, 23 Oct 2026 04:52:17 +0530 Subject: [PATCH 46/93] Resume initial section generation through typed content requests --- crates/typing-engine-wasm/src/lib.rs | 20 ++ .../typing-engine/src/bin/export-bindings.rs | 1 + crates/typing-engine/src/generation.rs | 179 ++++++++++++++---- crates/typing-engine/src/session.rs | 6 + crates/typing-engine/tests/content.rs | 112 +++++++++++ docs/rust-engine/progress.md | 3 + packages/typing-engine/src/browser.client.ts | 19 +- 7 files changed, 299 insertions(+), 41 deletions(-) diff --git a/crates/typing-engine-wasm/src/lib.rs b/crates/typing-engine-wasm/src/lib.rs index cbcd8f8aa..040e84733 100644 --- a/crates/typing-engine-wasm/src/lib.rs +++ b/crates/typing-engine-wasm/src/lib.rs @@ -143,6 +143,26 @@ impl WasmContent { &serde_json::json!({ "allWordsGenerated": generator.all_generated(), "currentQuote": generator.current_quote }), )?) } + pub fn request(&mut self) -> Result { + let generator = self + .content + .as_mut() + .ok_or_else(|| JsError::new("Content disposed"))? + .generator() + .map_err(|message| JsError::new(&message))?; + Ok(serde_json::to_string(&generator.pending_request())?) + } + pub fn supply(&mut self, request: &str, source: &str) -> Result<(), JsError> { + let request = serde_json::from_str(request)?; + let source = serde_json::from_str(source)?; + self.content + .as_mut() + .ok_or_else(|| JsError::new("Content disposed"))? + .generator() + .map_err(|message| JsError::new(&message))? + .supply(&request, source) + .map_err(|message| JsError::new(&message)) + } pub fn session(&mut self, repeating: bool) -> Result { let session = self .content diff --git a/crates/typing-engine/src/bin/export-bindings.rs b/crates/typing-engine/src/bin/export-bindings.rs index 91ad3fefe..dbc561241 100644 --- a/crates/typing-engine/src/bin/export-bindings.rs +++ b/crates/typing-engine/src/bin/export-bindings.rs @@ -20,6 +20,7 @@ fn main() -> Result<(), Box> { Command::export_all(&config)?; Config::export_all(&config)?; State::export_all(&config)?; + typing_engine::content::Request::export_all(&config)?; typing_engine::session::Preflight::export_all(&config)?; typing_engine::session::InsertionLayout::export_all(&config)?; typing_engine::session::Snapshot::export_all(&config)?; diff --git a/crates/typing-engine/src/generation.rs b/crates/typing-engine/src/generation.rs index 29618cfcb..ce85c7f4b 100644 --- a/crates/typing-engine/src/generation.rs +++ b/crates/typing-engine/src/generation.rs @@ -7,6 +7,7 @@ use ts_rs::TS; #[serde(default, rename_all = "camelCase")] pub struct Language { pub name: String, + pub bcp47: Option, pub words: Vec, pub right_to_left: Option, pub joining_script: bool, @@ -53,6 +54,14 @@ pub struct Assets { pub selected_quote_id: u32, pub favorite_quote_ids: Vec, pub polyglot: Vec, + pub sections: Vec, +} +struct Batch { + repeating: bool, + limit: usize, + words: Vec, + indexes: Vec, + raw: Vec, } pub struct Generator { config: GenerationConfig, @@ -76,6 +85,8 @@ pub struct Generator { pub current_quote: Option, polyglot: Vec, word_languages: std::collections::BTreeMap, usize>, + section_queue: Option, + batch: Option, } pub(crate) fn case(text: &Text, upper: bool) -> Text { let mut output = vec![]; @@ -156,9 +167,24 @@ impl Generator { assets: &mut Assets, ) -> Result { config.validate()?; - if crate::funboxes::has_function(&config.funbox, "pullSection") { - return Err("Section content protocol is not yet available".into()); - } + let section_queue = if let Some(name) = config.funbox.iter().find(|name| { + crate::funboxes::get(name).is_some_and(|metadata| { + metadata + .frontend_functions + .iter() + .any(|function| function == "pullSection") + }) + }) { + let english = language.bcp47.as_deref().unwrap_or("en").split('-').next() == Some("en"); + let mut queue = + crate::content::Queue::new(name.clone(), config.language.clone(), english); + for source in std::mem::take(&mut assets.sections) { + queue.prefetch(source)?; + } + Some(queue) + } else { + None + }; if config.mode == "custom" && custom.is_none() { return Err("Missing custom text".into()); } @@ -256,6 +282,8 @@ impl Generator { current_quote, polyglot, word_languages, + section_queue, + batch: None, }) } fn limit(&self) -> usize { @@ -372,62 +400,105 @@ impl Generator { } Ok(self.wordset[self.shuffled.pop().unwrap()].clone()) } + pub fn pending_request(&self) -> Option<&crate::content::Request> { + self.section_queue + .as_ref() + .and_then(|queue| queue.pending()) + } + pub fn supply( + &mut self, + request: &crate::content::Request, + source: crate::content::Source, + ) -> Result<(), String> { + self.section_queue + .as_mut() + .ok_or("Generator has no section source")? + .supply(request, source) + } + pub fn reset_requests(&mut self, generation: u32) { + if let Some(queue) = &mut self.section_queue { + queue.reset(generation); + } + self.batch = None; + } pub fn generate(&mut self, repeating: bool) -> Result { - self.repeating = repeating; - self.ordered = 0; - self.shuffled.clear(); - self.section.clear(); - self.section_index = 0; - self.section_history.clear(); - self.generated = 0; - if !repeating { - self.previous.clear(); - } - let limit = self.limit(); - if limit > 1_000_000 { - return Err("Generation request exceeds bound".into()); - } - let mut words = vec![]; - let mut indexes = vec![]; - let mut raw: Vec = vec![]; - if limit > 0 { + let mut batch = if let Some(batch) = self.batch.take() { + if batch.repeating != repeating { + self.batch = Some(batch); + return Err("Generation already awaiting content".into()); + } + batch + } else { + self.repeating = repeating; + self.ordered = 0; + self.shuffled.clear(); + self.section.clear(); + self.section_index = 0; + self.section_history.clear(); + self.generated = 0; + if !repeating { + self.previous.clear(); + } + let limit = self.limit(); + if limit > 1_000_000 { + return Err("Generation request exceeds bound".into()); + } + Batch { + repeating, + limit, + words: vec![], + indexes: vec![], + raw: vec![], + } + }; + if batch.limit > 0 { loop { - let next = self.next( - words.len(), - limit, - raw.last(), - raw.get(raw.len().wrapping_sub(2)), - )?; - indexes.push(next.section_index); - words.push(next.word.clone()); - raw.push(next.word_raw); + let next = match self.next( + batch.words.len(), + batch.limit, + batch.raw.last(), + batch.raw.get(batch.raw.len().wrapping_sub(2)), + ) { + Ok(next) => next, + Err(error) => { + if self.pending_request().is_some() { + self.batch = Some(batch); + } + return Err(error); + } + }; + batch.indexes.push(next.section_index); + batch.words.push(next.word); + batch.raw.push(next.word_raw); let sections = self.config.mode == "custom" && self .custom .as_ref() .is_some_and(|custom| custom.pipe_delimiter); if (sections - && ((self.section.is_empty() && self.section_index >= limit) - || words.len() >= 100)) - || (!sections && words.len() >= limit) + && ((self.section.is_empty() && self.section_index >= batch.limit) + || batch.words.len() >= 100)) + || (!sections && batch.words.len() >= batch.limit) { break; } } } - self.generated = words.len(); + self.generated = batch.words.len(); self.section_history.clear(); - let has_tab = words + let has_tab = batch + .words .iter() .chain(&self.wordset) .any(|word| word.0.contains(&9)); - let has_newline = words + let has_newline = batch + .words .iter() .chain(&self.wordset) .any(|word| word.0.contains(&10)); Ok(Generated { - words, - section_indexes: indexes, + words: batch.words, + section_indexes: batch.indexes, has_tab, has_newline, all_right_to_left: self.language.right_to_left, @@ -463,6 +534,13 @@ impl Generator { return Err("Repeated word is undefined".into()); } } + if self.section.is_empty() + && let Some(queue) = &mut self.section_queue + && queue.is_empty() + { + queue.request(); + return Err("Section content required".into()); + } let mut word = self.random_word()?; let previous_original = previous.cloned(); let previous = previous.map(|word| clean_previous(word, false)); @@ -507,6 +585,19 @@ impl Generator { retries += 1; word = self.random_word()?; } + } else if let Some(section) = self.section_queue.as_mut().and_then(|queue| queue.pop()) + { + let mut units = vec![]; + for (index, source_word) in section.words.into_iter().enumerate() { + if self.config.mode == "words" && index >= self.config.words as usize { + break; + } + if index > 0 { + units.push(32); + } + units.extend(source_word.0); + } + word = Text(units); } else { let mut retries = 0; let mut first = self.lazy(case( @@ -593,6 +684,7 @@ impl Generator { }); if self.config.mode != "custom" && self.config.mode != "quote" + && self.section_queue.is_none() && !self.config.punctuation && !self.config.funbox.iter().any(|name| { [ @@ -628,7 +720,10 @@ impl Generator { } word = Text(out); } - if self.config.punctuation && !self.language.original_punctuation { + if self.config.punctuation + && !self.language.original_punctuation + && self.section_queue.is_none() + { word = self.punctuation.apply( word, crate::punctuation::PunctuationContext { @@ -701,6 +796,8 @@ pub struct Prepare { pub selected_quote_id: u32, #[serde(default)] pub favorite_quote_ids: Vec, + #[serde(default)] + pub sections: Vec, } /// Retains only explicitly owned content-selection state across restarts. #[derive(Default)] @@ -715,6 +812,7 @@ impl Content { self.assets.polyglot = input.polyglot; self.assets.selected_quote_id = input.selected_quote_id; self.assets.favorite_quote_ids = input.favorite_quote_ids; + self.assets.sections = input.sections; self.generator = Some(Generator::with_assets( input.config, input.language, @@ -725,8 +823,9 @@ impl Content { Ok(()) } pub fn start_session(&mut self, repeating: bool) -> Result { + let generated = self.generator()?.generate(repeating)?; let generator = self.generator.take().ok_or("Content not prepared")?; - crate::Session::from_generator(generator, repeating) + crate::Session::from_generated(generator, generated) } pub fn generator(&mut self) -> Result<&mut Generator, String> { self.generator.as_mut().ok_or("Content not prepared".into()) diff --git a/crates/typing-engine/src/session.rs b/crates/typing-engine/src/session.rs index a1f0fec7b..c2535719e 100644 --- a/crates/typing-engine/src/session.rs +++ b/crates/typing-engine/src/session.rs @@ -413,6 +413,12 @@ impl Session { repeating: bool, ) -> Result { let generated = generator.generate(repeating)?; + Self::from_generated(generator, generated) + } + pub fn from_generated( + generator: crate::generation::Generator, + generated: crate::generation::Generated, + ) -> Result { let mut session = Self::new(generator.config().clone(), generated.words)?; session.sections = generated.section_indexes; session.custom_limit = generator.custom_limit(); diff --git a/crates/typing-engine/tests/content.rs b/crates/typing-engine/tests/content.rs index 2f3bf3fca..01fdc59e3 100644 --- a/crates/typing-engine/tests/content.rs +++ b/crates/typing-engine/tests/content.rs @@ -64,3 +64,115 @@ fn section_requests_are_owned_bounded_and_cancelled_by_generation() { } assert!(queue.is_empty()); } + +fn section_generator(prefetch: Vec) -> typing_engine::generation::Generator { + use typing_engine::{ + Config, + generation::{Assets, Generator, Language}, + random::Random, + }; + let config = Config { + mode: "words".into(), + words: 12, + punctuation: true, + funbox: vec!["poetry".into()], + ..Config::default() + }; + Generator::with_assets( + config, + Language { + name: "english".into(), + words: vec![Text::from("base")], + ..Language::default() + }, + None, + Random::seeded(4), + &mut Assets { + sections: prefetch, + ..Assets::default() + }, + ) + .unwrap() +} +#[test] +fn deferred_initial_generation_resumes_without_repeating_words_or_random_draws() { + let sources: Vec<_> = (0..4) + .map(|index| poem(&format!("Section{index} café Three Four"))) + .collect(); + let mut baseline = section_generator(sources.clone()); + let expected = baseline.generate(false).unwrap(); + let mut deferred = section_generator(vec![]); + let mut replies = 0; + let actual = loop { + match deferred.generate(false) { + Ok(generated) => break generated, + Err(error) => { + assert_eq!(error, "Section content required"); + let request = deferred.pending_request().unwrap().clone(); + assert!(deferred.generate(true).is_err()); + deferred.supply(&request, sources[replies].clone()).unwrap(); + assert!(deferred.supply(&request, poem("duplicate")).is_err()); + replies += 1; + } + } + }; + assert_eq!(replies, 3); + assert_eq!( + serde_json::to_value(&actual).unwrap(), + serde_json::to_value(&expected).unwrap() + ); + assert_eq!(actual.words[0], Text::from("Section0 ")); + assert_eq!(actual.words[1], Text::from("café ")); + assert_eq!( + serde_json::to_value(deferred.generate(true).unwrap()).unwrap(), + serde_json::to_value(expected).unwrap() + ); +} +#[test] +fn content_retains_initial_generator_while_session_creation_waits() { + use typing_engine::{ + Config, + generation::{Content, Language, Prepare}, + random::Random, + }; + let mut content = Content::default(); + content + .prepare(Prepare { + config: Config { + mode: "words".into(), + words: 2, + funbox: vec!["poetry".into()], + ..Config::default() + }, + language: Language { + name: "english".into(), + words: vec![Text::from("base")], + ..Language::default() + }, + custom: None, + random: Random::seeded(1), + quotes: None, + polyglot: vec![], + selected_quote_id: 1, + favorite_quote_ids: vec![], + sections: vec![], + }) + .unwrap(); + assert!(content.start_session(false).is_err()); + let request = content + .generator() + .unwrap() + .pending_request() + .unwrap() + .clone(); + content + .generator() + .unwrap() + .supply(&request, poem("one two")) + .unwrap(); + let session = content.start_session(false).unwrap(); + assert_eq!( + session.snapshot().words, + vec![Text::from("one "), Text::from("two")] + ); +} diff --git a/docs/rust-engine/progress.md b/docs/rust-engine/progress.md index 1051c4f35..8f043c34b 100644 --- a/docs/rust-engine/progress.md +++ b/docs/rust-engine/progress.md @@ -114,3 +114,6 @@ do not rerun completed checks without a new reason. - Current 0f7f2c13c Cloudflare build succeeds: 787.06 kB raw / 293.88 kB gzip (Vite), within payload budgets. Full browser proof and both actual-browser input-contract tests pass together: 5 tests, 19.7 seconds. - Poetry cleanup and extracted Wikipedia text normalization match 11 newly captured TypeScript cases. Rust owns cleanup; hosts retain network and HTML extraction. Bounded section queue tests cover one outstanding token, source-kind validation, 16-section prefetch, generation cancellation and rejection of stale/forged replies. These are not yet connected to generator/session continuation. + +- Initial section generation now resumes a retained batch across typed requests/replies, preserving already generated words and RNG state. Four content tests pass, including repeated generation and session creation after a delayed initial source. Native 61-generator snapshots still pass. Wasm request/supply declarations build and typecheck; browser behavior for section requests remains pending. Active-session continuation is the next dependency. +- Timing reconciliation still needs explicit attention: actual web completion captures time after navigation/hooks (and after async content replies), while input time is captured earlier. Consider a host-observed completion acknowledgement capability; Rust must own the decision, host supplies the actual monotonic/date observation. Never silently replace late completion timestamps with the original input timestamp. diff --git a/packages/typing-engine/src/browser.client.ts b/packages/typing-engine/src/browser.client.ts index 08baa6ca6..570875f9f 100644 --- a/packages/typing-engine/src/browser.client.ts +++ b/packages/typing-engine/src/browser.client.ts @@ -16,11 +16,22 @@ import type { NextWord } from "../generated/types/NextWord"; import type { Quote } from "../generated/types/Quote"; import type { Query } from "../generated/types/Query"; import type { Response } from "../generated/types/Response"; +import type { Request as ContentRequest } from "../generated/types/Request"; +import type { Source } from "../generated/types/Source"; import type { Preflight } from "../generated/types/Preflight"; import type { InsertionLayout } from "../generated/types/InsertionLayout"; import type { Snapshot } from "../generated/types/Snapshot"; import type { Log } from "../generated/types/Log"; -export type { Query, Response, Log, Snapshot, Preflight, InsertionLayout }; +export type { + Query, + Response, + Log, + Snapshot, + Preflight, + InsertionLayout, + ContentRequest, + Source, +}; export type { Command, @@ -186,6 +197,12 @@ export class BrowserContent { ), ) as NextWord; } + request(): ContentRequest | null { + return JSON.parse(this.live().request()) as ContentRequest | null; + } + supply(request: ContentRequest, source: Source): void { + this.live().supply(JSON.stringify(request), JSON.stringify(source)); + } createSession(repeating = false): BrowserSession { return new BrowserSession({}, [], this.live().session(repeating)); } From 56e88cdf06861624905e6cd21d365e664fdf7966 Mon Sep 17 00:00:00 2001 From: Lakshmi Tanmay Date: Fri, 23 Oct 2026 04:58:10 +0530 Subject: [PATCH 47/93] Preserve active input across background section requests and delayed replies --- crates/typing-engine/src/generation.rs | 3 + crates/typing-engine/src/session.rs | 236 +++++++++++++++--- crates/typing-engine/tests/content.rs | 164 ++++++++++++ .../typing-engine/tests/generated_session.rs | 36 +++ docs/rust-engine/decisions.md | 4 + docs/rust-engine/progress.md | 3 + 6 files changed, 407 insertions(+), 39 deletions(-) diff --git a/crates/typing-engine/src/generation.rs b/crates/typing-engine/src/generation.rs index ce85c7f4b..0effc9f00 100644 --- a/crates/typing-engine/src/generation.rs +++ b/crates/typing-engine/src/generation.rs @@ -142,6 +142,9 @@ fn collapse_spaces(text: Text) -> Text { Text(result) } impl Generator { + pub fn random_state(&self) -> &Random { + &self.random + } pub fn config(&self) -> &GenerationConfig { &self.config } diff --git a/crates/typing-engine/src/session.rs b/crates/typing-engine/src/session.rs index c2535719e..27b18b4c5 100644 --- a/crates/typing-engine/src/session.rs +++ b/crates/typing-engine/src/session.rs @@ -236,6 +236,14 @@ pub enum Command { #[serde(default)] defer_automatic: Option, }, + #[serde(rename_all = "camelCase")] + SupplyContent { + now: f64, + #[serde(default)] + date: f64, + request: crate::content::Request, + source: Box, + }, Configure { now: f64, config: Box, @@ -308,6 +316,11 @@ pub struct Update { )] #[ts(optional)] pub visual_input_override: Option, + #[serde(rename = "contentRequest", skip_serializing_if = "Option::is_none")] + #[ts(optional)] + pub content_request: Option, + #[serde(rename = "awaitingContent")] + pub awaiting_content: bool, } #[derive(Clone, Serialize, TS)] #[serde(rename_all = "camelCase")] @@ -323,6 +336,8 @@ pub struct Snapshot { pub words: Vec, pub section_indexes: Vec, pub all_words_generated: bool, + pub has_tab: bool, + pub has_newline: bool, pub current_quote: Option, } @@ -333,6 +348,27 @@ struct Recorded { data: Value, } +struct Checkpoint { + unit: u16, + now: f64, + date: f64, + input: Text, + target: Text, + correct: bool, + commit: bool, + word_correct: bool, + advance: bool, + checkpoint: bool, +} +struct DeferredInput { + units: VecDeque, + now: f64, + date: f64, + shift: Option, + automatic: bool, + composition: bool, + final_in_batch: bool, +} pub struct Session { pub pace: crate::pace::Pace, pub weak_spot: crate::weak_spot::WeakSpot, @@ -363,6 +399,10 @@ pub struct Session { word_changes: Vec, bailed_out: bool, visual_input_override: Option, + has_tab: bool, + has_newline: bool, + pending_commit: Option, + deferred_input: Option, } impl Session { @@ -375,6 +415,8 @@ impl Session { && config.mode != "zen" && !(config.mode == "words" && config.words == 0); let sections = (1..=words.len()).collect(); + let has_tab = words.iter().any(|word| word.0.contains(&9)); + let has_newline = words.iter().any(|word| word.0.contains(&10)); Ok(Self { pace: crate::pace::Pace::default(), weak_spot: crate::weak_spot::WeakSpot::default(), @@ -405,6 +447,10 @@ impl Session { word_changes: vec![], bailed_out: false, visual_input_override: None, + has_tab, + has_newline, + pending_commit: None, + deferred_input: None, }) } @@ -421,6 +467,8 @@ impl Session { ) -> Result { let mut session = Self::new(generator.config().clone(), generated.words)?; session.sections = generated.section_indexes; + session.has_tab = generated.has_tab; + session.has_newline = generated.has_newline; session.custom_limit = generator.custom_limit(); session.quote = generator .current_quote @@ -451,11 +499,8 @@ impl Session { ..observation }; let blocked = self.ended - || crate::input::blocked( - &self.config, - &observation, - self.words.iter().any(|word| word.0.contains(&10)), - ); + || self.pending_commit.is_some() + || crate::input::blocked(&self.config, &observation, self.has_newline); let display_length = target.len() - usize::from(target.0.last() == Some(&32)); let measure_overflow = !blocked && !layout.slow_timer @@ -478,6 +523,8 @@ impl Session { words: self.words.clone(), section_indexes: self.sections.clone(), all_words_generated: self.all_generated, + has_tab: self.has_tab, + has_newline: self.has_newline, current_quote: self.quote.clone(), } } @@ -516,6 +563,8 @@ impl Session { }; let index = self.words.len(); let next = generator.next(index, bound, previous.as_ref(), previous2.as_ref())?; + self.has_tab |= next.word.0.contains(&9); + self.has_newline |= next.word.0.contains(&10); self.words.push(next.word.clone()); self.sections.push(next.section_index); self.word_changes.push(WordChange { @@ -572,6 +621,11 @@ impl Session { self.active = false; self.ended = true; self.reason = reason; + self.pending_commit = None; + self.deferred_input = None; + if let Some(generator) = &mut self.generator { + generator.reset_requests(self.generation); + } self.record("timer", now, json!({"event":"end", "timer":((now-self.start.unwrap_or(now))/1000.0).floor(), "date":date})); } @@ -591,7 +645,8 @@ impl Session { | Command::FlushAutomatic { now, .. } | Command::Configure { now, .. } | Command::CustomLimit { now, .. } - | Command::Bailout { now, .. } => *now, + | Command::Bailout { now, .. } + | Command::SupplyContent { now, .. } => *now, }; if !now.is_finite() || now.abs() > 9_007_199_254_740_991.0 { return Err("Invalid timestamp".into()); @@ -612,6 +667,7 @@ impl Session { | Command::End { date, .. } | Command::Abandon { date, .. } | Command::Bailout { date, .. } + | Command::SupplyContent { date, .. } if !date.is_finite() => { return Err("Invalid wall-clock date".into()); @@ -653,28 +709,54 @@ impl Session { words: vec![], log_invalidated: false, visual_input_override: None, + content_request: self + .generator + .as_ref() + .and_then(|generator| generator.pending_request()) + .cloned(), + awaiting_content: self.pending_commit.is_some(), }); } - let length = data.len(); - for (index, unit) in data.0.into_iter().enumerate() { - if self.ended { - break; + self.process_input(DeferredInput { + units: data.0.into(), + now, + date, + shift: correct_shift_used, + automatic: automatic == Some(true), + composition: is_composition_ending == Some(true), + final_in_batch: layout.final_in_batch != Some(false), + }); + } + Command::SupplyContent { + now, + date, + request, + source, + } => { + self.generator + .as_mut() + .ok_or("Session has no generated content")? + .supply(&request, *source)?; + if let Err(error) = self.refill() { + if self + .generator + .as_ref() + .and_then(|generator| generator.pending_request()) + .is_none() + { + return Err(error); + } + } else if let Some(checkpoint) = self.pending_commit.take() { + let input_now = checkpoint.now; + let input_date = checkpoint.date; + self.complete_input(checkpoint, now, date); + self.drain_indentation(input_now, input_date); + if !self.ended + && let Some(input) = self.deferred_input.take() + { + self.process_input(input); } - // The browser checks failure/finish at the final unit of a - // committed input batch, not after each surrogate/character. - self.completion_checkpoint = index + 1 == length - && !self.composing - && layout.final_in_batch != Some(false); - self.insert( - unit, - now, - date, - correct_shift_used, - automatic == Some(true), - is_composition_ending == Some(true), - ); } - self.completion_checkpoint = true; } Command::Delete { kind, @@ -725,6 +807,8 @@ impl Session { Command::Reset { .. } => { self.index = 0; self.bailed_out = false; + self.pending_commit = None; + self.deferred_input = None; self.active = false; self.ended = false; self.reason = None; @@ -740,6 +824,9 @@ impl Session { self.earliest_visible_word = 0; self.automatic.clear(); self.generation = self.generation.wrapping_add(1); + if let Some(generator) = &mut self.generator { + generator.reset_requests(self.generation); + } } Command::Observe { now, @@ -784,9 +871,38 @@ impl Session { words: self.word_changes.clone(), log_invalidated: invalidated || previous_origin != origin, visual_input_override: self.visual_input_override.clone(), + content_request: self + .generator + .as_ref() + .and_then(|generator| generator.pending_request()) + .cloned(), + awaiting_content: self.pending_commit.is_some(), }) } + fn process_input(&mut self, mut input: DeferredInput) { + while !self.ended + && let Some(unit) = input.units.pop_front() + { + self.completion_checkpoint = + input.units.is_empty() && input.final_in_batch && !self.composing; + self.insert( + unit, + input.now, + input.date, + input.shift, + input.automatic, + input.composition, + ); + if self.pending_commit.is_some() { + if !input.units.is_empty() { + self.deferred_input = Some(input); + } + break; + } + } + self.completion_checkpoint = true; + } fn insert( &mut self, unit: u16, @@ -797,12 +913,18 @@ impl Session { composition: bool, ) { self.insert_one(unit, now, date, shift, automatic, composition); + self.drain_indentation(now, date); + } + fn drain_indentation(&mut self, now: f64, date: f64) { + if self.pending_commit.is_some() || self.ended { + return; + } if self.defer_automatic && self.indent_pending { self.indent_pending = false; self.automatic.push_back((now, date)); return; } - while self.indent_pending && !self.ended { + while self.indent_pending && !self.ended && self.pending_commit.is_none() { self.indent_pending = false; self.insert_one(9, now, date, None, true, false); if self.defer_automatic && self.indent_pending { @@ -832,11 +954,7 @@ impl Session { nospace, correct_shift_used: shift, }; - if crate::input::blocked( - &self.config, - &observation, - self.words.iter().any(|word| word.0.contains(&10)), - ) { + if crate::input::blocked(&self.config, &observation, self.has_newline) { return; } let unit = normalize( @@ -920,10 +1038,50 @@ impl Session { if !correct && !stopped && self.config.delete_on_error != "off" { self.delete_error(now); } + let checkpoint = Checkpoint { + unit, + now, + date, + input, + target, + correct, + commit, + word_correct, + advance, + checkpoint: self.completion_checkpoint, + }; if advance && let Err(_) = self.refill() { - self.end(now, date, Some("word generation error".into())); - return; + if self + .generator + .as_ref() + .and_then(|generator| generator.pending_request()) + .is_some() + { + if self.index + 1 >= self.words.len() { + self.pending_commit = Some(checkpoint); + return; + } + } else { + self.end(now, date, Some("word generation error".into())); + return; + } } + self.complete_input(checkpoint, now, date); + } + fn complete_input(&mut self, checkpoint: Checkpoint, finish_now: f64, finish_date: f64) { + let Checkpoint { + unit, + now, + date: _, + input, + target, + correct, + commit, + word_correct, + advance, + checkpoint, + } = checkpoint; + let checkpoint = checkpoint && !self.composing; // The actual browser navigates before checking difficulty/minimum burst. let committed_index = self.index; let progressed = @@ -931,7 +1089,7 @@ impl Session { if progressed { self.index += 1; } - if self.completion_checkpoint + if checkpoint && self.config.mode != "zen" && ((self.config.difficulty == "master" && !correct) || (self.config.difficulty == "expert" @@ -939,10 +1097,10 @@ impl Session { && !(input.is_empty() && (is_space(unit) || unit == 10)) && !word_correct)) { - self.end(now, date, Some("difficulty".into())); + self.end(finish_now, finish_date, Some("difficulty".into())); return; } - if self.completion_checkpoint && progressed && self.config.min_burst != "off" { + if checkpoint && progressed && self.config.min_burst != "off" { let log: crate::stats::Log = serde_json::from_value(self.log()).expect("Engine log is valid"); let burst = @@ -963,7 +1121,7 @@ impl Session { self.config.min_burst_custom_speed }) { - self.end(now, date, Some("min burst".into())); + self.end(finish_now, finish_date, Some("min burst".into())); return; } } @@ -972,12 +1130,12 @@ impl Session { && target.len() == input.len() + 1 && self.config.stop_on_error == "off" && self.config.delete_on_error == "off"; - if self.completion_checkpoint + if checkpoint && committed_index + 1 >= self.words.len() && finite && (word_correct || quick || advance) { - self.end(now, date, None); + self.end(finish_now, finish_date, None); } if !self.ended && correct @@ -1046,7 +1204,7 @@ impl Session { } fn delete(&mut self, kind: &str, now: f64, observed: Option, sentinel_removed: bool) { - if !self.active || self.ended { + if !self.active || self.ended || self.pending_commit.is_some() { return; } let input = self.current(); diff --git a/crates/typing-engine/tests/content.rs b/crates/typing-engine/tests/content.rs index 01fdc59e3..c7bcdf75e 100644 --- a/crates/typing-engine/tests/content.rs +++ b/crates/typing-engine/tests/content.rs @@ -117,6 +117,10 @@ fn deferred_initial_generation_resumes_without_repeating_words_or_random_draws() } }; assert_eq!(replies, 3); + assert_eq!( + serde_json::to_value(deferred.random_state()).unwrap(), + serde_json::to_value(baseline.random_state()).unwrap() + ); assert_eq!( serde_json::to_value(&actual).unwrap(), serde_json::to_value(&expected).unwrap() @@ -176,3 +180,163 @@ fn content_retains_initial_generator_while_session_creation_waits() { vec![Text::from("one "), Text::from("two")] ); } + +fn running_section_session(difficulty: &str) -> typing_engine::Session { + use typing_engine::{ + Config, + generation::{Assets, Generator, Language}, + random::Random, + }; + let generator = Generator::with_assets( + Config { + mode: "time".into(), + time: 0.0, + difficulty: difficulty.into(), + funbox: vec!["poetry".into()], + ..Config::default() + }, + Language { + name: "english".into(), + words: vec![Text::from("base")], + ..Language::default() + }, + None, + Random::seeded(1), + &mut Assets { + sections: vec![poem(&vec!["one"; 100].join(" "))], + ..Assets::default() + }, + ) + .unwrap(); + typing_engine::Session::from_generator(generator, false).unwrap() +} +fn commit( + session: &mut typing_engine::Session, + text: &str, + now: f64, +) -> typing_engine::session::Update { + session.apply(serde_json::from_value(serde_json::json!({"op":"insert","data":Text::from(text),"now":now,"date":1_000_000.0+now})).unwrap()).unwrap() +} +#[test] +fn active_session_prefetches_then_preserves_a_committed_batch_until_reply() { + let mut session = running_section_session("normal"); + let first = commit(&mut session, "one ", 0.0); + let request = first.content_request.unwrap(); + assert!(!first.awaiting_content); + assert_eq!(first.state.word_index, 1); + let waiting = commit(&mut session, &"one ".repeat(100), 100.0); + assert!(waiting.awaiting_content); + assert_eq!(waiting.state.word_index, 99); + assert_eq!(waiting.content_request, Some(request.clone())); + let log = session.log(); + let blocked = commit(&mut session, "unaccepted", 200.0); + assert!(blocked.awaiting_content); + assert_eq!(log, session.log()); + session + .apply( + serde_json::from_value( + serde_json::json!({"op":"delete","kind":"deleteWordBackward","now":300.0}), + ) + .unwrap(), + ) + .unwrap(); + assert_eq!(log, session.log()); + let resumed = session + .apply(typing_engine::Command::SupplyContent { + now: 1000.0, + date: 1_001_000.0, + request, + source: Box::new(poem("one two three")), + }) + .unwrap(); + assert!(!resumed.awaiting_content); + assert_eq!(resumed.state.word_index, 101); + assert_eq!(session.snapshot().words[101], Text::from("two ")); + let inserted = session.log()["events"] + .as_array() + .unwrap() + .iter() + .filter(|event| event["type"] == "input") + .count(); + assert_eq!(inserted, 404); +} +#[test] +fn reset_and_timeout_cancel_pending_reply_and_remaining_committed_units() { + use typing_engine::Command; + for reset in [false, true] { + let mut session = running_section_session("normal"); + let waiting = commit(&mut session, &"one ".repeat(101), 0.0); + assert!(waiting.awaiting_content); + let request = waiting.content_request.unwrap(); + if !reset { + session + .apply(Command::Configure { + now: 0.0, + config: Box::new(typing_engine::Config { + mode: "time".into(), + time: 1.0, + funbox: vec!["poetry".into()], + ..typing_engine::Config::default() + }), + }) + .unwrap(); + } + session + .apply(if reset { + Command::Reset { now: 1.0 } + } else { + Command::Advance { + now: 1000.0, + date: 1_001_000.0, + } + }) + .unwrap(); + let log = session.log(); + assert!( + session + .apply(Command::SupplyContent { + now: 2.0, + date: 1_000_002.0, + request, + source: Box::new(poem("late content")) + }) + .is_err() + ); + assert_eq!(log, session.log()); + assert_eq!(session.state().ended, !reset); + } +} +#[test] +fn failure_after_await_uses_reply_time_without_retiming_captured_input() { + use typing_engine::Command; + let mut session = running_section_session("master"); + for index in 0..99 { + commit(&mut session, "one ", index as f64 * 100.0); + } + let waiting = commit(&mut session, "xone ", 9900.0); + assert!(waiting.awaiting_content); + assert!(!waiting.state.ended); + let update = session + .apply(Command::SupplyContent { + now: 20000.0, + date: 1_020_000.0, + request: waiting.content_request.unwrap(), + source: Box::new(poem("next section")), + }) + .unwrap(); + assert!(update.state.ended); + assert_eq!(update.state.word_index, 100); + assert_eq!(update.state.reason.as_deref(), Some("difficulty")); + let log = session.log(); + let events = log["events"].as_array().unwrap(); + assert_eq!(events.last().unwrap()["testMs"].as_f64(), Some(20000.0)); + assert_eq!( + events + .iter() + .rev() + .find(|event| event["type"] == "input") + .unwrap()["testMs"] + .as_f64(), + Some(9900.0) + ); +} diff --git a/crates/typing-engine/tests/generated_session.rs b/crates/typing-engine/tests/generated_session.rs index d4d99ef98..39bbb66c5 100644 --- a/crates/typing-engine/tests/generated_session.rs +++ b/crates/typing-engine/tests/generated_session.rs @@ -199,3 +199,39 @@ fn invalid_configuration_and_overdue_work_leave_session_unchanged() { ); assert_eq!(log, session.log()); } + +#[test] +fn prepared_content_flags_include_tabs_and_newlines_beyond_initial_window() { + let mut text = vec![Text::from("word"); 100]; + text.push(Text::from("\tline\n")); + let custom = CustomText { + text, + mode: "repeat".into(), + limit: CustomLimit { + mode: "time".into(), + value: 30, + }, + pipe_delimiter: false, + }; + let mut session = Session::from_generator( + generator( + Config { + mode: "custom".into(), + ..Config::default() + }, + Some(custom), + ), + false, + ) + .unwrap(); + let snapshot = session.snapshot(); + assert!(snapshot.has_tab && snapshot.has_newline); + assert!(!snapshot.words.iter().any(|word| word.0.contains(&10))); + assert!( + !session + .preflight(Text::from("\n"), &Default::default()) + .blocked + ); + insert(&mut session, Text::from("\n"), 0.0); + assert_eq!(session.state().word_index, 1); +} diff --git a/docs/rust-engine/decisions.md b/docs/rust-engine/decisions.md index d1d05bbf7..0fe64be9c 100644 --- a/docs/rust-engine/decisions.md +++ b/docs/rust-engine/decisions.md @@ -26,3 +26,7 @@ Pseudolang retains UTF-16 prefix and JS property enumeration semantics. Its lega ## Browser progression precedes commit failure Actual built-Cloudflare browser tests show both master and minimum-burst failures advance the word before finishing when the input commits it. The headless oracle checks failure first. Rust follows the website: advancement precedes failure checks, while burst is calculated for the committed word. `browser-contract-overrides.json` explicitly classifies the one existing min-burst frame whose state differs; its original bytes stay unchanged and tests assert the original state before comparing the separately recorded browser state. Input events and scores retain exact equality. No score rule changes. + +## Section request ordering + +One outstanding section request per generator, with at most 16 prefetched sections. Background prefetch does not disable typing while already prepared words remain. At exhaustion, preserve the current commit and its bounded remaining UTF-16 batch; reject new insertion/deletion until a matching response arrives. Generation and input timestamps remain stable. Reset/end cancel requests. Resume-time completion uses the reply's explicit monotonic and wall-clock timestamps, matching the browser's post-await finish ordering. This replaces the old possibility of concurrent, out-of-order section fetches with explicit ordering as required by the migration contract; the browser adapter must not start duplicate requests. diff --git a/docs/rust-engine/progress.md b/docs/rust-engine/progress.md index 8f043c34b..ba25b24b3 100644 --- a/docs/rust-engine/progress.md +++ b/docs/rust-engine/progress.md @@ -117,3 +117,6 @@ do not rerun completed checks without a new reason. - Initial section generation now resumes a retained batch across typed requests/replies, preserving already generated words and RNG state. Four content tests pass, including repeated generation and session creation after a delayed initial source. Native 61-generator snapshots still pass. Wasm request/supply declarations build and typecheck; browser behavior for section requests remains pending. Active-session continuation is the next dependency. - Timing reconciliation still needs explicit attention: actual web completion captures time after navigation/hooks (and after async content replies), while input time is captured earlier. Consider a host-observed completion acknowledgement capability; Rust must own the decision, host supplies the actual monotonic/date observation. Never silently replace late completion timestamps with the original input timestamp. + +- Active section requests now preserve one committed batch (maximum 65,536 units). Background prefetch permits typing while target words remain; only exhausting content blocks input/deletion. Matching replies resume progression and buffered units; reset/end invalidate the request. A failure after awaiting content uses the reply's observed completion time while retaining original input-event times. Seven native content tests cover cleanup, initial resume, active resume, stale replies/reset/expiry and delayed failure. Browser verification pending. +- Prepared tab/newline capabilities now include custom content beyond the initial 100-word window; a native regression covers newline acceptance before that future word is generated. From 6b5892ee9ff32799ec3cab6e64570296c7db99af Mon Sep 17 00:00:00 2001 From: Lakshmi Tanmay Date: Fri, 23 Oct 2026 05:05:50 +0530 Subject: [PATCH 48/93] Freeze section generation, repetition and random-draw compatibility --- .../capture-section-generator-oracle.ts | 177 +++ .../oracle/section-generation.json | 1099 +++++++++++++++++ 2 files changed, 1276 insertions(+) create mode 100644 packages/typing-core/scripts/capture-section-generator-oracle.ts create mode 100644 packages/typing-fixtures/oracle/section-generation.json diff --git a/packages/typing-core/scripts/capture-section-generator-oracle.ts b/packages/typing-core/scripts/capture-section-generator-oracle.ts new file mode 100644 index 000000000..7a06f11bd --- /dev/null +++ b/packages/typing-core/scripts/capture-section-generator-oracle.ts @@ -0,0 +1,177 @@ +import { writeFileSync } from "node:fs"; +import { resolve } from "node:path"; +import type { Config, FunboxName } from "@oxytype/schemas/configs"; +import type { LanguageObject } from "@oxytype/schemas/languages"; +const units = (value: string): number[] => + Array.from({ length: value.length }, (_, i) => value.charCodeAt(i)); +const seeded = + (seed: number): (() => number) => + () => { + seed |= 0; + seed = (seed + 0x6d2b79f5) | 0; + let t = Math.imul(seed ^ (seed >>> 15), 1 | seed); + t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t; + return ((t ^ (t >>> 14)) >>> 0) / 4294967296; + }; +const original = Math.random; +let random = seeded(1); +Math.random = () => random(); +try { + const { createWordsGenerator } = await import("../src/words-generator"); + const { createFunboxWordFunctions, getWordFunboxes } = + await import("../src/funbox-word-functions"); + const { getDefaultConfig } = await import("../src/config/default-config"); + const { QuotesController } = await import("../src/quote-source"); + const { getPoem } = await import("../src/poetry"); + const { getSection } = await import("../src/wikipedia"); + const lazy = await import("../src/lazy-mode"); + const british = await import("../src/british-english"); + const english = await import("../src/english-punctuation"); + const references = []; + for (const [name, kind, time, repeat, randomCase] of [ + ["poetry-finite", "poetry", false, false, false], + ["article-finite", "wikipedia", false, false, false], + ["poetry-repeat", "poetry", false, true, false], + ["poetry-random-case", "poetry", false, false, true], + ["article-time", "wikipedia", true, false, false], + ] as const) { + random = seeded(91); + let repeated = false; + let counter = 0; + let words: string[] = []; + const sources: unknown[] = []; + const language = { + name: "english", + words: ["one", "two", "three", "four"], + } as LanguageObject; + const config: Config = { + ...getDefaultConfig(), + mode: time ? "time" : "words", + words: 12, + punctuation: true, + lazyMode: true, + funbox: [kind, ...(randomCase ? ["rAnDoMcAsE" as FunboxName] : [])], + }; + const pull = async (): Promise => { + const index = counter++; + const title = `Section ${index}`; + const author = "Author"; + if (kind === "poetry") { + const lines = [`Section${index} naïve TWO three`]; + sources.push({ + kind: "poem", + title: units(title), + author: units(author), + lines: lines.map(units), + }); + const result = await getPoem(async () => [{ title, author, lines }]); + if (result === false) throw new Error("Missing poem"); + return result; + } + const text = time + ? Array.from({ length: 100 }, (_, i) => `Word${index}_${i}`).join(" ") + : `Section${index} naïve… TWO three`; + sources.push({ + kind: "article", + title: units(title), + author: units(author), + plainText: units(text), + }); + return getSection("english", { + getLanguage: async () => ({ ...language, bcp47: "en" }), + fetchJson: async (url) => + url.includes("/w/api.php") + ? { query: { pages: { "1": { extract: "unused" } } } } + : { title, author, pageid: 1 }, + htmlToText: () => text, + }); + }; + const functions = createFunboxWordFunctions({ + getConfig: () => config, + getLanguage: async () => language, + getPoem: pull, + getSection: pull, + disableFunbox: () => undefined, + setLanguage: () => undefined, + }); + const generator = createWordsGenerator({ + getConfig: () => config, + getActiveFunboxes: () => getWordFunboxes(config.funbox, functions), + customText: { + getText: () => [], + getMode: () => "repeat", + getLimitMode: () => "word", + getLimitValue: () => 0, + getPipeDelimiter: () => false, + }, + quotes: new QuotesController({ + fetchJson: async () => { + throw new Error("Unexpected quote"); + }, + getSnapshot: () => null, + }), + transforms: { + replaceAccents: lazy.replaceAccents, + englishPunctuation: english, + britishEnglish: async (word, previous) => + british.replace(word, previous, config.mode), + }, + isRepeated: () => repeated, + isPractiseWordsActive: () => false, + getCurrentQuote: () => null, + setCurrentQuote: () => undefined, + getSelectedQuoteId: () => 1, + getWordsLength: () => words.length, + setMode: () => undefined, + setQuoteLengthAll: () => undefined, + disableFunbox: () => undefined, + }); + let generated = await generator.generateWords(language); + words = [...generated.words]; + if (repeat) { + repeated = true; + generated = await generator.generateWords(language); + words = [...generated.words]; + } + const extra = []; + for (let i = 0; i < (time ? 3 : 0); i++) { + const next = await generator.getNextWord( + words.length, + 100, + words.at(-1)?.replace(/[ \n]$/, ""), + words.at(-2)?.replace(/[ \n]$/, ""), + ); + words.push(next.word); + extra.push({ + ...next, + word: units(next.word), + wordRaw: units(next.wordRaw), + }); + } + references.push({ + name, + config, + language: { name: "english", words: language.words.map(units) }, + seed: 91, + sources, + repeat, + expected: { + ...generated, + words: generated.words.map(units), + extra, + allWordsGenerated: generator.areAllWordsGenerated(), + }, + randomTail: random(), + }); + } + writeFileSync( + resolve( + import.meta.dirname, + "../../typing-fixtures/oracle/section-generation.json", + ), + `${JSON.stringify(references, null, 2)}\n`, + { flag: "wx" }, + ); +} finally { + Math.random = original; +} diff --git a/packages/typing-fixtures/oracle/section-generation.json b/packages/typing-fixtures/oracle/section-generation.json new file mode 100644 index 000000000..7830b7bb3 --- /dev/null +++ b/packages/typing-fixtures/oracle/section-generation.json @@ -0,0 +1,1099 @@ +[ + { + "name": "poetry-finite", + "config": { + "theme": "rust", + "themeLight": "serika", + "themeDark": "rust", + "autoSwitchTheme": false, + "customTheme": false, + "customThemeColors": [ + "#180d0d", + "#e8692e", + "#e8692e", + "#9c867a", + "#271616", + "#f3e7dc", + "#ff4d6d", + "#b8344f", + "#ff4d6d", + "#b8344f" + ], + "favThemes": [], + "showKeyTips": false, + "smoothCaret": "medium", + "codeUnindentOnBackspace": false, + "quickRestart": "off", + "punctuation": true, + "numbers": false, + "words": 12, + "time": 30, + "mode": "words", + "quoteLength": [1], + "language": "english", + "fontSize": 2, + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "quickEnd": false, + "caretStyle": "default", + "paceCaretStyle": "default", + "flipTestColors": false, + "layout": "default", + "funbox": ["poetry"], + "confidenceMode": "off", + "indicateTypos": "off", + "compositionDisplay": "replace", + "timerStyle": "mini", + "liveSpeedStyle": "off", + "liveAccStyle": "off", + "liveBurstStyle": "off", + "colorfulMode": false, + "randomTheme": "off", + "timerColor": "main", + "timerOpacity": "1", + "stopOnError": "off", + "deleteOnError": "off", + "showAllLines": false, + "keymapMode": "off", + "keymapStyle": "staggered", + "keymapLegendStyle": "lowercase", + "keymapLayout": "overrideSync", + "keymapKeys": "minimal", + "keymapSize": 1, + "fontFamily": "Roboto_Mono", + "smoothLineScroll": false, + "alwaysShowDecimalPlaces": false, + "alwaysShowWordsHistory": false, + "singleListCommandLine": "on", + "commandPaletteHotkey": "Mod+K", + "capsLockWarning": true, + "playSoundOnError": "off", + "playSoundOnClick": "off", + "soundVolume": 0.5, + "startGraphsAtZero": true, + "showOutOfFocusWarning": true, + "showTestModesNotice": true, + "paceCaret": "off", + "paceCaretCustomSpeed": 100, + "repeatedPace": true, + "accountChart": ["on", "on", "on", "on"], + "minWpm": "off", + "minWpmCustomSpeed": 100, + "highlightMode": "letter", + "typedEffect": "keep", + "typingSpeedUnit": "wpm", + "hideExtraLetters": false, + "strictSpace": false, + "minAcc": "off", + "minAccCustom": 90, + "monkey": false, + "repeatQuotes": "off", + "resultSaving": true, + "oppositeShiftMode": "off", + "customBackground": "", + "customBackgroundSize": "cover", + "customBackgroundFilter": [0, 1, 1, 1], + "customLayoutfluid": ["qwerty", "dvorak", "colemak"], + "customPolyglot": ["english", "spanish", "french", "german"], + "monkeyPowerLevel": "off", + "minBurst": "off", + "minBurstCustomSpeed": 100, + "burstHeatmap": false, + "britishEnglish": false, + "lazyMode": true, + "showAverage": "off", + "showPb": false, + "tapeMode": "off", + "tapeMargin": 50, + "maxLineWidth": 0, + "playTimeWarning": "off" + }, + "language": { + "name": "english", + "words": [ + [111, 110, 101], + [116, 119, 111], + [116, 104, 114, 101, 101], + [102, 111, 117, 114] + ] + }, + "seed": 91, + "sources": [ + { + "kind": "poem", + "title": [83, 101, 99, 116, 105, 111, 110, 32, 48], + "author": [65, 117, 116, 104, 111, 114], + "lines": [ + [ + 83, 101, 99, 116, 105, 111, 110, 48, 32, 110, 97, 239, 118, 101, 32, + 84, 87, 79, 32, 116, 104, 114, 101, 101 + ] + ] + }, + { + "kind": "poem", + "title": [83, 101, 99, 116, 105, 111, 110, 32, 49], + "author": [65, 117, 116, 104, 111, 114], + "lines": [ + [ + 83, 101, 99, 116, 105, 111, 110, 49, 32, 110, 97, 239, 118, 101, 32, + 84, 87, 79, 32, 116, 104, 114, 101, 101 + ] + ] + }, + { + "kind": "poem", + "title": [83, 101, 99, 116, 105, 111, 110, 32, 50], + "author": [65, 117, 116, 104, 111, 114], + "lines": [ + [ + 83, 101, 99, 116, 105, 111, 110, 50, 32, 110, 97, 239, 118, 101, 32, + 84, 87, 79, 32, 116, 104, 114, 101, 101 + ] + ] + } + ], + "repeat": false, + "expected": { + "words": [ + [83, 101, 99, 116, 105, 111, 110, 48, 32], + [110, 97, 105, 118, 101, 32], + [84, 87, 79, 32], + [116, 104, 114, 101, 101, 32], + [83, 101, 99, 116, 105, 111, 110, 49, 32], + [110, 97, 105, 118, 101, 32], + [84, 87, 79, 32], + [116, 104, 114, 101, 101, 32], + [83, 101, 99, 116, 105, 111, 110, 50, 32], + [110, 97, 105, 118, 101, 32], + [84, 87, 79, 32], + [116, 104, 114, 101, 101, 32] + ], + "sectionIndexes": [1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3], + "hasTab": false, + "hasNewline": false, + "allJoiningScript": false, + "extra": [], + "allWordsGenerated": true + }, + "randomTail": 0.48793180380016565 + }, + { + "name": "article-finite", + "config": { + "theme": "rust", + "themeLight": "serika", + "themeDark": "rust", + "autoSwitchTheme": false, + "customTheme": false, + "customThemeColors": [ + "#180d0d", + "#e8692e", + "#e8692e", + "#9c867a", + "#271616", + "#f3e7dc", + "#ff4d6d", + "#b8344f", + "#ff4d6d", + "#b8344f" + ], + "favThemes": [], + "showKeyTips": false, + "smoothCaret": "medium", + "codeUnindentOnBackspace": false, + "quickRestart": "off", + "punctuation": true, + "numbers": false, + "words": 12, + "time": 30, + "mode": "words", + "quoteLength": [1], + "language": "english", + "fontSize": 2, + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "quickEnd": false, + "caretStyle": "default", + "paceCaretStyle": "default", + "flipTestColors": false, + "layout": "default", + "funbox": ["wikipedia"], + "confidenceMode": "off", + "indicateTypos": "off", + "compositionDisplay": "replace", + "timerStyle": "mini", + "liveSpeedStyle": "off", + "liveAccStyle": "off", + "liveBurstStyle": "off", + "colorfulMode": false, + "randomTheme": "off", + "timerColor": "main", + "timerOpacity": "1", + "stopOnError": "off", + "deleteOnError": "off", + "showAllLines": false, + "keymapMode": "off", + "keymapStyle": "staggered", + "keymapLegendStyle": "lowercase", + "keymapLayout": "overrideSync", + "keymapKeys": "minimal", + "keymapSize": 1, + "fontFamily": "Roboto_Mono", + "smoothLineScroll": false, + "alwaysShowDecimalPlaces": false, + "alwaysShowWordsHistory": false, + "singleListCommandLine": "on", + "commandPaletteHotkey": "Mod+K", + "capsLockWarning": true, + "playSoundOnError": "off", + "playSoundOnClick": "off", + "soundVolume": 0.5, + "startGraphsAtZero": true, + "showOutOfFocusWarning": true, + "showTestModesNotice": true, + "paceCaret": "off", + "paceCaretCustomSpeed": 100, + "repeatedPace": true, + "accountChart": ["on", "on", "on", "on"], + "minWpm": "off", + "minWpmCustomSpeed": 100, + "highlightMode": "letter", + "typedEffect": "keep", + "typingSpeedUnit": "wpm", + "hideExtraLetters": false, + "strictSpace": false, + "minAcc": "off", + "minAccCustom": 90, + "monkey": false, + "repeatQuotes": "off", + "resultSaving": true, + "oppositeShiftMode": "off", + "customBackground": "", + "customBackgroundSize": "cover", + "customBackgroundFilter": [0, 1, 1, 1], + "customLayoutfluid": ["qwerty", "dvorak", "colemak"], + "customPolyglot": ["english", "spanish", "french", "german"], + "monkeyPowerLevel": "off", + "minBurst": "off", + "minBurstCustomSpeed": 100, + "burstHeatmap": false, + "britishEnglish": false, + "lazyMode": true, + "showAverage": "off", + "showPb": false, + "tapeMode": "off", + "tapeMargin": 50, + "maxLineWidth": 0, + "playTimeWarning": "off" + }, + "language": { + "name": "english", + "words": [ + [111, 110, 101], + [116, 119, 111], + [116, 104, 114, 101, 101], + [102, 111, 117, 114] + ] + }, + "seed": 91, + "sources": [ + { + "kind": "article", + "title": [83, 101, 99, 116, 105, 111, 110, 32, 48], + "author": [65, 117, 116, 104, 111, 114], + "plainText": [ + 83, 101, 99, 116, 105, 111, 110, 48, 32, 110, 97, 239, 118, 101, 8230, + 32, 84, 87, 79, 32, 116, 104, 114, 101, 101 + ] + }, + { + "kind": "article", + "title": [83, 101, 99, 116, 105, 111, 110, 32, 49], + "author": [65, 117, 116, 104, 111, 114], + "plainText": [ + 83, 101, 99, 116, 105, 111, 110, 49, 32, 110, 97, 239, 118, 101, 8230, + 32, 84, 87, 79, 32, 116, 104, 114, 101, 101 + ] + }, + { + "kind": "article", + "title": [83, 101, 99, 116, 105, 111, 110, 32, 50], + "author": [65, 117, 116, 104, 111, 114], + "plainText": [ + 83, 101, 99, 116, 105, 111, 110, 50, 32, 110, 97, 239, 118, 101, 8230, + 32, 84, 87, 79, 32, 116, 104, 114, 101, 101 + ] + } + ], + "repeat": false, + "expected": { + "words": [ + [83, 101, 99, 116, 105, 111, 110, 48, 32], + [110, 97, 118, 101, 46, 46, 46, 32], + [84, 87, 79, 32], + [116, 104, 114, 101, 101, 32], + [83, 101, 99, 116, 105, 111, 110, 49, 32], + [110, 97, 118, 101, 46, 46, 46, 32], + [84, 87, 79, 32], + [116, 104, 114, 101, 101, 32], + [83, 101, 99, 116, 105, 111, 110, 50, 32], + [110, 97, 118, 101, 46, 46, 46, 32], + [84, 87, 79, 32], + [116, 104, 114, 101, 101, 32] + ], + "sectionIndexes": [1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3], + "hasTab": false, + "hasNewline": false, + "allJoiningScript": false, + "extra": [], + "allWordsGenerated": true + }, + "randomTail": 0.48793180380016565 + }, + { + "name": "poetry-repeat", + "config": { + "theme": "rust", + "themeLight": "serika", + "themeDark": "rust", + "autoSwitchTheme": false, + "customTheme": false, + "customThemeColors": [ + "#180d0d", + "#e8692e", + "#e8692e", + "#9c867a", + "#271616", + "#f3e7dc", + "#ff4d6d", + "#b8344f", + "#ff4d6d", + "#b8344f" + ], + "favThemes": [], + "showKeyTips": false, + "smoothCaret": "medium", + "codeUnindentOnBackspace": false, + "quickRestart": "off", + "punctuation": true, + "numbers": false, + "words": 12, + "time": 30, + "mode": "words", + "quoteLength": [1], + "language": "english", + "fontSize": 2, + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "quickEnd": false, + "caretStyle": "default", + "paceCaretStyle": "default", + "flipTestColors": false, + "layout": "default", + "funbox": ["poetry"], + "confidenceMode": "off", + "indicateTypos": "off", + "compositionDisplay": "replace", + "timerStyle": "mini", + "liveSpeedStyle": "off", + "liveAccStyle": "off", + "liveBurstStyle": "off", + "colorfulMode": false, + "randomTheme": "off", + "timerColor": "main", + "timerOpacity": "1", + "stopOnError": "off", + "deleteOnError": "off", + "showAllLines": false, + "keymapMode": "off", + "keymapStyle": "staggered", + "keymapLegendStyle": "lowercase", + "keymapLayout": "overrideSync", + "keymapKeys": "minimal", + "keymapSize": 1, + "fontFamily": "Roboto_Mono", + "smoothLineScroll": false, + "alwaysShowDecimalPlaces": false, + "alwaysShowWordsHistory": false, + "singleListCommandLine": "on", + "commandPaletteHotkey": "Mod+K", + "capsLockWarning": true, + "playSoundOnError": "off", + "playSoundOnClick": "off", + "soundVolume": 0.5, + "startGraphsAtZero": true, + "showOutOfFocusWarning": true, + "showTestModesNotice": true, + "paceCaret": "off", + "paceCaretCustomSpeed": 100, + "repeatedPace": true, + "accountChart": ["on", "on", "on", "on"], + "minWpm": "off", + "minWpmCustomSpeed": 100, + "highlightMode": "letter", + "typedEffect": "keep", + "typingSpeedUnit": "wpm", + "hideExtraLetters": false, + "strictSpace": false, + "minAcc": "off", + "minAccCustom": 90, + "monkey": false, + "repeatQuotes": "off", + "resultSaving": true, + "oppositeShiftMode": "off", + "customBackground": "", + "customBackgroundSize": "cover", + "customBackgroundFilter": [0, 1, 1, 1], + "customLayoutfluid": ["qwerty", "dvorak", "colemak"], + "customPolyglot": ["english", "spanish", "french", "german"], + "monkeyPowerLevel": "off", + "minBurst": "off", + "minBurstCustomSpeed": 100, + "burstHeatmap": false, + "britishEnglish": false, + "lazyMode": true, + "showAverage": "off", + "showPb": false, + "tapeMode": "off", + "tapeMargin": 50, + "maxLineWidth": 0, + "playTimeWarning": "off" + }, + "language": { + "name": "english", + "words": [ + [111, 110, 101], + [116, 119, 111], + [116, 104, 114, 101, 101], + [102, 111, 117, 114] + ] + }, + "seed": 91, + "sources": [ + { + "kind": "poem", + "title": [83, 101, 99, 116, 105, 111, 110, 32, 48], + "author": [65, 117, 116, 104, 111, 114], + "lines": [ + [ + 83, 101, 99, 116, 105, 111, 110, 48, 32, 110, 97, 239, 118, 101, 32, + 84, 87, 79, 32, 116, 104, 114, 101, 101 + ] + ] + }, + { + "kind": "poem", + "title": [83, 101, 99, 116, 105, 111, 110, 32, 49], + "author": [65, 117, 116, 104, 111, 114], + "lines": [ + [ + 83, 101, 99, 116, 105, 111, 110, 49, 32, 110, 97, 239, 118, 101, 32, + 84, 87, 79, 32, 116, 104, 114, 101, 101 + ] + ] + }, + { + "kind": "poem", + "title": [83, 101, 99, 116, 105, 111, 110, 32, 50], + "author": [65, 117, 116, 104, 111, 114], + "lines": [ + [ + 83, 101, 99, 116, 105, 111, 110, 50, 32, 110, 97, 239, 118, 101, 32, + 84, 87, 79, 32, 116, 104, 114, 101, 101 + ] + ] + } + ], + "repeat": true, + "expected": { + "words": [ + [83, 101, 99, 116, 105, 111, 110, 48, 32], + [110, 97, 105, 118, 101, 32], + [84, 87, 79, 32], + [116, 104, 114, 101, 101, 32], + [83, 101, 99, 116, 105, 111, 110, 49, 32], + [110, 97, 105, 118, 101, 32], + [84, 87, 79, 32], + [116, 104, 114, 101, 101, 32], + [83, 101, 99, 116, 105, 111, 110, 50, 32], + [110, 97, 105, 118, 101, 32], + [84, 87, 79, 32], + [116, 104, 114, 101, 101, 32] + ], + "sectionIndexes": [1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3], + "hasTab": false, + "hasNewline": false, + "allJoiningScript": false, + "extra": [], + "allWordsGenerated": true + }, + "randomTail": 0.48793180380016565 + }, + { + "name": "poetry-random-case", + "config": { + "theme": "rust", + "themeLight": "serika", + "themeDark": "rust", + "autoSwitchTheme": false, + "customTheme": false, + "customThemeColors": [ + "#180d0d", + "#e8692e", + "#e8692e", + "#9c867a", + "#271616", + "#f3e7dc", + "#ff4d6d", + "#b8344f", + "#ff4d6d", + "#b8344f" + ], + "favThemes": [], + "showKeyTips": false, + "smoothCaret": "medium", + "codeUnindentOnBackspace": false, + "quickRestart": "off", + "punctuation": true, + "numbers": false, + "words": 12, + "time": 30, + "mode": "words", + "quoteLength": [1], + "language": "english", + "fontSize": 2, + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "quickEnd": false, + "caretStyle": "default", + "paceCaretStyle": "default", + "flipTestColors": false, + "layout": "default", + "funbox": ["poetry", "rAnDoMcAsE"], + "confidenceMode": "off", + "indicateTypos": "off", + "compositionDisplay": "replace", + "timerStyle": "mini", + "liveSpeedStyle": "off", + "liveAccStyle": "off", + "liveBurstStyle": "off", + "colorfulMode": false, + "randomTheme": "off", + "timerColor": "main", + "timerOpacity": "1", + "stopOnError": "off", + "deleteOnError": "off", + "showAllLines": false, + "keymapMode": "off", + "keymapStyle": "staggered", + "keymapLegendStyle": "lowercase", + "keymapLayout": "overrideSync", + "keymapKeys": "minimal", + "keymapSize": 1, + "fontFamily": "Roboto_Mono", + "smoothLineScroll": false, + "alwaysShowDecimalPlaces": false, + "alwaysShowWordsHistory": false, + "singleListCommandLine": "on", + "commandPaletteHotkey": "Mod+K", + "capsLockWarning": true, + "playSoundOnError": "off", + "playSoundOnClick": "off", + "soundVolume": 0.5, + "startGraphsAtZero": true, + "showOutOfFocusWarning": true, + "showTestModesNotice": true, + "paceCaret": "off", + "paceCaretCustomSpeed": 100, + "repeatedPace": true, + "accountChart": ["on", "on", "on", "on"], + "minWpm": "off", + "minWpmCustomSpeed": 100, + "highlightMode": "letter", + "typedEffect": "keep", + "typingSpeedUnit": "wpm", + "hideExtraLetters": false, + "strictSpace": false, + "minAcc": "off", + "minAccCustom": 90, + "monkey": false, + "repeatQuotes": "off", + "resultSaving": true, + "oppositeShiftMode": "off", + "customBackground": "", + "customBackgroundSize": "cover", + "customBackgroundFilter": [0, 1, 1, 1], + "customLayoutfluid": ["qwerty", "dvorak", "colemak"], + "customPolyglot": ["english", "spanish", "french", "german"], + "monkeyPowerLevel": "off", + "minBurst": "off", + "minBurstCustomSpeed": 100, + "burstHeatmap": false, + "britishEnglish": false, + "lazyMode": true, + "showAverage": "off", + "showPb": false, + "tapeMode": "off", + "tapeMargin": 50, + "maxLineWidth": 0, + "playTimeWarning": "off" + }, + "language": { + "name": "english", + "words": [ + [111, 110, 101], + [116, 119, 111], + [116, 104, 114, 101, 101], + [102, 111, 117, 114] + ] + }, + "seed": 91, + "sources": [ + { + "kind": "poem", + "title": [83, 101, 99, 116, 105, 111, 110, 32, 48], + "author": [65, 117, 116, 104, 111, 114], + "lines": [ + [ + 83, 101, 99, 116, 105, 111, 110, 48, 32, 110, 97, 239, 118, 101, 32, + 84, 87, 79, 32, 116, 104, 114, 101, 101 + ] + ] + }, + { + "kind": "poem", + "title": [83, 101, 99, 116, 105, 111, 110, 32, 49], + "author": [65, 117, 116, 104, 111, 114], + "lines": [ + [ + 83, 101, 99, 116, 105, 111, 110, 49, 32, 110, 97, 239, 118, 101, 32, + 84, 87, 79, 32, 116, 104, 114, 101, 101 + ] + ] + }, + { + "kind": "poem", + "title": [83, 101, 99, 116, 105, 111, 110, 32, 50], + "author": [65, 117, 116, 104, 111, 114], + "lines": [ + [ + 83, 101, 99, 116, 105, 111, 110, 50, 32, 110, 97, 239, 118, 101, 32, + 84, 87, 79, 32, 116, 104, 114, 101, 101 + ] + ] + } + ], + "repeat": false, + "expected": { + "words": [ + [83, 101, 67, 84, 105, 79, 78, 48, 32], + [110, 65, 73, 86, 101, 32], + [84, 87, 111, 32], + [84, 72, 82, 69, 101, 32], + [83, 101, 99, 116, 73, 111, 110, 49, 32], + [110, 97, 73, 86, 101, 32], + [84, 119, 79, 32], + [116, 72, 114, 101, 101, 32], + [115, 101, 99, 116, 73, 79, 110, 50, 32], + [110, 65, 105, 118, 101, 32], + [84, 119, 111, 32], + [84, 72, 82, 101, 69, 32] + ], + "sectionIndexes": [1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3], + "hasTab": false, + "hasNewline": false, + "allJoiningScript": false, + "extra": [], + "allWordsGenerated": true + }, + "randomTail": 0.6900723299477249 + }, + { + "name": "article-time", + "config": { + "theme": "rust", + "themeLight": "serika", + "themeDark": "rust", + "autoSwitchTheme": false, + "customTheme": false, + "customThemeColors": [ + "#180d0d", + "#e8692e", + "#e8692e", + "#9c867a", + "#271616", + "#f3e7dc", + "#ff4d6d", + "#b8344f", + "#ff4d6d", + "#b8344f" + ], + "favThemes": [], + "showKeyTips": false, + "smoothCaret": "medium", + "codeUnindentOnBackspace": false, + "quickRestart": "off", + "punctuation": true, + "numbers": false, + "words": 12, + "time": 30, + "mode": "time", + "quoteLength": [1], + "language": "english", + "fontSize": 2, + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "quickEnd": false, + "caretStyle": "default", + "paceCaretStyle": "default", + "flipTestColors": false, + "layout": "default", + "funbox": ["wikipedia"], + "confidenceMode": "off", + "indicateTypos": "off", + "compositionDisplay": "replace", + "timerStyle": "mini", + "liveSpeedStyle": "off", + "liveAccStyle": "off", + "liveBurstStyle": "off", + "colorfulMode": false, + "randomTheme": "off", + "timerColor": "main", + "timerOpacity": "1", + "stopOnError": "off", + "deleteOnError": "off", + "showAllLines": false, + "keymapMode": "off", + "keymapStyle": "staggered", + "keymapLegendStyle": "lowercase", + "keymapLayout": "overrideSync", + "keymapKeys": "minimal", + "keymapSize": 1, + "fontFamily": "Roboto_Mono", + "smoothLineScroll": false, + "alwaysShowDecimalPlaces": false, + "alwaysShowWordsHistory": false, + "singleListCommandLine": "on", + "commandPaletteHotkey": "Mod+K", + "capsLockWarning": true, + "playSoundOnError": "off", + "playSoundOnClick": "off", + "soundVolume": 0.5, + "startGraphsAtZero": true, + "showOutOfFocusWarning": true, + "showTestModesNotice": true, + "paceCaret": "off", + "paceCaretCustomSpeed": 100, + "repeatedPace": true, + "accountChart": ["on", "on", "on", "on"], + "minWpm": "off", + "minWpmCustomSpeed": 100, + "highlightMode": "letter", + "typedEffect": "keep", + "typingSpeedUnit": "wpm", + "hideExtraLetters": false, + "strictSpace": false, + "minAcc": "off", + "minAccCustom": 90, + "monkey": false, + "repeatQuotes": "off", + "resultSaving": true, + "oppositeShiftMode": "off", + "customBackground": "", + "customBackgroundSize": "cover", + "customBackgroundFilter": [0, 1, 1, 1], + "customLayoutfluid": ["qwerty", "dvorak", "colemak"], + "customPolyglot": ["english", "spanish", "french", "german"], + "monkeyPowerLevel": "off", + "minBurst": "off", + "minBurstCustomSpeed": 100, + "burstHeatmap": false, + "britishEnglish": false, + "lazyMode": true, + "showAverage": "off", + "showPb": false, + "tapeMode": "off", + "tapeMargin": 50, + "maxLineWidth": 0, + "playTimeWarning": "off" + }, + "language": { + "name": "english", + "words": [ + [111, 110, 101], + [116, 119, 111], + [116, 104, 114, 101, 101], + [102, 111, 117, 114] + ] + }, + "seed": 91, + "sources": [ + { + "kind": "article", + "title": [83, 101, 99, 116, 105, 111, 110, 32, 48], + "author": [65, 117, 116, 104, 111, 114], + "plainText": [ + 87, 111, 114, 100, 48, 95, 48, 32, 87, 111, 114, 100, 48, 95, 49, 32, + 87, 111, 114, 100, 48, 95, 50, 32, 87, 111, 114, 100, 48, 95, 51, 32, + 87, 111, 114, 100, 48, 95, 52, 32, 87, 111, 114, 100, 48, 95, 53, 32, + 87, 111, 114, 100, 48, 95, 54, 32, 87, 111, 114, 100, 48, 95, 55, 32, + 87, 111, 114, 100, 48, 95, 56, 32, 87, 111, 114, 100, 48, 95, 57, 32, + 87, 111, 114, 100, 48, 95, 49, 48, 32, 87, 111, 114, 100, 48, 95, 49, + 49, 32, 87, 111, 114, 100, 48, 95, 49, 50, 32, 87, 111, 114, 100, 48, + 95, 49, 51, 32, 87, 111, 114, 100, 48, 95, 49, 52, 32, 87, 111, 114, + 100, 48, 95, 49, 53, 32, 87, 111, 114, 100, 48, 95, 49, 54, 32, 87, + 111, 114, 100, 48, 95, 49, 55, 32, 87, 111, 114, 100, 48, 95, 49, 56, + 32, 87, 111, 114, 100, 48, 95, 49, 57, 32, 87, 111, 114, 100, 48, 95, + 50, 48, 32, 87, 111, 114, 100, 48, 95, 50, 49, 32, 87, 111, 114, 100, + 48, 95, 50, 50, 32, 87, 111, 114, 100, 48, 95, 50, 51, 32, 87, 111, + 114, 100, 48, 95, 50, 52, 32, 87, 111, 114, 100, 48, 95, 50, 53, 32, + 87, 111, 114, 100, 48, 95, 50, 54, 32, 87, 111, 114, 100, 48, 95, 50, + 55, 32, 87, 111, 114, 100, 48, 95, 50, 56, 32, 87, 111, 114, 100, 48, + 95, 50, 57, 32, 87, 111, 114, 100, 48, 95, 51, 48, 32, 87, 111, 114, + 100, 48, 95, 51, 49, 32, 87, 111, 114, 100, 48, 95, 51, 50, 32, 87, + 111, 114, 100, 48, 95, 51, 51, 32, 87, 111, 114, 100, 48, 95, 51, 52, + 32, 87, 111, 114, 100, 48, 95, 51, 53, 32, 87, 111, 114, 100, 48, 95, + 51, 54, 32, 87, 111, 114, 100, 48, 95, 51, 55, 32, 87, 111, 114, 100, + 48, 95, 51, 56, 32, 87, 111, 114, 100, 48, 95, 51, 57, 32, 87, 111, + 114, 100, 48, 95, 52, 48, 32, 87, 111, 114, 100, 48, 95, 52, 49, 32, + 87, 111, 114, 100, 48, 95, 52, 50, 32, 87, 111, 114, 100, 48, 95, 52, + 51, 32, 87, 111, 114, 100, 48, 95, 52, 52, 32, 87, 111, 114, 100, 48, + 95, 52, 53, 32, 87, 111, 114, 100, 48, 95, 52, 54, 32, 87, 111, 114, + 100, 48, 95, 52, 55, 32, 87, 111, 114, 100, 48, 95, 52, 56, 32, 87, + 111, 114, 100, 48, 95, 52, 57, 32, 87, 111, 114, 100, 48, 95, 53, 48, + 32, 87, 111, 114, 100, 48, 95, 53, 49, 32, 87, 111, 114, 100, 48, 95, + 53, 50, 32, 87, 111, 114, 100, 48, 95, 53, 51, 32, 87, 111, 114, 100, + 48, 95, 53, 52, 32, 87, 111, 114, 100, 48, 95, 53, 53, 32, 87, 111, + 114, 100, 48, 95, 53, 54, 32, 87, 111, 114, 100, 48, 95, 53, 55, 32, + 87, 111, 114, 100, 48, 95, 53, 56, 32, 87, 111, 114, 100, 48, 95, 53, + 57, 32, 87, 111, 114, 100, 48, 95, 54, 48, 32, 87, 111, 114, 100, 48, + 95, 54, 49, 32, 87, 111, 114, 100, 48, 95, 54, 50, 32, 87, 111, 114, + 100, 48, 95, 54, 51, 32, 87, 111, 114, 100, 48, 95, 54, 52, 32, 87, + 111, 114, 100, 48, 95, 54, 53, 32, 87, 111, 114, 100, 48, 95, 54, 54, + 32, 87, 111, 114, 100, 48, 95, 54, 55, 32, 87, 111, 114, 100, 48, 95, + 54, 56, 32, 87, 111, 114, 100, 48, 95, 54, 57, 32, 87, 111, 114, 100, + 48, 95, 55, 48, 32, 87, 111, 114, 100, 48, 95, 55, 49, 32, 87, 111, + 114, 100, 48, 95, 55, 50, 32, 87, 111, 114, 100, 48, 95, 55, 51, 32, + 87, 111, 114, 100, 48, 95, 55, 52, 32, 87, 111, 114, 100, 48, 95, 55, + 53, 32, 87, 111, 114, 100, 48, 95, 55, 54, 32, 87, 111, 114, 100, 48, + 95, 55, 55, 32, 87, 111, 114, 100, 48, 95, 55, 56, 32, 87, 111, 114, + 100, 48, 95, 55, 57, 32, 87, 111, 114, 100, 48, 95, 56, 48, 32, 87, + 111, 114, 100, 48, 95, 56, 49, 32, 87, 111, 114, 100, 48, 95, 56, 50, + 32, 87, 111, 114, 100, 48, 95, 56, 51, 32, 87, 111, 114, 100, 48, 95, + 56, 52, 32, 87, 111, 114, 100, 48, 95, 56, 53, 32, 87, 111, 114, 100, + 48, 95, 56, 54, 32, 87, 111, 114, 100, 48, 95, 56, 55, 32, 87, 111, + 114, 100, 48, 95, 56, 56, 32, 87, 111, 114, 100, 48, 95, 56, 57, 32, + 87, 111, 114, 100, 48, 95, 57, 48, 32, 87, 111, 114, 100, 48, 95, 57, + 49, 32, 87, 111, 114, 100, 48, 95, 57, 50, 32, 87, 111, 114, 100, 48, + 95, 57, 51, 32, 87, 111, 114, 100, 48, 95, 57, 52, 32, 87, 111, 114, + 100, 48, 95, 57, 53, 32, 87, 111, 114, 100, 48, 95, 57, 54, 32, 87, + 111, 114, 100, 48, 95, 57, 55, 32, 87, 111, 114, 100, 48, 95, 57, 56, + 32, 87, 111, 114, 100, 48, 95, 57, 57 + ] + }, + { + "kind": "article", + "title": [83, 101, 99, 116, 105, 111, 110, 32, 49], + "author": [65, 117, 116, 104, 111, 114], + "plainText": [ + 87, 111, 114, 100, 49, 95, 48, 32, 87, 111, 114, 100, 49, 95, 49, 32, + 87, 111, 114, 100, 49, 95, 50, 32, 87, 111, 114, 100, 49, 95, 51, 32, + 87, 111, 114, 100, 49, 95, 52, 32, 87, 111, 114, 100, 49, 95, 53, 32, + 87, 111, 114, 100, 49, 95, 54, 32, 87, 111, 114, 100, 49, 95, 55, 32, + 87, 111, 114, 100, 49, 95, 56, 32, 87, 111, 114, 100, 49, 95, 57, 32, + 87, 111, 114, 100, 49, 95, 49, 48, 32, 87, 111, 114, 100, 49, 95, 49, + 49, 32, 87, 111, 114, 100, 49, 95, 49, 50, 32, 87, 111, 114, 100, 49, + 95, 49, 51, 32, 87, 111, 114, 100, 49, 95, 49, 52, 32, 87, 111, 114, + 100, 49, 95, 49, 53, 32, 87, 111, 114, 100, 49, 95, 49, 54, 32, 87, + 111, 114, 100, 49, 95, 49, 55, 32, 87, 111, 114, 100, 49, 95, 49, 56, + 32, 87, 111, 114, 100, 49, 95, 49, 57, 32, 87, 111, 114, 100, 49, 95, + 50, 48, 32, 87, 111, 114, 100, 49, 95, 50, 49, 32, 87, 111, 114, 100, + 49, 95, 50, 50, 32, 87, 111, 114, 100, 49, 95, 50, 51, 32, 87, 111, + 114, 100, 49, 95, 50, 52, 32, 87, 111, 114, 100, 49, 95, 50, 53, 32, + 87, 111, 114, 100, 49, 95, 50, 54, 32, 87, 111, 114, 100, 49, 95, 50, + 55, 32, 87, 111, 114, 100, 49, 95, 50, 56, 32, 87, 111, 114, 100, 49, + 95, 50, 57, 32, 87, 111, 114, 100, 49, 95, 51, 48, 32, 87, 111, 114, + 100, 49, 95, 51, 49, 32, 87, 111, 114, 100, 49, 95, 51, 50, 32, 87, + 111, 114, 100, 49, 95, 51, 51, 32, 87, 111, 114, 100, 49, 95, 51, 52, + 32, 87, 111, 114, 100, 49, 95, 51, 53, 32, 87, 111, 114, 100, 49, 95, + 51, 54, 32, 87, 111, 114, 100, 49, 95, 51, 55, 32, 87, 111, 114, 100, + 49, 95, 51, 56, 32, 87, 111, 114, 100, 49, 95, 51, 57, 32, 87, 111, + 114, 100, 49, 95, 52, 48, 32, 87, 111, 114, 100, 49, 95, 52, 49, 32, + 87, 111, 114, 100, 49, 95, 52, 50, 32, 87, 111, 114, 100, 49, 95, 52, + 51, 32, 87, 111, 114, 100, 49, 95, 52, 52, 32, 87, 111, 114, 100, 49, + 95, 52, 53, 32, 87, 111, 114, 100, 49, 95, 52, 54, 32, 87, 111, 114, + 100, 49, 95, 52, 55, 32, 87, 111, 114, 100, 49, 95, 52, 56, 32, 87, + 111, 114, 100, 49, 95, 52, 57, 32, 87, 111, 114, 100, 49, 95, 53, 48, + 32, 87, 111, 114, 100, 49, 95, 53, 49, 32, 87, 111, 114, 100, 49, 95, + 53, 50, 32, 87, 111, 114, 100, 49, 95, 53, 51, 32, 87, 111, 114, 100, + 49, 95, 53, 52, 32, 87, 111, 114, 100, 49, 95, 53, 53, 32, 87, 111, + 114, 100, 49, 95, 53, 54, 32, 87, 111, 114, 100, 49, 95, 53, 55, 32, + 87, 111, 114, 100, 49, 95, 53, 56, 32, 87, 111, 114, 100, 49, 95, 53, + 57, 32, 87, 111, 114, 100, 49, 95, 54, 48, 32, 87, 111, 114, 100, 49, + 95, 54, 49, 32, 87, 111, 114, 100, 49, 95, 54, 50, 32, 87, 111, 114, + 100, 49, 95, 54, 51, 32, 87, 111, 114, 100, 49, 95, 54, 52, 32, 87, + 111, 114, 100, 49, 95, 54, 53, 32, 87, 111, 114, 100, 49, 95, 54, 54, + 32, 87, 111, 114, 100, 49, 95, 54, 55, 32, 87, 111, 114, 100, 49, 95, + 54, 56, 32, 87, 111, 114, 100, 49, 95, 54, 57, 32, 87, 111, 114, 100, + 49, 95, 55, 48, 32, 87, 111, 114, 100, 49, 95, 55, 49, 32, 87, 111, + 114, 100, 49, 95, 55, 50, 32, 87, 111, 114, 100, 49, 95, 55, 51, 32, + 87, 111, 114, 100, 49, 95, 55, 52, 32, 87, 111, 114, 100, 49, 95, 55, + 53, 32, 87, 111, 114, 100, 49, 95, 55, 54, 32, 87, 111, 114, 100, 49, + 95, 55, 55, 32, 87, 111, 114, 100, 49, 95, 55, 56, 32, 87, 111, 114, + 100, 49, 95, 55, 57, 32, 87, 111, 114, 100, 49, 95, 56, 48, 32, 87, + 111, 114, 100, 49, 95, 56, 49, 32, 87, 111, 114, 100, 49, 95, 56, 50, + 32, 87, 111, 114, 100, 49, 95, 56, 51, 32, 87, 111, 114, 100, 49, 95, + 56, 52, 32, 87, 111, 114, 100, 49, 95, 56, 53, 32, 87, 111, 114, 100, + 49, 95, 56, 54, 32, 87, 111, 114, 100, 49, 95, 56, 55, 32, 87, 111, + 114, 100, 49, 95, 56, 56, 32, 87, 111, 114, 100, 49, 95, 56, 57, 32, + 87, 111, 114, 100, 49, 95, 57, 48, 32, 87, 111, 114, 100, 49, 95, 57, + 49, 32, 87, 111, 114, 100, 49, 95, 57, 50, 32, 87, 111, 114, 100, 49, + 95, 57, 51, 32, 87, 111, 114, 100, 49, 95, 57, 52, 32, 87, 111, 114, + 100, 49, 95, 57, 53, 32, 87, 111, 114, 100, 49, 95, 57, 54, 32, 87, + 111, 114, 100, 49, 95, 57, 55, 32, 87, 111, 114, 100, 49, 95, 57, 56, + 32, 87, 111, 114, 100, 49, 95, 57, 57 + ] + } + ], + "repeat": false, + "expected": { + "words": [ + [87, 111, 114, 100, 48, 95, 48, 32], + [87, 111, 114, 100, 48, 95, 49, 32], + [87, 111, 114, 100, 48, 95, 50, 32], + [87, 111, 114, 100, 48, 95, 51, 32], + [87, 111, 114, 100, 48, 95, 52, 32], + [87, 111, 114, 100, 48, 95, 53, 32], + [87, 111, 114, 100, 48, 95, 54, 32], + [87, 111, 114, 100, 48, 95, 55, 32], + [87, 111, 114, 100, 48, 95, 56, 32], + [87, 111, 114, 100, 48, 95, 57, 32], + [87, 111, 114, 100, 48, 95, 49, 48, 32], + [87, 111, 114, 100, 48, 95, 49, 49, 32], + [87, 111, 114, 100, 48, 95, 49, 50, 32], + [87, 111, 114, 100, 48, 95, 49, 51, 32], + [87, 111, 114, 100, 48, 95, 49, 52, 32], + [87, 111, 114, 100, 48, 95, 49, 53, 32], + [87, 111, 114, 100, 48, 95, 49, 54, 32], + [87, 111, 114, 100, 48, 95, 49, 55, 32], + [87, 111, 114, 100, 48, 95, 49, 56, 32], + [87, 111, 114, 100, 48, 95, 49, 57, 32], + [87, 111, 114, 100, 48, 95, 50, 48, 32], + [87, 111, 114, 100, 48, 95, 50, 49, 32], + [87, 111, 114, 100, 48, 95, 50, 50, 32], + [87, 111, 114, 100, 48, 95, 50, 51, 32], + [87, 111, 114, 100, 48, 95, 50, 52, 32], + [87, 111, 114, 100, 48, 95, 50, 53, 32], + [87, 111, 114, 100, 48, 95, 50, 54, 32], + [87, 111, 114, 100, 48, 95, 50, 55, 32], + [87, 111, 114, 100, 48, 95, 50, 56, 32], + [87, 111, 114, 100, 48, 95, 50, 57, 32], + [87, 111, 114, 100, 48, 95, 51, 48, 32], + [87, 111, 114, 100, 48, 95, 51, 49, 32], + [87, 111, 114, 100, 48, 95, 51, 50, 32], + [87, 111, 114, 100, 48, 95, 51, 51, 32], + [87, 111, 114, 100, 48, 95, 51, 52, 32], + [87, 111, 114, 100, 48, 95, 51, 53, 32], + [87, 111, 114, 100, 48, 95, 51, 54, 32], + [87, 111, 114, 100, 48, 95, 51, 55, 32], + [87, 111, 114, 100, 48, 95, 51, 56, 32], + [87, 111, 114, 100, 48, 95, 51, 57, 32], + [87, 111, 114, 100, 48, 95, 52, 48, 32], + [87, 111, 114, 100, 48, 95, 52, 49, 32], + [87, 111, 114, 100, 48, 95, 52, 50, 32], + [87, 111, 114, 100, 48, 95, 52, 51, 32], + [87, 111, 114, 100, 48, 95, 52, 52, 32], + [87, 111, 114, 100, 48, 95, 52, 53, 32], + [87, 111, 114, 100, 48, 95, 52, 54, 32], + [87, 111, 114, 100, 48, 95, 52, 55, 32], + [87, 111, 114, 100, 48, 95, 52, 56, 32], + [87, 111, 114, 100, 48, 95, 52, 57, 32], + [87, 111, 114, 100, 48, 95, 53, 48, 32], + [87, 111, 114, 100, 48, 95, 53, 49, 32], + [87, 111, 114, 100, 48, 95, 53, 50, 32], + [87, 111, 114, 100, 48, 95, 53, 51, 32], + [87, 111, 114, 100, 48, 95, 53, 52, 32], + [87, 111, 114, 100, 48, 95, 53, 53, 32], + [87, 111, 114, 100, 48, 95, 53, 54, 32], + [87, 111, 114, 100, 48, 95, 53, 55, 32], + [87, 111, 114, 100, 48, 95, 53, 56, 32], + [87, 111, 114, 100, 48, 95, 53, 57, 32], + [87, 111, 114, 100, 48, 95, 54, 48, 32], + [87, 111, 114, 100, 48, 95, 54, 49, 32], + [87, 111, 114, 100, 48, 95, 54, 50, 32], + [87, 111, 114, 100, 48, 95, 54, 51, 32], + [87, 111, 114, 100, 48, 95, 54, 52, 32], + [87, 111, 114, 100, 48, 95, 54, 53, 32], + [87, 111, 114, 100, 48, 95, 54, 54, 32], + [87, 111, 114, 100, 48, 95, 54, 55, 32], + [87, 111, 114, 100, 48, 95, 54, 56, 32], + [87, 111, 114, 100, 48, 95, 54, 57, 32], + [87, 111, 114, 100, 48, 95, 55, 48, 32], + [87, 111, 114, 100, 48, 95, 55, 49, 32], + [87, 111, 114, 100, 48, 95, 55, 50, 32], + [87, 111, 114, 100, 48, 95, 55, 51, 32], + [87, 111, 114, 100, 48, 95, 55, 52, 32], + [87, 111, 114, 100, 48, 95, 55, 53, 32], + [87, 111, 114, 100, 48, 95, 55, 54, 32], + [87, 111, 114, 100, 48, 95, 55, 55, 32], + [87, 111, 114, 100, 48, 95, 55, 56, 32], + [87, 111, 114, 100, 48, 95, 55, 57, 32], + [87, 111, 114, 100, 48, 95, 56, 48, 32], + [87, 111, 114, 100, 48, 95, 56, 49, 32], + [87, 111, 114, 100, 48, 95, 56, 50, 32], + [87, 111, 114, 100, 48, 95, 56, 51, 32], + [87, 111, 114, 100, 48, 95, 56, 52, 32], + [87, 111, 114, 100, 48, 95, 56, 53, 32], + [87, 111, 114, 100, 48, 95, 56, 54, 32], + [87, 111, 114, 100, 48, 95, 56, 55, 32], + [87, 111, 114, 100, 48, 95, 56, 56, 32], + [87, 111, 114, 100, 48, 95, 56, 57, 32], + [87, 111, 114, 100, 48, 95, 57, 48, 32], + [87, 111, 114, 100, 48, 95, 57, 49, 32], + [87, 111, 114, 100, 48, 95, 57, 50, 32], + [87, 111, 114, 100, 48, 95, 57, 51, 32], + [87, 111, 114, 100, 48, 95, 57, 52, 32], + [87, 111, 114, 100, 48, 95, 57, 53, 32], + [87, 111, 114, 100, 48, 95, 57, 54, 32], + [87, 111, 114, 100, 48, 95, 57, 55, 32], + [87, 111, 114, 100, 48, 95, 57, 56, 32], + [87, 111, 114, 100, 48, 95, 57, 57, 32] + ], + "sectionIndexes": [ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1 + ], + "hasTab": false, + "hasNewline": false, + "allJoiningScript": false, + "extra": [ + { + "word": [87, 111, 114, 100, 49, 95, 48, 32], + "wordRaw": [87, 111, 114, 100, 49, 95, 48], + "sectionIndex": 2 + }, + { + "word": [87, 111, 114, 100, 49, 95, 49, 32], + "wordRaw": [87, 111, 114, 100, 49, 95, 49], + "sectionIndex": 2 + }, + { + "word": [87, 111, 114, 100, 49, 95, 50, 32], + "wordRaw": [87, 111, 114, 100, 49, 95, 50], + "sectionIndex": 2 + } + ], + "allWordsGenerated": false + }, + "randomTail": 0.7512186642270535 + } +] From f3dab5f40845f871bc9df2bdec2c9d333bdcaf59 Mon Sep 17 00:00:00 2001 From: Lakshmi Tanmay Date: Fri, 23 Oct 2026 05:05:52 +0530 Subject: [PATCH 49/93] Verify prepared and active section requests in built browser Wasm --- crates/typing-engine/tests/content.rs | 53 +++++++++++ docs/rust-engine/progress.md | 4 + frontend/e2e/engine-proof.migration.ts | 126 ++++++++++++++++++++++++- frontend/src/routes/engine-proof.tsx | 87 +++++++++++++++++ 4 files changed, 266 insertions(+), 4 deletions(-) diff --git a/crates/typing-engine/tests/content.rs b/crates/typing-engine/tests/content.rs index c7bcdf75e..76ec59234 100644 --- a/crates/typing-engine/tests/content.rs +++ b/crates/typing-engine/tests/content.rs @@ -340,3 +340,56 @@ fn failure_after_await_uses_reply_time_without_retiming_captured_input() { Some(9900.0) ); } + +#[test] +fn section_generation_and_random_draw_order_match_frozen_typescript() { + use typing_engine::generation::{Assets, Generator}; + let path = std::path::Path::new(env!("CARGO_MANIFEST_DIR")) + .join("../../packages/typing-fixtures/oracle/section-generation.json"); + let cases: Vec = serde_json::from_slice(&std::fs::read(path).unwrap()).unwrap(); + for case in cases { + let mut generator = Generator::with_assets( + serde_json::from_value(case["config"].clone()).unwrap(), + serde_json::from_value(case["language"].clone()).unwrap(), + None, + typing_engine::random::Random::seeded(case["seed"].as_u64().unwrap() as u32), + &mut Assets { + sections: serde_json::from_value(case["sources"].clone()).unwrap(), + ..Assets::default() + }, + ) + .unwrap(); + let mut generated = generator.generate(false).unwrap(); + if case["repeat"] == true { + generated = generator.generate(true).unwrap(); + } + let mut words = generated.words.clone(); + let mut extra = vec![]; + for _ in 0..case["expected"]["extra"].as_array().unwrap().len() { + let strip = |word: &Text| { + let mut word = word.clone(); + if matches!(word.0.last(), Some(32 | 10)) { + word.0.pop(); + } + word + }; + let previous = words.last().map(strip); + let previous2 = words.get(words.len().wrapping_sub(2)).map(strip); + let next = generator + .next(words.len(), 100, previous.as_ref(), previous2.as_ref()) + .unwrap(); + words.push(next.word.clone()); + extra.push(next); + } + let mut actual = serde_json::to_value(generated).unwrap(); + actual["extra"] = serde_json::to_value(extra).unwrap(); + actual["allWordsGenerated"] = serde_json::json!(generator.all_generated()); + assert_eq!(actual, case["expected"], "{}", case["name"]); + assert_eq!( + generator.random_state().clone().draw().unwrap(), + case["randomTail"].as_f64().unwrap(), + "{} RNG", + case["name"] + ); + } +} diff --git a/docs/rust-engine/progress.md b/docs/rust-engine/progress.md index ba25b24b3..3b7356078 100644 --- a/docs/rust-engine/progress.md +++ b/docs/rust-engine/progress.md @@ -120,3 +120,7 @@ do not rerun completed checks without a new reason. - Active section requests now preserve one committed batch (maximum 65,536 units). Background prefetch permits typing while target words remain; only exhausting content blocks input/deletion. Matching replies resume progression and buffered units; reset/end invalidate the request. A failure after awaiting content uses the reply's observed completion time while retaining original input-event times. Seven native content tests cover cleanup, initial resume, active resume, stale replies/reset/expiry and delayed failure. Browser verification pending. - Prepared tab/newline capabilities now include custom content beyond the initial 100-word window; a native regression covers newline acceptance before that future word is generated. + +- New section-generation references cover poetry/article finite/time/repeat, lazy mode and random-case ordering. Native output and subsequent RNG draw match all five exactly; no old snapshots changed. +- Browser proof now also exercises delayed initial section preparation and active batch resume; combined actual-browser run: 6 tests pass, 19.1 seconds. +- Section exports increased release-profile Wasm to 854,263 raw / 310,646 gzip level 6, above gzip budget. A dedicated `wasm` Cargo profile (`opt-level=z`) plus existing Binaryen produces 748,995 / 288,742 bytes. Switching the build entry point to this profile; full browser/boundary retest pending. diff --git a/frontend/e2e/engine-proof.migration.ts b/frontend/e2e/engine-proof.migration.ts index 17e319813..02064bb6c 100644 --- a/frontend/e2e/engine-proof.migration.ts +++ b/frontend/e2e/engine-proof.migration.ts @@ -125,7 +125,7 @@ test("native fixture matches browser Wasm through built Cloudflare assets", asyn } }); -test("all 61 unchanged generator snapshots match browser Wasm", async ({ +test("all 61 original and 5 section generator references match browser Wasm", async ({ page, }) => { const staticRoot = resolve(import.meta.dirname, "../static"); @@ -214,6 +214,7 @@ test("all 61 unchanged generator snapshots match browser Wasm", async ({ polyglot, selectedQuoteId: fixture.selectedQuoteId ?? 1, favoriteQuoteIds: [], + sections: [] as unknown[], }, repeat: fixture.repeat, extraWords: fixture.extraWords, @@ -229,6 +230,37 @@ test("all 61 unchanged generator snapshots match browser Wasm", async ({ ), ); } + const names = cases.map((fixture) => fixture.name); + const sections = JSON.parse( + await readFile(resolve(root, "oracle/section-generation.json"), "utf8"), + ) as { + name: string; + config: Record; + language: unknown; + seed: number; + sources: unknown[]; + repeat: boolean; + expected: Record & { extra: unknown[] }; + }[]; + for (const fixture of sections) { + inputs.push({ + input: { + config: fixture.config, + language: fixture.language, + custom: null, + random: { kind: "seeded", state: fixture.seed }, + quotes: null, + polyglot: [], + selectedQuoteId: 1, + favoriteQuoteIds: [], + sections: fixture.sources, + }, + repeat: fixture.repeat, + extraWords: fixture.expected.extra.length, + }); + expected.push({ ...fixture.expected, currentQuote: null }); + names.push(fixture.name); + } await page.goto("/engine-proof"); await page .getByRole("textbox", { name: "Command fixture" }) @@ -241,9 +273,9 @@ test("all 61 unchanged generator snapshots match browser Wasm", async ({ const actual = JSON.parse( await page.getByTestId("engine-proof-result").innerText(), ) as unknown[]; - expect(actual).toHaveLength(61); - for (const [index, fixture] of cases.entries()) { - expect(actual[index], fixture.name).toEqual(expected[index]); + expect(actual).toHaveLength(66); + for (const [index, name] of names.entries()) { + expect(actual[index], name).toEqual(expected[index]); } }); @@ -359,3 +391,89 @@ test("historical results, replay and practice agree with frozen references in Wa expect(actual[index], reference.name).toEqual(expected[index]); } }); + +test("typed section requests preserve prepared and active input through browser Wasm", async ({ + page, +}) => { + const units = (text: string): number[] => + Array.from({ length: text.length }, (_, i) => text.charCodeAt(i)); + const poem = (text: string): unknown => ({ + kind: "poem", + title: units("Title"), + author: units("Author"), + lines: [units(text)], + }); + const input = { + prepare: { + config: { mode: "time", time: 0, funbox: ["poetry"] }, + language: { name: "english", words: [units("base")] }, + custom: null, + random: { kind: "seeded", state: 1 }, + quotes: null, + polyglot: [], + sections: [], + selectedQuoteId: 1, + favoriteQuoteIds: [], + }, + initialReplies: [poem(Array.from({ length: 100 }, () => "one").join(" "))], + steps: [ + { + command: { op: "insert", data: units("one "), now: 0, date: 1_000_000 }, + }, + { + command: { + op: "insert", + data: units("one ".repeat(100)), + now: 100, + date: 1_000_100, + }, + }, + { source: poem("one two three"), now: 1000, date: 1_001_000 }, + ], + }; + await page.goto("/engine-proof"); + await page + .getByRole("textbox", { name: "Command fixture" }) + .fill(JSON.stringify(input)); + await page.getByRole("button", { name: "Run prepared session" }).click(); + await expect(page.getByTestId("engine-proof-result")).not.toBeEmpty(); + await expect(page.getByTestId("engine-proof-error")).toBeEmpty(); + const result = JSON.parse( + await page.getByTestId("engine-proof-result").innerText(), + ) as { + requests: { id: number; generation: number; kind: string }[]; + updates: { + state: { wordIndex: number }; + awaitingContent: boolean; + contentRequest?: { id: number; generation: number; kind: string }; + }[]; + snapshot: { words: number[][] }; + log: { events: { type: string; testMs: number }[] }; + }; + expect(result.requests).toEqual([ + { id: 1, generation: 0, kind: "poetry", language: "english" }, + ]); + expect( + result.updates.map((update) => [ + update.state.wordIndex, + update.awaitingContent, + ]), + ).toEqual([ + [1, false], + [99, true], + [101, false], + ]); + expect(result.updates[0]?.contentRequest).toEqual({ + id: 2, + generation: 0, + kind: "poetry", + language: "english", + }); + expect(result.updates[1]?.contentRequest).toEqual( + result.updates[0]?.contentRequest, + ); + expect(result.snapshot.words[101]).toEqual(units("two ")); + const events = result.log.events.filter((event) => event.type === "input"); + expect(events).toHaveLength(404); + expect(events.at(-1)?.testMs).toBe(100); +}); diff --git a/frontend/src/routes/engine-proof.tsx b/frontend/src/routes/engine-proof.tsx index 2be0a2438..6aec7bc23 100644 --- a/frontend/src/routes/engine-proof.tsx +++ b/frontend/src/routes/engine-proof.tsx @@ -6,6 +6,9 @@ import type { Prepare, Log, Query, + Source, + ContentRequest, + Update, } from "@oxytype/typing-engine/browser.client"; import { createFileRoute } from "@tanstack/solid-router"; @@ -174,6 +177,82 @@ function EngineProof() { setFailure(error instanceof Error ? error.message : String(error)); } }; + const runPrepared = async (): Promise => { + setResult(""); + setFailure(""); + try { + const input = JSON.parse(fixture()) as { + prepare: Prepare; + initialReplies: Source[]; + steps: ( + | { command: Command } + | { source: Source; now: number; date: number } + )[]; + }; + const { loadEngine } = + await import("@oxytype/typing-engine/browser.client"); + const engine = await loadEngine(); + if (disposed) return; + const content = engine.createContent(); + let session: BrowserSession | undefined; + try { + content.prepare(input.prepare); + const requests: ContentRequest[] = []; + for (let reply = 0; reply <= input.initialReplies.length; reply++) { + try { + session = content.createSession(); + break; + } catch (error) { + const request = content.request(); + const source = input.initialReplies[reply]; + if (request === null || source === undefined) throw error; + requests.push(request); + content.supply(request, source); + } + } + if (session === undefined) { + throw new Error("Prepared session unavailable"); + } + handles.add(session); + const updates: Update[] = []; + for (const step of input.steps) { + if ("command" in step) { + updates.push(session.command(step.command)); + } else { + const request = updates.at(-1)?.contentRequest; + if (request === undefined) { + throw new Error("Missing active content request"); + } + updates.push( + session.command({ + op: "supplyContent", + request, + source: step.source, + now: step.now, + date: step.date, + }), + ); + } + } + setResult( + JSON.stringify({ + requests, + updates, + snapshot: session.snapshot(), + log: session.log(), + }), + ); + } finally { + if (session !== undefined) { + session.dispose(); + handles.delete(session); + } + content.dispose(); + } + } catch (error) { + setFailure(error instanceof Error ? error.message : String(error)); + } + }; return ( Run analysis +
{result()}
{failure()}
{benchmark()}
From e75a43eca3362ebb0ab3164f20a7db56d0258f59 Mon Sep 17 00:00:00 2001 From: Lakshmi Tanmay Date: Fri, 23 Oct 2026 05:08:23 +0530 Subject: [PATCH 50/93] Use a measured size-optimized Wasm profile within frozen payload budgets --- Cargo.toml | 4 ++ .../profile-z-proof-performance.json | 58 +++++++++++++++++++ docs/rust-engine/progress.md | 2 + scripts/engine.ts | 5 +- 4 files changed, 67 insertions(+), 2 deletions(-) create mode 100644 docs/rust-engine/profile-z-proof-performance.json diff --git a/Cargo.toml b/Cargo.toml index 657027a0b..c1fd99039 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,3 +29,7 @@ lto = true codegen-units = 1 opt-level = "s" strip = "debuginfo" + +[profile.wasm] +inherits = "release" +opt-level = "z" diff --git a/docs/rust-engine/profile-z-proof-performance.json b/docs/rust-engine/profile-z-proof-performance.json new file mode 100644 index 000000000..e81319c08 --- /dev/null +++ b/docs/rust-engine/profile-z-proof-performance.json @@ -0,0 +1,58 @@ +{ + "fixture": "built Cloudflare Start; Desktop Chrome; 50 warmed samples per boundary; 200 UTF-16 units per sample; 7 real keyboard commits with double-rAF paint", + "browser": "153.0.8010.12", + "wasmBytes": 748995, + "wasmHeaders": { + "transfer-encoding": "chunked", + "cache-control": "public, max-age=31536000, immutable", + "content-encoding": "gzip", + "cf-cache-status": "HIT", + "etag": "\"2e37ebf61d290b865bf979e0a9886442\"", + "content-type": "application/wasm", + "x-content-type-options": "nosniff" + }, + "inputToPaint": [ + 19.799999997019768, 25.299999997019768, 22.69999998807907, + 22.599999994039536, 22.100000008940697, 26.900000005960464, 36 + ], + "benchmark": { + "initializationMs": 30, + "jsonMs": [ + 0.008000000044703484, 0.008000000044703484, 0.0075, 0.0075, + 0.006999999955296516, 0.007000000029802322, 0.0075, 0.007000000029802322, + 0.007000000029802322, 0.006999999955296516, 0.0075, 0.006499999985098839, + 0.006499999985098839, 0.006499999985098839, 0.007000000029802322, + 0.007000000029802322, 0.006499999985098839, 0.0075, 0.006499999985098839, + 0.007000000029802322, 0.006000000014901161, 0.006999999955296516, + 0.005999999940395355, 0.006499999985098839, 0.006500000059604645, + 0.006000000014901161, 0.007000000029802322, 0.006499999985098839, + 0.006500000059604645, 0.006999999955296516, 0.006499999985098839, + 0.006499999985098839, 0.006499999985098839, 0.007000000029802322, + 0.006000000014901161, 0.005999999940395355, 0.006000000014901161, + 0.006500000059604645, 0.006499999985098839, 0.006499999985098839, + 0.006499999985098839, 0.005999999940395355, 0.005999999940395355, + 0.006999999955296516, 0.006000000014901161, 0.006499999985098839, + 0.006499999985098839, 0.005999999940395355, 0.006499999985098839, + 0.006499999985098839 + ], + "objectMs": [ + 0.012999999970197677, 0.010499999970197677, 0.010499999970197677, 0.01, + 0.009500000029802322, 0.009500000029802322, 0.009500000029802322, + 0.010499999970197677, 0.009500000029802322, 0.009499999955296516, + 0.00850000001490116, 0.008499999940395355, 0.00850000001490116, + 0.00850000001490116, 0.01, 0.00899999998509884, 0.00850000001490116, + 0.008499999940395355, 0.008000000044703484, 0.00850000001490116, + 0.008499999940395355, 0.00850000001490116, 0.008000000044703484, + 0.008499999940395355, 0.007999999970197678, 0.00850000001490116, + 0.00850000001490116, 0.00850000001490116, 0.00899999998509884, + 0.00850000001490116, 0.00850000001490116, 0.007999999970197678, + 0.008499999940395355, 0.00850000001490116, 0.00850000001490116, + 0.00850000001490116, 0.00899999998509884, 0.007999999970197678, + 0.008000000044703484, 0.008000000044703484, 0.00850000001490116, + 0.007999999970197678, 0.007999999970197678, 0.008499999940395355, + 0.00850000001490116, 0.008000000044703484, 0.007999999970197678, + 0.007999999970197678, 0.00850000001490116, 0.00850000001490116 + ], + "memoryBytes": 1376256 + } +} diff --git a/docs/rust-engine/progress.md b/docs/rust-engine/progress.md index 3b7356078..92e7ba51a 100644 --- a/docs/rust-engine/progress.md +++ b/docs/rust-engine/progress.md @@ -124,3 +124,5 @@ do not rerun completed checks without a new reason. - New section-generation references cover poetry/article finite/time/repeat, lazy mode and random-case ordering. Native output and subsequent RNG draw match all five exactly; no old snapshots changed. - Browser proof now also exercises delayed initial section preparation and active batch resume; combined actual-browser run: 6 tests pass, 19.1 seconds. - Section exports increased release-profile Wasm to 854,263 raw / 310,646 gzip level 6, above gzip budget. A dedicated `wasm` Cargo profile (`opt-level=z`) plus existing Binaryen produces 748,995 / 288,742 bytes. Switching the build entry point to this profile; full browser/boundary retest pending. + +- Dedicated Wasm `z` profile passes the actual Cloudflare browser proof: all 61 original + 5 section references, historical derivation, lifecycle/content requests, and both web behavior contracts (6 tests, 19.8 seconds). Boundary sample: JSON median 0.0065 ms / p95 0.0075 ms per UTF-16 unit; object p95 0.0105 ms; initialization 30 ms (single sample); 7 keyboard input-to-paint maximum 36 ms; Wasm memory 1,376,256 bytes. Raw evidence: profile-z-proof-performance.json. Full initialization distribution/soak/performance acceptance remains pending. diff --git a/scripts/engine.ts b/scripts/engine.ts index b95d99ea5..f859d7722 100644 --- a/scripts/engine.ts +++ b/scripts/engine.ts @@ -29,7 +29,8 @@ switch (process.argv[2]) { run("cargo", [ "build", "--locked", - "--release", + "--profile", + "wasm", "--target", "wasm32-unknown-unknown", "-p", @@ -45,7 +46,7 @@ switch (process.argv[2]) { "engine", resolve( root, - "target/wasm32-unknown-unknown/release/typing_engine_wasm.wasm", + "target/wasm32-unknown-unknown/wasm/typing_engine_wasm.wasm", ), ]); const binaryen = dirname( From 77006a5cc050e8ced3670d88bf699883ebd481f8 Mon Sep 17 00:00:00 2001 From: Lakshmi Tanmay Date: Fri, 23 Oct 2026 05:10:09 +0530 Subject: [PATCH 51/93] Keep completion decisions in Rust with explicit host timing acknowledgement --- crates/typing-engine/src/session.rs | 94 +++++++++++++--- crates/typing-engine/tests/completion.rs | 133 +++++++++++++++++++++++ docs/rust-engine/contract.md | 4 + docs/rust-engine/progress.md | 2 + 4 files changed, 219 insertions(+), 14 deletions(-) create mode 100644 crates/typing-engine/tests/completion.rs diff --git a/crates/typing-engine/src/session.rs b/crates/typing-engine/src/session.rs index 27b18b4c5..5eda9917c 100644 --- a/crates/typing-engine/src/session.rs +++ b/crates/typing-engine/src/session.rs @@ -235,6 +235,14 @@ pub enum Command { earliest_visible_word: Option, #[serde(default)] defer_automatic: Option, + #[serde(default)] + #[ts(optional)] + defer_finish: Option, + }, + AcknowledgeFinish { + now: f64, + #[serde(default)] + date: f64, }, #[serde(rename_all = "camelCase")] SupplyContent { @@ -279,6 +287,12 @@ pub struct Preflight { pub measure_overflow: bool, pub normalized: Text, } +#[derive(Clone, Serialize, TS)] +pub struct FinishRequest { + #[serde(skip_serializing_if = "Option::is_none")] + #[ts(optional)] + pub reason: Option, +} #[derive(Clone, Debug, Serialize, Deserialize, TS)] #[serde(rename_all = "camelCase")] pub struct State { @@ -321,6 +335,9 @@ pub struct Update { pub content_request: Option, #[serde(rename = "awaitingContent")] pub awaiting_content: bool, + #[serde(skip_serializing_if = "Option::is_none")] + #[ts(optional)] + pub finish: Option, } #[derive(Clone, Serialize, TS)] #[serde(rename_all = "camelCase")] @@ -339,6 +356,9 @@ pub struct Snapshot { pub has_tab: bool, pub has_newline: bool, pub current_quote: Option, + #[serde(skip_serializing_if = "Option::is_none")] + #[ts(optional)] + pub finish: Option, } #[derive(Clone)] @@ -403,6 +423,9 @@ pub struct Session { has_newline: bool, pending_commit: Option, deferred_input: Option, + defer_finish: bool, + pending_finish: Option, + pending_finish_at: f64, } impl Session { @@ -451,6 +474,9 @@ impl Session { has_newline, pending_commit: None, deferred_input: None, + defer_finish: false, + pending_finish: None, + pending_finish_at: 0.0, }) } @@ -500,6 +526,7 @@ impl Session { }; let blocked = self.ended || self.pending_commit.is_some() + || self.pending_finish.is_some() || crate::input::blocked(&self.config, &observation, self.has_newline); let display_length = target.len() - usize::from(target.0.last() == Some(&32)); let measure_overflow = !blocked @@ -526,6 +553,7 @@ impl Session { has_tab: self.has_tab, has_newline: self.has_newline, current_quote: self.quote.clone(), + finish: self.pending_finish.clone(), } } fn strip_final_separator(&mut self) { @@ -614,6 +642,17 @@ impl Session { json!({"event":"start", "timer":0, "date":date}), ); } + fn finish(&mut self, now: f64, date: f64, reason: Option) { + if !self.active || self.ended || self.pending_finish.is_some() { + return; + } + if self.defer_finish { + self.pending_finish = Some(FinishRequest { reason }); + self.pending_finish_at = now; + } else { + self.end(now, date, reason); + } + } fn end(&mut self, now: f64, date: f64, reason: Option) { if !self.active || self.ended { return; @@ -621,6 +660,7 @@ impl Session { self.active = false; self.ended = true; self.reason = reason; + self.pending_finish = None; self.pending_commit = None; self.deferred_input = None; if let Some(generator) = &mut self.generator { @@ -646,7 +686,8 @@ impl Session { | Command::Configure { now, .. } | Command::CustomLimit { now, .. } | Command::Bailout { now, .. } - | Command::SupplyContent { now, .. } => *now, + | Command::SupplyContent { now, .. } + | Command::AcknowledgeFinish { now, .. } => *now, }; if !now.is_finite() || now.abs() > 9_007_199_254_740_991.0 { return Err("Invalid timestamp".into()); @@ -668,6 +709,7 @@ impl Session { | Command::Abandon { date, .. } | Command::Bailout { date, .. } | Command::SupplyContent { date, .. } + | Command::AcknowledgeFinish { date, .. } if !date.is_finite() => { return Err("Invalid wall-clock date".into()); @@ -715,6 +757,7 @@ impl Session { .and_then(|generator| generator.pending_request()) .cloned(), awaiting_content: self.pending_commit.is_some(), + finish: self.pending_finish.clone(), }); } self.process_input(DeferredInput { @@ -796,10 +839,17 @@ impl Session { } Command::Bailout { now, date } => { self.bailed_out = true; - self.end(now, date, None); + self.finish(now, date, None); } Command::Start { now, date } => self.start(now, date), - Command::Finish { now, date, reason } => self.end(now, date, reason), + Command::Finish { now, date, reason } => self.finish(now, date, reason), + Command::AcknowledgeFinish { now, date } => { + if self.pending_finish.is_none() || now < self.pending_finish_at { + return Err("Invalid completion acknowledgement".into()); + } + let finish = self.pending_finish.take().unwrap(); + self.end(now, date, finish.reason); + } Command::End { now, date } | Command::Abandon { now, date } => { self.end(now, date, None) } @@ -809,6 +859,7 @@ impl Session { self.bailed_out = false; self.pending_commit = None; self.deferred_input = None; + self.pending_finish = None; self.active = false; self.ended = false; self.reason = None; @@ -837,16 +888,21 @@ impl Session { Command::View { earliest_visible_word, defer_automatic, + defer_finish, .. } => { self.earliest_visible_word = earliest_visible_word.unwrap_or(0); if let Some(defer) = defer_automatic { self.defer_automatic = defer; } + if let Some(defer) = defer_finish { + self.defer_finish = defer; + } } Command::FlushAutomatic { generation, .. } => { if generation == self.generation && !self.ended + && self.pending_finish.is_none() && let Some((now, date)) = self.automatic.pop_front() { self.completion_checkpoint = !self.composing; @@ -877,11 +933,13 @@ impl Session { .and_then(|generator| generator.pending_request()) .cloned(), awaiting_content: self.pending_commit.is_some(), + finish: self.pending_finish.clone(), }) } fn process_input(&mut self, mut input: DeferredInput) { while !self.ended + && self.pending_finish.is_none() && let Some(unit) = input.units.pop_front() { self.completion_checkpoint = @@ -916,7 +974,7 @@ impl Session { self.drain_indentation(now, date); } fn drain_indentation(&mut self, now: f64, date: f64) { - if self.pending_commit.is_some() || self.ended { + if self.pending_commit.is_some() || self.ended || self.pending_finish.is_some() { return; } if self.defer_automatic && self.indent_pending { @@ -924,7 +982,11 @@ impl Session { self.automatic.push_back((now, date)); return; } - while self.indent_pending && !self.ended && self.pending_commit.is_none() { + while self.indent_pending + && !self.ended + && self.pending_commit.is_none() + && self.pending_finish.is_none() + { self.indent_pending = false; self.insert_one(9, now, date, None, true, false); if self.defer_automatic && self.indent_pending { @@ -1062,7 +1124,7 @@ impl Session { return; } } else { - self.end(now, date, Some("word generation error".into())); + self.finish(now, date, Some("word generation error".into())); return; } } @@ -1097,7 +1159,7 @@ impl Session { && !(input.is_empty() && (is_space(unit) || unit == 10)) && !word_correct)) { - self.end(finish_now, finish_date, Some("difficulty".into())); + self.finish(finish_now, finish_date, Some("difficulty".into())); return; } if checkpoint && progressed && self.config.min_burst != "off" { @@ -1121,7 +1183,7 @@ impl Session { self.config.min_burst_custom_speed }) { - self.end(finish_now, finish_date, Some("min burst".into())); + self.finish(finish_now, finish_date, Some("min burst".into())); return; } } @@ -1135,7 +1197,7 @@ impl Session { && finite && (word_correct || quick || advance) { - self.end(finish_now, finish_date, None); + self.finish(finish_now, finish_date, None); } if !self.ended && correct @@ -1204,7 +1266,11 @@ impl Session { } fn delete(&mut self, kind: &str, now: f64, observed: Option, sentinel_removed: bool) { - if !self.active || self.ended || self.pending_commit.is_some() { + if !self.active + || self.ended + || self.pending_commit.is_some() + || self.pending_finish.is_some() + { return; } let input = self.current(); @@ -1282,7 +1348,7 @@ impl Session { } fn advance(&mut self, now: f64, date: f64) { - if !self.active { + if !self.active || self.pending_finish.is_some() { return; } let start = self.start.unwrap_or(now); @@ -1291,7 +1357,7 @@ impl Session { return; } let due = ((now - start) / 1000.0).floor() as u32; - while self.active && self.ticks < due { + while self.active && self.pending_finish.is_none() && self.ticks < due { self.ticks += 1; let mut data = json!({"event":"step","timer":self.ticks}); if self.ticks < due { @@ -1330,7 +1396,7 @@ impl Session { None }; if let Some(reason) = reason { - self.end(now, date, Some(reason.into())); + self.finish(now, date, Some(reason.into())); } } let limit = if self.config.mode == "time" { @@ -1344,7 +1410,7 @@ impl Session { None }; if limit.is_some_and(|limit| limit > 0.0 && f64::from(self.ticks) >= limit) { - self.end(now, date, None); + self.finish(now, date, None); } } } diff --git a/crates/typing-engine/tests/completion.rs b/crates/typing-engine/tests/completion.rs new file mode 100644 index 000000000..b008c9092 --- /dev/null +++ b/crates/typing-engine/tests/completion.rs @@ -0,0 +1,133 @@ +use serde_json::json; +use typing_engine::{Config, Session, Text}; +fn command( + session: &mut Session, + value: serde_json::Value, +) -> Result { + session.apply(serde_json::from_value(value).unwrap()) +} +fn session() -> Session { + let mut session = Session::new( + Config { + mode: "words".into(), + words: 1, + ..Config::default() + }, + vec![Text::from("a")], + ) + .unwrap(); + command( + &mut session, + json!({"op":"view","now":0,"deferFinish":true}), + ) + .unwrap(); + session +} +#[test] +fn host_observes_completion_time_without_deciding_completion() { + let mut session = session(); + let update = command( + &mut session, + json!({"op":"insert","data":[97],"now":1000,"date":1000000}), + ) + .unwrap(); + assert!(update.finish.is_some()); + assert!(!update.state.ended); + assert!( + session + .preflight(Text::from("x"), &Default::default()) + .blocked + ); + let log = session.log(); + assert!( + command(&mut session, json!({"op":"insert","data":[120],"now":1020})) + .unwrap() + .events + .is_empty() + ); + command( + &mut session, + json!({"op":"advance","now":5000,"date":1004000}), + ) + .unwrap(); + assert_eq!(log, session.log()); + assert!( + command( + &mut session, + json!({"op":"acknowledgeFinish","now":999,"date":1000000}) + ) + .is_err() + ); + assert_eq!(log, session.log()); + let update = command( + &mut session, + json!({"op":"acknowledgeFinish","now":1050,"date":1000050}), + ) + .unwrap(); + assert!(update.state.ended); + assert!(update.finish.is_none()); + assert_eq!( + session.log()["events"].as_array().unwrap().last().unwrap()["testMs"].as_f64(), + Some(50.0) + ); + let log = session.log(); + assert!( + command( + &mut session, + json!({"op":"acknowledgeFinish","now":1060,"date":1000060}) + ) + .is_err() + ); + assert_eq!(log, session.log()); +} +#[test] +fn reset_cancels_completion_acknowledgement_without_ending_a_new_test() { + let mut session = session(); + command(&mut session, json!({"op":"insert","data":[97],"now":0})).unwrap(); + command(&mut session, json!({"op":"reset","now":1})).unwrap(); + assert!(command(&mut session, json!({"op":"acknowledgeFinish","now":2})).is_err()); + assert!(!session.state().ended); + assert!(!session.state().active); + assert!(session.log()["events"].as_array().unwrap().is_empty()); +} +#[test] +fn failure_and_timer_completion_keep_their_engine_reason_until_acknowledged() { + for timed in [false, true] { + let mut session = Session::new( + Config { + mode: if timed { "time" } else { "words" }.into(), + time: 1.0, + words: 1, + difficulty: "master".into(), + ..Config::default() + }, + vec![Text::from("a")], + ) + .unwrap(); + command( + &mut session, + json!({"op":"view","now":0,"deferFinish":true}), + ) + .unwrap(); + let update = command( + &mut session, + json!({"op":"insert","data":[if timed{97}else{120}],"now":0}), + ) + .unwrap(); + let update = if timed { + command(&mut session, json!({"op":"advance","now":1000})).unwrap() + } else { + update + }; + assert_eq!( + update.finish.unwrap().reason.as_deref(), + if timed { None } else { Some("difficulty") } + ); + let update = command(&mut session, json!({"op":"acknowledgeFinish","now":1100})).unwrap(); + assert!(update.state.ended); + assert_eq!( + update.state.reason.as_deref(), + if timed { None } else { Some("difficulty") } + ); + } +} diff --git a/docs/rust-engine/contract.md b/docs/rust-engine/contract.md index 1d8a01f6d..03c9c6054 100644 --- a/docs/rust-engine/contract.md +++ b/docs/rust-engine/contract.md @@ -121,3 +121,7 @@ fixtures: commands/*.json, with frozen TypeScript frames in oracle/*.json. Compare every command's state and events, then final derived values. A harness self-test must reject a deliberately altered word index. New captures refuse to overwrite files. Existing snapshots are never updated by the harness. + +## Observed web completion + +`view {deferFinish:true}` enables a finish-request/acknowledgement handshake. Rust decides completion and returns `finish` in the update; hosts disable input and stop scheduling, perform the same platform navigation/hooks as before, then send `acknowledgeFinish {now,date}`. The end event uses that explicit observation, preserving the website's later completion clock without a hidden Rust clock. New typing/ticks cannot change the decision while awaiting acknowledgement. Reset/end cancel it; stale/duplicate acknowledgement is an error. Native callers can keep immediate completion. diff --git a/docs/rust-engine/progress.md b/docs/rust-engine/progress.md index 92e7ba51a..551ed546c 100644 --- a/docs/rust-engine/progress.md +++ b/docs/rust-engine/progress.md @@ -126,3 +126,5 @@ do not rerun completed checks without a new reason. - Section exports increased release-profile Wasm to 854,263 raw / 310,646 gzip level 6, above gzip budget. A dedicated `wasm` Cargo profile (`opt-level=z`) plus existing Binaryen produces 748,995 / 288,742 bytes. Switching the build entry point to this profile; full browser/boundary retest pending. - Dedicated Wasm `z` profile passes the actual Cloudflare browser proof: all 61 original + 5 section references, historical derivation, lifecycle/content requests, and both web behavior contracts (6 tests, 19.8 seconds). Boundary sample: JSON median 0.0065 ms / p95 0.0075 ms per UTF-16 unit; object p95 0.0105 ms; initialization 30 ms (single sample); 7 keyboard input-to-paint maximum 36 ms; Wasm memory 1,376,256 bytes. Raw evidence: profile-z-proof-performance.json. Full initialization distribution/soak/performance acceptance remains pending. + +- Added opt-in host-observed finish acknowledgement: Rust requests completion/failure and blocks further input/ticks; the host supplies actual completion time after its navigation/hooks. Duplicate/stale/earlier acknowledgements are rejected. Native tests cover score-timing origin, blocked input, timer/failure reasons and reset cancellation. Default immediate sessions retain all command-oracle behavior. Browser proof for acknowledgement still pending. From c9436fbbc500ba347f34a8a180076a579ff85aa1 Mon Sep 17 00:00:00 2001 From: Lakshmi Tanmay Date: Fri, 23 Oct 2026 05:32:10 +0530 Subject: [PATCH 52/93] Preserve browser slow-render failure thresholds and tick ordering in Rust --- crates/typing-engine/src/session.rs | 53 +++++++++ crates/typing-engine/tests/completion.rs | 6 +- crates/typing-engine/tests/slow_timer.rs | 104 ++++++++++++++++++ docs/rust-engine/progress.md | 11 ++ .../e2e/engine-input-contract.migration.ts | 36 ++++++ .../src/features/typing/runtime.client.tsx | 7 +- 6 files changed, 212 insertions(+), 5 deletions(-) create mode 100644 crates/typing-engine/tests/slow_timer.rs diff --git a/crates/typing-engine/src/session.rs b/crates/typing-engine/src/session.rs index 5eda9917c..abe892d37 100644 --- a/crates/typing-engine/src/session.rs +++ b/crates/typing-engine/src/session.rs @@ -238,6 +238,9 @@ pub enum Command { #[serde(default)] #[ts(optional)] defer_finish: Option, + #[serde(default)] + #[ts(optional)] + monitor_render_drift: Option, }, AcknowledgeFinish { now: f64, @@ -338,6 +341,11 @@ pub struct Update { #[serde(skip_serializing_if = "Option::is_none")] #[ts(optional)] pub finish: Option, + #[serde(rename = "slowTimer")] + pub slow_timer: bool, + #[serde(skip_serializing_if = "Option::is_none")] + #[ts(optional)] + pub drift: Option, } #[derive(Clone, Serialize, TS)] #[serde(rename_all = "camelCase")] @@ -426,6 +434,10 @@ pub struct Session { defer_finish: bool, pending_finish: Option, pending_finish_at: f64, + monitor_render_drift: bool, + slow_timer: bool, + slow_timer_count: u8, + last_drift: Option, } impl Session { @@ -477,6 +489,10 @@ impl Session { defer_finish: false, pending_finish: None, pending_finish_at: 0.0, + monitor_render_drift: false, + slow_timer: false, + slow_timer_count: 0, + last_drift: None, }) } @@ -531,6 +547,7 @@ impl Session { let display_length = target.len() - usize::from(target.0.last() == Some(&32)); let measure_overflow = !blocked && !layout.slow_timer + && !self.slow_timer && observation.data.len() == 1 && !self.config.blind_mode && !self.config.hide_extra_letters @@ -634,6 +651,8 @@ impl Session { return; } self.active = true; + self.slow_timer = false; + self.slow_timer_count = 0; self.start = Some(now); self.pace.start(now); self.record( @@ -660,6 +679,8 @@ impl Session { self.active = false; self.ended = true; self.reason = reason; + self.slow_timer = false; + self.slow_timer_count = 0; self.pending_finish = None; self.pending_commit = None; self.deferred_input = None; @@ -728,6 +749,7 @@ impl Session { let invalidated = matches!(command, Command::Finalize { .. } | Command::Reset { .. }); self.word_changes.clear(); self.visual_input_override = None; + self.last_drift = None; match command { Command::Insert { data, @@ -758,6 +780,8 @@ impl Session { .cloned(), awaiting_content: self.pending_commit.is_some(), finish: self.pending_finish.clone(), + slow_timer: self.slow_timer, + drift: None, }); } self.process_input(DeferredInput { @@ -860,6 +884,8 @@ impl Session { self.pending_commit = None; self.deferred_input = None; self.pending_finish = None; + self.slow_timer = false; + self.slow_timer_count = 0; self.active = false; self.ended = false; self.reason = None; @@ -889,6 +915,7 @@ impl Session { earliest_visible_word, defer_automatic, defer_finish, + monitor_render_drift, .. } => { self.earliest_visible_word = earliest_visible_word.unwrap_or(0); @@ -898,6 +925,9 @@ impl Session { if let Some(defer) = defer_finish { self.defer_finish = defer; } + if let Some(monitor) = monitor_render_drift { + self.monitor_render_drift = monitor; + } } Command::FlushAutomatic { generation, .. } => { if generation == self.generation @@ -934,6 +964,8 @@ impl Session { .cloned(), awaiting_content: self.pending_commit.is_some(), finish: self.pending_finish.clone(), + slow_timer: self.slow_timer, + drift: self.last_drift, }) } @@ -1356,10 +1388,31 @@ impl Session { if drift < 0.0 { return; } + self.last_drift = Some(drift); + if self.monitor_render_drift + && ((self.config.mode == "time" && self.config.time > 0.0 && self.config.time < 130.0) + || (self.config.mode == "words" + && self.config.words > 0 + && self.config.words < 250)) + { + if drift > 125.0 { + self.slow_timer = true; + } + if drift > 250.0 { + self.slow_timer_count = self.slow_timer_count.saturating_add(1); + } + if drift > 500.0 || self.slow_timer_count > 5 { + self.finish(now, date, Some("slow timer".into())); + return; + } + } let due = ((now - start) / 1000.0).floor() as u32; while self.active && self.pending_finish.is_none() && self.ticks < due { self.ticks += 1; let mut data = json!({"event":"step","timer":self.ticks}); + if self.slow_timer { + data["slowTimer"] = json!(true); + } if self.ticks < due { data["catchup"] = json!(true); } else { diff --git a/crates/typing-engine/tests/completion.rs b/crates/typing-engine/tests/completion.rs index b008c9092..b6c4bd97e 100644 --- a/crates/typing-engine/tests/completion.rs +++ b/crates/typing-engine/tests/completion.rs @@ -61,20 +61,20 @@ fn host_observes_completion_time_without_deciding_completion() { assert_eq!(log, session.log()); let update = command( &mut session, - json!({"op":"acknowledgeFinish","now":1050,"date":1000050}), + json!({"op":"acknowledgeFinish","now":5050,"date":1004050}), ) .unwrap(); assert!(update.state.ended); assert!(update.finish.is_none()); assert_eq!( session.log()["events"].as_array().unwrap().last().unwrap()["testMs"].as_f64(), - Some(50.0) + Some(4050.0) ); let log = session.log(); assert!( command( &mut session, - json!({"op":"acknowledgeFinish","now":1060,"date":1000060}) + json!({"op":"acknowledgeFinish","now":5060,"date":1004060}) ) .is_err() ); diff --git a/crates/typing-engine/tests/slow_timer.rs b/crates/typing-engine/tests/slow_timer.rs new file mode 100644 index 000000000..06c72e6c6 --- /dev/null +++ b/crates/typing-engine/tests/slow_timer.rs @@ -0,0 +1,104 @@ +use serde_json::json; +use typing_engine::{Config, Session, Text}; +fn apply(session: &mut Session, value: serde_json::Value) -> typing_engine::session::Update { + session + .apply(serde_json::from_value(value).unwrap()) + .unwrap() +} +fn monitored(config: Config) -> Session { + let mut session = Session::new(config, vec![Text::from("a ")]).unwrap(); + apply( + &mut session, + json!({"op":"view","now":0,"monitorRenderDrift":true}), + ); + apply(&mut session, json!({"op":"start","now":0})); + session +} +#[test] +fn web_slow_timer_thresholds_are_strict_and_failure_precedes_ticks() { + for drift in [125.0, 126.0, 250.0, 251.0, 500.0, 501.0] { + let mut session = monitored(Config::default()); + let update = apply(&mut session, json!({"op":"advance","now":1000.0+drift})); + assert_eq!(update.drift, Some(drift)); + assert_eq!(update.state.ended, drift > 500.0); + if drift <= 500.0 { + assert_eq!(update.slow_timer, drift > 125.0); + } + let log = session.log(); + let events = log["events"].as_array().unwrap(); + let steps: Vec<_> = events + .iter() + .filter(|event| event["type"] == "timer" && event["data"]["event"] == "step") + .collect(); + assert_eq!(steps.len(), usize::from(drift <= 500.0)); + if drift > 500.0 { + assert_eq!(update.state.reason.as_deref(), Some("slow timer")); + } else if drift > 125.0 { + assert_eq!(steps[0]["data"]["slowTimer"], true); + } + } +} +#[test] +fn six_slow_callbacks_fail_and_reset_clears_counter() { + let mut session = monitored(Config::default()); + for tick in 1..=6 { + let update = apply( + &mut session, + json!({"op":"advance","now":f64::from(tick)*1000.0+251.0}), + ); + assert_eq!(update.state.ended, tick == 6); + } + apply(&mut session, json!({"op":"reset","now":7000})); + apply(&mut session, json!({"op":"start","now":7000})); + assert!( + !apply(&mut session, json!({"op":"advance","now":8251})) + .state + .ended + ); +} +#[test] +fn long_and_infinite_modes_keep_catchup_without_web_short_test_failure() { + for config in [ + Config { + time: 130.0, + ..Config::default() + }, + Config { + time: 0.0, + ..Config::default() + }, + Config { + mode: "words".into(), + words: 250, + ..Config::default() + }, + Config { + mode: "words".into(), + words: 0, + ..Config::default() + }, + ] { + let mut session = monitored(config); + let update = apply(&mut session, json!({"op":"advance","now":6000})); + assert!(!update.state.ended); + assert!(!update.slow_timer); + } +} +#[test] +fn engine_slow_render_state_skips_overflow_measurement_until_reset() { + let mut session = monitored(Config { + mode: "words".into(), + words: 50, + ..Config::default() + }); + apply(&mut session, json!({"op":"insert","data":[120],"now":0})); + let layout = typing_engine::session::InsertionLayout { + wrapped: true, + ..Default::default() + }; + assert!(session.preflight(Text::from("x"), &layout).blocked); + apply(&mut session, json!({"op":"advance","now":1126})); + let check = session.preflight(Text::from("x"), &layout); + assert!(!check.blocked); + assert!(!check.measure_overflow); +} diff --git a/docs/rust-engine/progress.md b/docs/rust-engine/progress.md index 551ed546c..8daabf75d 100644 --- a/docs/rust-engine/progress.md +++ b/docs/rust-engine/progress.md @@ -128,3 +128,14 @@ do not rerun completed checks without a new reason. - Dedicated Wasm `z` profile passes the actual Cloudflare browser proof: all 61 original + 5 section references, historical derivation, lifecycle/content requests, and both web behavior contracts (6 tests, 19.8 seconds). Boundary sample: JSON median 0.0065 ms / p95 0.0075 ms per UTF-16 unit; object p95 0.0105 ms; initialization 30 ms (single sample); 7 keyboard input-to-paint maximum 36 ms; Wasm memory 1,376,256 bytes. Raw evidence: profile-z-proof-performance.json. Full initialization distribution/soak/performance acceptance remains pending. - Added opt-in host-observed finish acknowledgement: Rust requests completion/failure and blocks further input/ticks; the host supplies actual completion time after its navigation/hooks. Duplicate/stale/earlier acknowledgements are rejected. Native tests cover score-timing origin, blocked input, timer/failure reasons and reset cancellation. Default immediate sessions retain all command-oracle behavior. Browser proof for acknowledgement still pending. + +## Additional cutover dependencies identified during integration review + +- Preserve app-lifetime weak-spot learning and remembered pace across session replacement through an explicit owned engine context; current newly created generators start fresh. +- Complete Wasm pace operations and a persistent replay handle. +- Replace min-burst whole-log cloning and provide bounded live-stat queries before long-session/per-key performance acceptance. +- Initialization must be safe for concurrent owners without module-level app state; consider async module compilation followed by synchronous guarded wasm-bindgen initialization, plus app-owned load promises. +- Expose structured recovery actions for missing quote/favorite/polyglot content and active content failures; avoid string-matching rules in hosts. +- Port restart/no-quit/long-test and custom-progress calculations; preserve browser input normalization boundaries in helper extraction. + +- Slow-render policy now belongs to Rust: exact short-test thresholds/counters, failure before ticks, long/infinite catchup and overflow suppression. Native full suite/Clippy pass. Built Cloudflare browser proof passes all 5 Wasm tests, including observed completion time and slow-render failure; the actual website's slow-render contract passes separately. Initial web test hit an offscreen words-container click; corrected to the visible first word and reran (1 pass, 7.5 s). Existing master/min-burst tests also pass. Loader concurrency and persistent replay changes are subsequent work, not included in this evidence. diff --git a/frontend/e2e/engine-input-contract.migration.ts b/frontend/e2e/engine-input-contract.migration.ts index 0fbf794e1..dfffb8409 100644 --- a/frontend/e2e/engine-input-contract.migration.ts +++ b/frontend/e2e/engine-input-contract.migration.ts @@ -71,3 +71,39 @@ test.describe("minimum burst progression", () => { expect(state.wordIndex).toBe(1); }); }); + +test.describe("web slow render policy", () => { + test.use({ + config: { + mode: "time", + time: 30, + difficulty: "normal", + minBurst: "off", + smoothCaret: "off", + }, + }); + test("stops a short test before ticking after a long render stall", async ({ + page, + }) => { + await openTest(page); + await page.locator("#words .word").first().click(); + await expect(page.locator("#wordsInput")).toBeFocused(); + await page.keyboard.insertText("a"); + await page.evaluate(() => { + const until = performance.now() + 1600; + while (performance.now() < until) { + /* Simulate a main-thread render stall. */ + } + }); + await expect(page.locator("#result")).toBeVisible(); + const reason = await page.evaluate( + () => + ( + window as unknown as { + engineMigrationState: () => { reason?: string }; + } + ).engineMigrationState().reason, + ); + expect(reason).toBe("slow timer"); + }); +}); diff --git a/frontend/src/features/typing/runtime.client.tsx b/frontend/src/features/typing/runtime.client.tsx index 6534df4d8..b8767491d 100644 --- a/frontend/src/features/typing/runtime.client.tsx +++ b/frontend/src/features/typing/runtime.client.tsx @@ -10,7 +10,7 @@ import { useKeymapEvents, } from "../../ts/states/test"; import { addToGlobal } from "../../ts/utils/misc"; -import { buildEventLog } from "./runtime/events/data"; +import { buildEventLog, getTestSession } from "./runtime/events/data"; import { useFunboxEvents } from "./runtime/funbox/funbox"; import { usePaceCaretEvents } from "./runtime/pace-caret"; import { usePractiseEvents } from "./runtime/practise-words"; @@ -41,7 +41,10 @@ export function TypingRuntime(): null { lastEventLog: getLastEventLog, ...(import.meta.env.OXYTYPE_ENGINE_PROOF ? { - engineMigrationState: () => ({ wordIndex: getActiveWordIndex() }), + engineMigrationState: () => ({ + wordIndex: getActiveWordIndex(), + reason: getTestSession().getFinishReason(), + }), } : {}), currentEventLog: buildEventLog, From fb739e226b64fe007077d66a49c2e19ea7b3eb57 Mon Sep 17 00:00:00 2001 From: Lakshmi Tanmay Date: Fri, 23 Oct 2026 05:35:06 +0530 Subject: [PATCH 53/93] Own incremental replay state and seeking in Rust with disposable Wasm handles --- crates/typing-engine-wasm/src/lib.rs | 43 +++++++++ .../typing-engine/src/bin/export-bindings.rs | 2 + crates/typing-engine/src/replay.rs | 93 +++++++++++++++++++ crates/typing-engine/tests/analysis.rs | 24 +++++ docs/rust-engine/progress.md | 2 + 5 files changed, 164 insertions(+) diff --git a/crates/typing-engine-wasm/src/lib.rs b/crates/typing-engine-wasm/src/lib.rs index 040e84733..6889b2f66 100644 --- a/crates/typing-engine-wasm/src/lib.rs +++ b/crates/typing-engine-wasm/src/lib.rs @@ -187,3 +187,46 @@ pub fn analyze(log: &str, query: &str) -> Result { &log, query, ))?) } + +#[wasm_bindgen] +pub struct WasmReplay { + playback: Option, +} +#[wasm_bindgen] +impl WasmReplay { + #[wasm_bindgen(constructor)] + pub fn new(log: &str, zen: bool, version: u32) -> Result { + if version != BINDING_VERSION { + return Err(JsError::new("Binding version mismatch")); + } + let log = serde_json::from_str(log)?; + Ok(Self { + playback: Some(typing_engine::replay::Playback::new(&log, zen)), + }) + } + pub fn snapshot(&self) -> Result { + let playback = self + .playback + .as_ref() + .ok_or_else(|| JsError::new("Replay disposed"))?; + Ok(serde_json::to_string(&playback.snapshot())?) + } + pub fn step(&mut self) -> Result { + let playback = self + .playback + .as_mut() + .ok_or_else(|| JsError::new("Replay disposed"))?; + Ok(serde_json::to_string(&playback.step())?) + } + pub fn seek(&mut self, cursor: &str) -> Result { + let cursor = serde_json::from_str(cursor)?; + let playback = self + .playback + .as_mut() + .ok_or_else(|| JsError::new("Replay disposed"))?; + Ok(serde_json::to_string(&playback.seek(cursor))?) + } + pub fn dispose(&mut self) { + self.playback = None; + } +} diff --git a/crates/typing-engine/src/bin/export-bindings.rs b/crates/typing-engine/src/bin/export-bindings.rs index dbc561241..c80c8bb6b 100644 --- a/crates/typing-engine/src/bin/export-bindings.rs +++ b/crates/typing-engine/src/bin/export-bindings.rs @@ -30,6 +30,8 @@ fn main() -> Result<(), Box> { typing_engine::analysis::Response::export_all(&config)?; typing_engine::replay::Word::export_all(&config)?; typing_engine::replay::Seek::export_all(&config)?; + typing_engine::replay::PlaybackSnapshot::export_all(&config)?; + typing_engine::replay::PlaybackUpdate::export_all(&config)?; typing_engine::generation::Prepare::export_all(&config)?; typing_engine::generation::Generated::export_all(&config)?; typing_engine::generation::NextWord::export_all(&config)?; diff --git a/crates/typing-engine/src/replay.rs b/crates/typing-engine/src/replay.rs index 3cdb4248a..083c2fe02 100644 --- a/crates/typing-engine/src/replay.rs +++ b/crates/typing-engine/src/replay.rs @@ -305,3 +305,96 @@ pub fn seek(words: &mut [Word], steps: &[Step], target: Cursor) -> Seek { } Seek { cursor, next_index } } + +#[derive(Serialize, TS)] +#[serde(rename_all = "camelCase")] +pub struct PlaybackSnapshot { + pub words: Vec, + pub cursor: Cursor, + pub next_index: usize, + pub next_time: Option, + pub duration: f64, +} +#[derive(Serialize, TS)] +pub struct ReplayWordChange { + pub index: usize, + pub word: Word, +} +#[derive(Serialize, TS)] +#[serde(rename_all = "camelCase")] +pub struct PlaybackUpdate { + pub words: Vec, + pub cursor: Cursor, + pub next_index: usize, + pub next_time: Option, + pub sound: Option, +} +/// Owns playback state. Hosts schedule steps and render returned word deltas. +pub struct Playback { + initial: Vec, + words: Vec, + steps: Vec, + cursor: Cursor, + next_index: usize, + duration: f64, +} +impl Playback { + pub fn new(log: &Log, zen: bool) -> Self { + let steps = steps(log, zen); + let words = prompt(&words(log, zen), &steps); + Self { + initial: words.clone(), + words, + steps, + cursor: Cursor::default(), + next_index: 0, + duration: duration(log), + } + } + pub fn snapshot(&self) -> PlaybackSnapshot { + PlaybackSnapshot { + words: self.words.clone(), + cursor: self.cursor, + next_index: self.next_index, + next_time: self.steps.get(self.next_index).map(|step| step.time), + duration: self.duration, + } + } + pub fn step(&mut self) -> PlaybackUpdate { + let previous = self.cursor.word_index; + let sound = self.steps.get(self.next_index).and_then(|step| { + self.next_index += 1; + apply(&mut self.words, &mut self.cursor, step) + }); + let mut changed = vec![]; + if sound.is_some() { + for index in [previous, self.cursor.word_index] { + if let Ok(index) = usize::try_from(index) + && !changed + .iter() + .any(|change: &ReplayWordChange| change.index == index) + && let Some(word) = self.words.get(index) + { + changed.push(ReplayWordChange { + index, + word: word.clone(), + }); + } + } + } + PlaybackUpdate { + words: changed, + cursor: self.cursor, + next_index: self.next_index, + next_time: self.steps.get(self.next_index).map(|step| step.time), + sound: sound.map(str::to_owned), + } + } + pub fn seek(&mut self, target: Cursor) -> PlaybackSnapshot { + self.words.clone_from(&self.initial); + let result = seek(&mut self.words, &self.steps, target); + self.cursor = result.cursor; + self.next_index = result.next_index; + self.snapshot() + } +} diff --git a/crates/typing-engine/tests/analysis.rs b/crates/typing-engine/tests/analysis.rs index c4e4d70d9..e5f532ffb 100644 --- a/crates/typing-engine/tests/analysis.rs +++ b/crates/typing-engine/tests/analysis.rs @@ -59,6 +59,25 @@ fn replay_seeking_practice_and_validity_match_frozen_typescript_oracle() { equal(&json!(sounds), &expected["sounds"], name); equal(&json!(prompt), &expected["final"], name); equal(&json!(cursor), &expected["cursor"], name); + let mut playback = replay::Playback::new(&log, expected["zen"].as_bool().unwrap()); + let mut rendered = playback.snapshot().words; + let mut sounds = vec![]; + while playback.snapshot().next_time.is_some() { + let update = playback.step(); + sounds.push(update.sound); + assert!(update.words.len() <= 2); + for change in update.words { + rendered[change.index] = change.word; + } + } + equal(&json!(rendered), &expected["final"], name); + equal(&json!(sounds), &expected["sounds"], name); + equal( + &json!(playback.snapshot().cursor), + &expected["cursor"], + name, + ); + assert!(playback.step().words.is_empty()); for expected in expected["seeks"].as_array().unwrap() { let mut prompt = replay::prompt(&words, &steps); let target = serde_json::from_value(expected["target"].clone()).unwrap(); @@ -66,6 +85,11 @@ fn replay_seeking_practice_and_validity_match_frozen_typescript_oracle() { equal(&json!(actual.cursor), &expected["cursor"], name); equal(&json!(actual.next_index), &expected["nextIndex"], name); equal(&json!(prompt), &expected["words"], name); + let actual = + playback.seek(serde_json::from_value(expected["target"].clone()).unwrap()); + equal(&json!(actual.cursor), &expected["cursor"], name); + equal(&json!(actual.next_index), &expected["nextIndex"], name); + equal(&json!(actual.words), &expected["words"], name); } } let words: Vec<_> = log diff --git a/docs/rust-engine/progress.md b/docs/rust-engine/progress.md index 8daabf75d..b811c9a8b 100644 --- a/docs/rust-engine/progress.md +++ b/docs/rust-engine/progress.md @@ -139,3 +139,5 @@ do not rerun completed checks without a new reason. - Port restart/no-quit/long-test and custom-progress calculations; preserve browser input normalization boundaries in helper extraction. - Slow-render policy now belongs to Rust: exact short-test thresholds/counters, failure before ticks, long/infinite catchup and overflow suppression. Native full suite/Clippy pass. Built Cloudflare browser proof passes all 5 Wasm tests, including observed completion time and slow-render failure; the actual website's slow-render contract passes separately. Initial web test hit an offscreen words-container click; corrected to the visible first word and reran (1 pass, 7.5 s). Existing master/min-burst tests also pass. Loader concurrency and persistent replay changes are subsequent work, not included in this evidence. + +- Persistent replay now owns cursor, prompt and step position in Rust; ordinary steps return at most two changed words. Native replay regression reconstructs final prompts from deltas and checks sounds plus repeated seeks against all eight immutable recordings in normal/zen interpretations. Native regression and Clippy pass. Wasm facade added; built-browser verification pending. From 55efcd94ac11ccec9d2bfc7d48e651a2a74709c7 Mon Sep 17 00:00:00 2001 From: Lakshmi Tanmay Date: Fri, 23 Oct 2026 05:35:54 +0530 Subject: [PATCH 54/93] Make Wasm loading owned, retryable and safe across concurrent session owners --- docs/rust-engine/progress.md | 2 + frontend/e2e/engine-proof.migration.ts | 137 ++++++++++++++++++- frontend/src/routes/engine-proof.tsx | 130 +++++++++++++++++- packages/typing-engine/src/browser.client.ts | 91 +++++++++++- 4 files changed, 349 insertions(+), 11 deletions(-) diff --git a/docs/rust-engine/progress.md b/docs/rust-engine/progress.md index b811c9a8b..817748f15 100644 --- a/docs/rust-engine/progress.md +++ b/docs/rust-engine/progress.md @@ -141,3 +141,5 @@ do not rerun completed checks without a new reason. - Slow-render policy now belongs to Rust: exact short-test thresholds/counters, failure before ticks, long/infinite catchup and overflow suppression. Native full suite/Clippy pass. Built Cloudflare browser proof passes all 5 Wasm tests, including observed completion time and slow-render failure; the actual website's slow-render contract passes separately. Initial web test hit an offscreen words-container click; corrected to the visible first word and reran (1 pass, 7.5 s). Existing master/min-burst tests also pass. Loader concurrency and persistent replay changes are subsequent work, not included in this evidence. - Persistent replay now owns cursor, prompt and step position in Rust; ordinary steps return at most two changed words. Native replay regression reconstructs final prompts from deltas and checks sounds plus repeated seeks against all eight immutable recordings in normal/zen interpretations. Native regression and Clippy pass. Wasm facade added; built-browser verification pending. + +- Cloudflare production build and combined browser migration run pass: 9 tests, 25.5 seconds. Includes all 19 traces, 66 generators, eight historical result/replay references, persistent playback deltas/sounds/seeks/disposal, completion timing, active content and actual web failure ordering. Concurrent cold initialization preserves five live handles; one failed Wasm download retries, one owner's load promise is shared, disposal rejects later loads. Type-aware lint passes. Asset: 766.89 kB raw / 297.25 kB gzip (Vite); both frozen payload limits pass. App-owned loader resource added; production website cutover remains pending. diff --git a/frontend/e2e/engine-proof.migration.ts b/frontend/e2e/engine-proof.migration.ts index 02064bb6c..abb17e676 100644 --- a/frontend/e2e/engine-proof.migration.ts +++ b/frontend/e2e/engine-proof.migration.ts @@ -287,7 +287,20 @@ test("historical results, replay and practice agree with frozen references in Wa ) as { name: string; frames: Record[]; - replay: { zen: boolean; words: unknown; steps: unknown }[]; + replay: { + zen: boolean; + words: unknown; + steps: unknown; + final: unknown; + cursor: unknown; + sounds: unknown; + seeks: { + target: unknown; + words: unknown; + cursor: unknown; + nextIndex: number; + }[]; + }[]; practice: { missed: string; slow: boolean; @@ -374,8 +387,28 @@ test("historical results, replay and practice agree with frozen references in Wa }, }); } - inputs.push({ log, queries }); - expected.push(outputs); + inputs.push({ + log, + queries, + playback: reference.replay.map((replay) => ({ + zen: replay.zen, + targets: replay.seeks.map((seek) => seek.target), + })), + }); + expected.push({ + analysis: outputs, + playback: reference.replay.map((replay) => ({ + words: replay.final, + sounds: replay.sounds, + cursor: replay.cursor, + stale: true, + seeks: replay.seeks.map(({ words, cursor, nextIndex }) => ({ + words, + cursor, + nextIndex, + })), + })), + }); } await page.goto("/engine-proof"); await page @@ -477,3 +510,101 @@ test("typed section requests preserve prepared and active input through browser expect(events).toHaveLength(404); expect(events.at(-1)?.testMs).toBe(100); }); + +test("Wasm owns slow-render failure and accepts observed completion time", async ({ + page, +}) => { + await page.goto("/engine-proof"); + const run = async ( + fixture: unknown, + ): Promise<{ + frames: { + state: { active: boolean; ended: boolean; reason?: string }; + log: { + events: { type: string; testMs: number; data: { event?: string } }[]; + }; + }[]; + }> => { + await page + .getByRole("textbox", { name: "Command fixture" }) + .fill(JSON.stringify(fixture)); + await page.getByRole("button", { name: "Run fixture" }).click(); + await expect(page.getByTestId("engine-proof-result")).not.toBeEmpty(); + await expect(page.getByTestId("engine-proof-error")).toBeEmpty(); + return JSON.parse( + await page.getByTestId("engine-proof-result").innerText(), + ) as { + frames: { + state: { active: boolean; ended: boolean; reason?: string }; + log: { + events: { type: string; testMs: number; data: { event?: string } }[]; + }; + }[]; + }; + }; + const completed = await run({ + name: "observed completion", + config: { mode: "words", words: 1 }, + words: [[97]], + commands: [ + { op: "view", now: 0, deferFinish: true }, + { op: "insert", data: [97], now: 1000, date: 1000000 }, + { op: "advance", now: 5000, date: 1004000 }, + { op: "acknowledgeFinish", now: 5050, date: 1004050 }, + ], + }); + expect(completed.frames[1]?.state).toMatchObject({ + active: true, + ended: false, + }); + expect(completed.frames.at(-1)?.state).toMatchObject({ + active: false, + ended: true, + }); + expect(completed.frames.at(-1)?.log.events.at(-1)?.testMs).toBe(4050); + const failed = await run({ + name: "web slow timer", + config: { mode: "time", time: 30 }, + words: [[97, 32]], + commands: [ + { op: "view", now: 0, monitorRenderDrift: true }, + { op: "start", now: 0, date: 0 }, + { op: "advance", now: 1501, date: 1501 }, + ], + }); + expect(failed.frames.at(-1)?.state).toMatchObject({ + ended: true, + reason: "slow timer", + }); + expect( + failed.frames + .at(-1) + ?.log.events.filter( + (event) => event.type === "timer" && event.data.event === "step", + ), + ).toEqual([]); +}); + +test("owned Wasm loading retries failed downloads and preserves concurrent handles", async ({ + page, +}) => { + let downloads = 0; + await page.route("**/*.wasm", async (route) => { + downloads++; + if (downloads === 1) await route.abort("failed"); + else await route.continue(); + }); + await page.goto("/engine-proof"); + await page.getByRole("button", { name: "Run loading", exact: true }).click(); + await expect(page.getByTestId("engine-proof-result")).not.toBeEmpty(); + await expect(page.getByTestId("engine-proof-error")).toBeEmpty(); + expect( + JSON.parse(await page.getByTestId("engine-proof-result").innerText()), + ).toEqual({ + recovered: true, + shared: true, + intact: true, + rejected: true, + }); + expect(downloads).toBe(6); +}); diff --git a/frontend/src/routes/engine-proof.tsx b/frontend/src/routes/engine-proof.tsx index 6aec7bc23..e3a3c436f 100644 --- a/frontend/src/routes/engine-proof.tsx +++ b/frontend/src/routes/engine-proof.tsx @@ -9,6 +9,7 @@ import type { Source, ContentRequest, Update, + Cursor, } from "@oxytype/typing-engine/browser.client"; import { createFileRoute } from "@tanstack/solid-router"; @@ -29,10 +30,13 @@ function EngineProof() { let typingSession: BrowserSession | undefined; let disposed = false; const handles = new Set(); + const loaders = new Set<{ dispose: () => void }>(); onCleanup(() => { disposed = true; for (const handle of handles) handle.dispose(); handles.clear(); + for (const loader of loaders) loader.dispose(); + loaders.clear(); }); const run = async (): Promise => { setResult(""); @@ -98,6 +102,68 @@ function EngineProof() { setFailure(error instanceof Error ? error.message : String(error)); } }; + const runLoading = async (): Promise => { + setResult(""); + setFailure(""); + const { createEngineLoader, loadEngine, encodeText } = + await import("@oxytype/typing-engine/browser.client"); + const loader = createEngineLoader(); + const sessions: BrowserSession[] = []; + loaders.add(loader); + try { + let recovered = false; + try { + await loader.load(); + } catch { + recovered = true; + } + const first = loader.load(); + const shared = first === loader.load(); + await Promise.all( + [first, ...Array.from({ length: 4 }, async () => loadEngine())].map( + async (ready) => { + const engine = await ready; + if (disposed) return; + const session = engine.createSession({ mode: "words", words: 0 }, [ + encodeText("one "), + ]); + sessions.push(session); + session.command({ + op: "insert", + data: encodeText("o"), + now: 0, + date: 0, + automatic: null, + isCompositionEnding: null, + correctShiftUsed: null, + }); + }, + ), + ); + const intact = + sessions.length === 5 && + sessions.every( + (session) => + JSON.stringify(session.snapshot().state.inputValue) === "[111]", + ); + loader.dispose(); + let rejected = false; + try { + await loader.load(); + } catch { + rejected = true; + } + if (!disposed) { + setResult(JSON.stringify({ recovered, shared, intact, rejected })); + } + } catch (error) { + setFailure(error instanceof Error ? error.message : String(error)); + } finally { + for (const session of sessions) session.dispose(); + loader.dispose(); + loaders.delete(loader); + } + }; const runGeneration = async (): Promise => { setResult(""); setFailure(""); @@ -161,16 +227,62 @@ function EngineProof() { setResult(""); setFailure(""); try { - const inputs = JSON.parse(fixture()) as { log: Log; queries: Query[] }[]; + const inputs = JSON.parse(fixture()) as { + log: Log; + queries: Query[]; + playback?: { zen: boolean; targets: Cursor[] }[]; + }[]; const { loadEngine, analyzeLog } = await import("@oxytype/typing-engine/browser.client"); - await loadEngine(); + const engine = await loadEngine(); if (disposed) return; setResult( JSON.stringify( - inputs.map(({ log, queries }) => - queries.map((query) => analyzeLog(log, query)), - ), + inputs.map(({ log, queries, playback }) => { + const analysis = queries.map((query) => analyzeLog(log, query)); + if (playback === undefined) return analysis; + return { + analysis, + playback: playback.map(({ zen, targets }) => { + const replay = engine.createReplay(log, zen); + try { + const initial = replay.snapshot(); + const words = initial.words; + const sounds = []; + let cursor = initial.cursor; + let next = initial.nextTime; + while (next !== null) { + const update = replay.step(); + for (const change of update.words) { + words[change.index] = change.word; + } + sounds.push(update.sound); + cursor = update.cursor; + next = update.nextTime; + } + const seeks = targets.map((target) => { + const snapshot = replay.seek(target); + return { + words: snapshot.words, + cursor: snapshot.cursor, + nextIndex: snapshot.nextIndex, + }; + }); + replay.dispose(); + replay.dispose(); + let stale = false; + try { + replay.step(); + } catch { + stale = true; + } + return { words, sounds, cursor, seeks, stale }; + } finally { + replay.dispose(); + } + }), + }; + }), ), ); } catch (error) { @@ -296,6 +408,14 @@ function EngineProof() { > Run prepared session +
{result()}
{failure()}
{benchmark()}
diff --git a/packages/typing-engine/src/browser.client.ts b/packages/typing-engine/src/browser.client.ts index 570875f9f..546c08318 100644 --- a/packages/typing-engine/src/browser.client.ts +++ b/packages/typing-engine/src/browser.client.ts @@ -1,8 +1,10 @@ -import initialize, { +import { + initSync, binding_version, analyze, WasmSession, WasmContent, + WasmReplay, } from "../generated/engine.js"; import wasmUrl from "../generated/engine_bg.wasm?url"; import type { Command } from "../generated/types/Command"; @@ -21,10 +23,16 @@ import type { Source } from "../generated/types/Source"; import type { Preflight } from "../generated/types/Preflight"; import type { InsertionLayout } from "../generated/types/InsertionLayout"; import type { Snapshot } from "../generated/types/Snapshot"; +import type { PlaybackSnapshot } from "../generated/types/PlaybackSnapshot"; +import type { PlaybackUpdate } from "../generated/types/PlaybackUpdate"; +import type { Cursor } from "../generated/types/Cursor"; import type { Log } from "../generated/types/Log"; export type { Query, Response, + PlaybackSnapshot, + PlaybackUpdate, + Cursor, Log, Snapshot, Preflight, @@ -59,12 +67,25 @@ export function decodeText(units: Text): string { } /** Module preparation is async; all prepared session commands are synchronous. */ -export async function loadEngine(): Promise<{ +export async function loadEngine(signal?: AbortSignal): Promise<{ createSession: (config: Partial, words: Text[]) => BrowserSession; memoryBytes: () => number; createContent: () => BrowserContent; + createReplay: (log: Log, zen: boolean) => BrowserReplay; }> { - const module = await initialize({ module_or_path: wasmUrl }); + // Compile asynchronously, then use wasm-bindgen's synchronous guard. Two + // concurrent async initializers could otherwise replace memory under handles. + signal?.throwIfAborted(); + const response = await fetch(wasmUrl, { signal }); + if (!response.ok) { + throw new Error(`Typing engine download failed: ${response.status}`); + } + const compiled = + typeof WebAssembly.compileStreaming === "function" + ? await WebAssembly.compileStreaming(response) + : await WebAssembly.compile(await response.arrayBuffer()); + signal?.throwIfAborted(); + const module = initSync({ module: compiled }); if (binding_version() !== BINDING_VERSION) { throw new Error("Typing engine binding version mismatch"); } @@ -72,6 +93,38 @@ export async function loadEngine(): Promise<{ createSession: (config, words) => new BrowserSession(config, words), memoryBytes: () => module.memory.buffer.byteLength, createContent: () => new BrowserContent(), + createReplay: (log, zen) => new BrowserReplay(log, zen), + }; +} + +export type Engine = Awaited>; + +/** The caller owns this resource and must dispose it with its app/route owner. */ +export function createEngineLoader(): { + load: () => Promise; + dispose: () => void; +} { + const controller = new AbortController(); + let pending: Promise | undefined; + let disposed = false; + return { + // oxlint-disable-next-line typescript/promise-function-async -- Owners share the exact pending promise. + load: () => { + if (disposed) { + return Promise.reject(new Error("Typing engine loader disposed")); + } + pending ??= loadEngine(controller.signal).catch((error: unknown) => { + pending = undefined; + throw error; + }); + return pending; + }, + dispose: () => { + if (disposed) return; + disposed = true; + controller.abort(); + pending = undefined; + }, }; } @@ -230,3 +283,35 @@ export function analyzeLog( analyze(JSON.stringify(log), JSON.stringify(query)), ) as Extract; } + +/** Persistent playback: scheduling/audio/DOM belong to the host. */ +export class BrowserReplay { + private handle: WasmReplay | undefined; + constructor(log: Log, zen: boolean) { + this.handle = new WasmReplay(JSON.stringify(log), zen, BINDING_VERSION); + } + private live(): WasmReplay { + if (this.handle === undefined) { + throw new Error("Typing engine replay disposed"); + } + return this.handle; + } + snapshot(): PlaybackSnapshot { + return JSON.parse(this.live().snapshot()) as PlaybackSnapshot; + } + step(): PlaybackUpdate { + return JSON.parse(this.live().step()) as PlaybackUpdate; + } + seek(cursor: Cursor): PlaybackSnapshot { + return JSON.parse( + this.live().seek(JSON.stringify(cursor)), + ) as PlaybackSnapshot; + } + dispose(): void { + const handle = this.handle; + if (handle === undefined) return; + this.handle = undefined; + handle.dispose(); + handle.free(); + } +} From 3862526a07fc984175e85bf5709878ac7639d120 Mon Sep 17 00:00:00 2001 From: Lakshmi Tanmay Date: Fri, 23 Oct 2026 05:40:58 +0530 Subject: [PATCH 55/93] Preserve exact language size suffixes at input normalization boundaries --- crates/typing-engine/src/session.rs | 2 +- crates/typing-engine/src/text.rs | 14 +- crates/typing-engine/tests/input.rs | 19 + docs/rust-engine/progress.md | 2 + .../scripts/capture-normalization-oracle.ts | 38 + .../typing-fixtures/oracle/normalization.json | 3404 +++++++++++++++++ 6 files changed, 3477 insertions(+), 2 deletions(-) create mode 100644 packages/typing-core/scripts/capture-normalization-oracle.ts create mode 100644 packages/typing-fixtures/oracle/normalization.json diff --git a/crates/typing-engine/src/session.rs b/crates/typing-engine/src/session.rs index abe892d37..efc5f9e54 100644 --- a/crates/typing-engine/src/session.rs +++ b/crates/typing-engine/src/session.rs @@ -1059,7 +1059,7 @@ impl Session { let replacement = if unit == 0x2026 && target.0.get(input.len()) != Some(&unit) { Some("...") } else if unit == 0x0133 - && self.config.language.starts_with("dutch") + && crate::text::base_language(&self.config.language) == "dutch" && target.0.get(input.len()) != Some(&unit) { Some("ij") diff --git a/crates/typing-engine/src/text.rs b/crates/typing-engine/src/text.rs index 3f4c2ccd5..dd3365804 100644 --- a/crates/typing-engine/src/text.rs +++ b/crates/typing-engine/src/text.rs @@ -51,7 +51,7 @@ pub fn normalize(unit: u16, target: Option, language: &str) -> u16 { || ["’‘'ʼ׳ʻ᾽", "\"”“„", "–—-‐‑", ",‚"].iter().any(|set| { set.encode_utf16().any(|c| c == unit) && set.encode_utf16().any(|c| c == target) }) - || (language.split('_').next() == Some("russian") + || (base_language(language) == "russian" && "ёеe".encode_utf16().any(|c| c == unit) && "ёеe".encode_utf16().any(|c| c == target)); if equivalent { @@ -61,6 +61,18 @@ pub fn normalize(unit: u16, target: Option, language: &str) -> u16 { if is_space(unit) { 32 } else { unit } } +/// Match JavaScript /_\d*k$/ exactly; named variants are not size suffixes. +pub fn base_language(language: &str) -> &str { + if let Some((base, suffix)) = language.rsplit_once('_') + && let Some(digits) = suffix.strip_suffix('k') + && digits.bytes().all(|unit| unit.is_ascii_digit()) + { + base + } else { + language + } +} + /// JavaScript \s, distinct from the app's directly typable space set. pub fn js_whitespace(unit: u16) -> bool { matches!(unit, 9..=13 | 32 | 160 | 0x1680 | 0x2000..=0x200a | 0x2028 | diff --git a/crates/typing-engine/tests/input.rs b/crates/typing-engine/tests/input.rs index f713293d6..25191a60f 100644 --- a/crates/typing-engine/tests/input.rs +++ b/crates/typing-engine/tests/input.rs @@ -1,5 +1,24 @@ use serde_json::Value; #[test] +fn language_equivalence_strips_only_the_legacy_size_suffix() { + let path = std::path::Path::new(env!("CARGO_MANIFEST_DIR")) + .join("../../packages/typing-fixtures/oracle/normalization.json"); + let cases: Vec = serde_json::from_slice(&std::fs::read(path).unwrap()).unwrap(); + for case in cases { + let data: typing_engine::Text = serde_json::from_value(case["data"].clone()).unwrap(); + let target: typing_engine::Text = serde_json::from_value(case["target"].clone()).unwrap(); + assert_eq!( + serde_json::json!([typing_engine::text::normalize( + data.0[0], + target.0.first().copied(), + case["language"].as_str().unwrap() + )]), + case["expected"], + "{case}" + ); + } +} +#[test] fn input_decisions_match_frozen_typescript_matrix() { let path = std::path::Path::new(env!("CARGO_MANIFEST_DIR")) .join("../../packages/typing-fixtures/oracle/input-rules.json"); diff --git a/docs/rust-engine/progress.md b/docs/rust-engine/progress.md index 817748f15..88a6e90e2 100644 --- a/docs/rust-engine/progress.md +++ b/docs/rust-engine/progress.md @@ -143,3 +143,5 @@ do not rerun completed checks without a new reason. - Persistent replay now owns cursor, prompt and step position in Rust; ordinary steps return at most two changed words. Native replay regression reconstructs final prompts from deltas and checks sounds plus repeated seeks against all eight immutable recordings in normal/zen interpretations. Native regression and Clippy pass. Wasm facade added; built-browser verification pending. - Cloudflare production build and combined browser migration run pass: 9 tests, 25.5 seconds. Includes all 19 traces, 66 generators, eight historical result/replay references, persistent playback deltas/sounds/seeks/disposal, completion timing, active content and actual web failure ordering. Concurrent cold initialization preserves five live handles; one failed Wasm download retries, one owner's load promise is shared, disposal rejects later loads. Type-aware lint passes. Asset: 766.89 kB raw / 297.25 kB gzip (Vite); both frozen payload limits pass. App-owned loader resource added; production website cutover remains pending. + +- Captured 567 additional normalization boundaries from unchanged TypeScript. They exposed and now guard Russian named-variant equivalence: strip only legacy /_\d*k$/ suffixes; preserve named variants. Dutch expansion uses the same exact language boundary. Existing 2,048 input decisions and new normalization matrix pass natively. New expectations captured once with exclusive file creation; no previous fixture changed. Browser rerun pending. diff --git a/packages/typing-core/scripts/capture-normalization-oracle.ts b/packages/typing-core/scripts/capture-normalization-oracle.ts new file mode 100644 index 000000000..5a3f2a457 --- /dev/null +++ b/packages/typing-core/scripts/capture-normalization-oracle.ts @@ -0,0 +1,38 @@ +import { writeFileSync } from "node:fs"; +import { resolve } from "node:path"; +import type { Language } from "@oxytype/schemas/languages"; +import { normalizeData } from "../src/input/util"; + +const units = (text: string): number[] => + Array.from({ length: text.length }, (_, i) => text.charCodeAt(i)); +const cases = []; +for (const language of [ + "russian", + "russian_1k", + "russian_25k", + "russian_phonetic", + "russian_phonetic_1k", + "russian_k", + "russian_1kk", + "dutch", + "dutch_1k", +]) { + for (const data of ["e", "е", "ё", "‘", "'", " ", "\u2002", "…", "\ud800"]) { + for (const target of ["e", "е", "ё", "'", " ", "\u2002", "\ud800"]) { + cases.push({ + language, + data: units(data), + target: units(target), + expected: units(normalizeData(data, "", target, language as Language)), + }); + } + } +} +writeFileSync( + resolve( + import.meta.dirname, + "../../typing-fixtures/oracle/normalization.json", + ), + `${JSON.stringify(cases, null, 2)}\n`, + { flag: "wx" }, +); diff --git a/packages/typing-fixtures/oracle/normalization.json b/packages/typing-fixtures/oracle/normalization.json new file mode 100644 index 000000000..cead2ebed --- /dev/null +++ b/packages/typing-fixtures/oracle/normalization.json @@ -0,0 +1,3404 @@ +[ + { + "language": "russian", + "data": [101], + "target": [101], + "expected": [101] + }, + { + "language": "russian", + "data": [101], + "target": [1077], + "expected": [1077] + }, + { + "language": "russian", + "data": [101], + "target": [1105], + "expected": [1105] + }, + { + "language": "russian", + "data": [101], + "target": [39], + "expected": [101] + }, + { + "language": "russian", + "data": [101], + "target": [32], + "expected": [101] + }, + { + "language": "russian", + "data": [101], + "target": [8194], + "expected": [101] + }, + { + "language": "russian", + "data": [101], + "target": [55296], + "expected": [101] + }, + { + "language": "russian", + "data": [1077], + "target": [101], + "expected": [101] + }, + { + "language": "russian", + "data": [1077], + "target": [1077], + "expected": [1077] + }, + { + "language": "russian", + "data": [1077], + "target": [1105], + "expected": [1105] + }, + { + "language": "russian", + "data": [1077], + "target": [39], + "expected": [1077] + }, + { + "language": "russian", + "data": [1077], + "target": [32], + "expected": [1077] + }, + { + "language": "russian", + "data": [1077], + "target": [8194], + "expected": [1077] + }, + { + "language": "russian", + "data": [1077], + "target": [55296], + "expected": [1077] + }, + { + "language": "russian", + "data": [1105], + "target": [101], + "expected": [101] + }, + { + "language": "russian", + "data": [1105], + "target": [1077], + "expected": [1077] + }, + { + "language": "russian", + "data": [1105], + "target": [1105], + "expected": [1105] + }, + { + "language": "russian", + "data": [1105], + "target": [39], + "expected": [1105] + }, + { + "language": "russian", + "data": [1105], + "target": [32], + "expected": [1105] + }, + { + "language": "russian", + "data": [1105], + "target": [8194], + "expected": [1105] + }, + { + "language": "russian", + "data": [1105], + "target": [55296], + "expected": [1105] + }, + { + "language": "russian", + "data": [8216], + "target": [101], + "expected": [8216] + }, + { + "language": "russian", + "data": [8216], + "target": [1077], + "expected": [8216] + }, + { + "language": "russian", + "data": [8216], + "target": [1105], + "expected": [8216] + }, + { + "language": "russian", + "data": [8216], + "target": [39], + "expected": [39] + }, + { + "language": "russian", + "data": [8216], + "target": [32], + "expected": [8216] + }, + { + "language": "russian", + "data": [8216], + "target": [8194], + "expected": [8216] + }, + { + "language": "russian", + "data": [8216], + "target": [55296], + "expected": [8216] + }, + { + "language": "russian", + "data": [39], + "target": [101], + "expected": [39] + }, + { + "language": "russian", + "data": [39], + "target": [1077], + "expected": [39] + }, + { + "language": "russian", + "data": [39], + "target": [1105], + "expected": [39] + }, + { + "language": "russian", + "data": [39], + "target": [39], + "expected": [39] + }, + { + "language": "russian", + "data": [39], + "target": [32], + "expected": [39] + }, + { + "language": "russian", + "data": [39], + "target": [8194], + "expected": [39] + }, + { + "language": "russian", + "data": [39], + "target": [55296], + "expected": [39] + }, + { + "language": "russian", + "data": [32], + "target": [101], + "expected": [32] + }, + { + "language": "russian", + "data": [32], + "target": [1077], + "expected": [32] + }, + { + "language": "russian", + "data": [32], + "target": [1105], + "expected": [32] + }, + { + "language": "russian", + "data": [32], + "target": [39], + "expected": [32] + }, + { + "language": "russian", + "data": [32], + "target": [32], + "expected": [32] + }, + { + "language": "russian", + "data": [32], + "target": [8194], + "expected": [8194] + }, + { + "language": "russian", + "data": [32], + "target": [55296], + "expected": [32] + }, + { + "language": "russian", + "data": [8194], + "target": [101], + "expected": [32] + }, + { + "language": "russian", + "data": [8194], + "target": [1077], + "expected": [32] + }, + { + "language": "russian", + "data": [8194], + "target": [1105], + "expected": [32] + }, + { + "language": "russian", + "data": [8194], + "target": [39], + "expected": [32] + }, + { + "language": "russian", + "data": [8194], + "target": [32], + "expected": [32] + }, + { + "language": "russian", + "data": [8194], + "target": [8194], + "expected": [8194] + }, + { + "language": "russian", + "data": [8194], + "target": [55296], + "expected": [32] + }, + { + "language": "russian", + "data": [8230], + "target": [101], + "expected": [8230] + }, + { + "language": "russian", + "data": [8230], + "target": [1077], + "expected": [8230] + }, + { + "language": "russian", + "data": [8230], + "target": [1105], + "expected": [8230] + }, + { + "language": "russian", + "data": [8230], + "target": [39], + "expected": [8230] + }, + { + "language": "russian", + "data": [8230], + "target": [32], + "expected": [8230] + }, + { + "language": "russian", + "data": [8230], + "target": [8194], + "expected": [8230] + }, + { + "language": "russian", + "data": [8230], + "target": [55296], + "expected": [8230] + }, + { + "language": "russian", + "data": [55296], + "target": [101], + "expected": [55296] + }, + { + "language": "russian", + "data": [55296], + "target": [1077], + "expected": [55296] + }, + { + "language": "russian", + "data": [55296], + "target": [1105], + "expected": [55296] + }, + { + "language": "russian", + "data": [55296], + "target": [39], + "expected": [55296] + }, + { + "language": "russian", + "data": [55296], + "target": [32], + "expected": [55296] + }, + { + "language": "russian", + "data": [55296], + "target": [8194], + "expected": [55296] + }, + { + "language": "russian", + "data": [55296], + "target": [55296], + "expected": [55296] + }, + { + "language": "russian_1k", + "data": [101], + "target": [101], + "expected": [101] + }, + { + "language": "russian_1k", + "data": [101], + "target": [1077], + "expected": [1077] + }, + { + "language": "russian_1k", + "data": [101], + "target": [1105], + "expected": [1105] + }, + { + "language": "russian_1k", + "data": [101], + "target": [39], + "expected": [101] + }, + { + "language": "russian_1k", + "data": [101], + "target": [32], + "expected": [101] + }, + { + "language": "russian_1k", + "data": [101], + "target": [8194], + "expected": [101] + }, + { + "language": "russian_1k", + "data": [101], + "target": [55296], + "expected": [101] + }, + { + "language": "russian_1k", + "data": [1077], + "target": [101], + "expected": [101] + }, + { + "language": "russian_1k", + "data": [1077], + "target": [1077], + "expected": [1077] + }, + { + "language": "russian_1k", + "data": [1077], + "target": [1105], + "expected": [1105] + }, + { + "language": "russian_1k", + "data": [1077], + "target": [39], + "expected": [1077] + }, + { + "language": "russian_1k", + "data": [1077], + "target": [32], + "expected": [1077] + }, + { + "language": "russian_1k", + "data": [1077], + "target": [8194], + "expected": [1077] + }, + { + "language": "russian_1k", + "data": [1077], + "target": [55296], + "expected": [1077] + }, + { + "language": "russian_1k", + "data": [1105], + "target": [101], + "expected": [101] + }, + { + "language": "russian_1k", + "data": [1105], + "target": [1077], + "expected": [1077] + }, + { + "language": "russian_1k", + "data": [1105], + "target": [1105], + "expected": [1105] + }, + { + "language": "russian_1k", + "data": [1105], + "target": [39], + "expected": [1105] + }, + { + "language": "russian_1k", + "data": [1105], + "target": [32], + "expected": [1105] + }, + { + "language": "russian_1k", + "data": [1105], + "target": [8194], + "expected": [1105] + }, + { + "language": "russian_1k", + "data": [1105], + "target": [55296], + "expected": [1105] + }, + { + "language": "russian_1k", + "data": [8216], + "target": [101], + "expected": [8216] + }, + { + "language": "russian_1k", + "data": [8216], + "target": [1077], + "expected": [8216] + }, + { + "language": "russian_1k", + "data": [8216], + "target": [1105], + "expected": [8216] + }, + { + "language": "russian_1k", + "data": [8216], + "target": [39], + "expected": [39] + }, + { + "language": "russian_1k", + "data": [8216], + "target": [32], + "expected": [8216] + }, + { + "language": "russian_1k", + "data": [8216], + "target": [8194], + "expected": [8216] + }, + { + "language": "russian_1k", + "data": [8216], + "target": [55296], + "expected": [8216] + }, + { + "language": "russian_1k", + "data": [39], + "target": [101], + "expected": [39] + }, + { + "language": "russian_1k", + "data": [39], + "target": [1077], + "expected": [39] + }, + { + "language": "russian_1k", + "data": [39], + "target": [1105], + "expected": [39] + }, + { + "language": "russian_1k", + "data": [39], + "target": [39], + "expected": [39] + }, + { + "language": "russian_1k", + "data": [39], + "target": [32], + "expected": [39] + }, + { + "language": "russian_1k", + "data": [39], + "target": [8194], + "expected": [39] + }, + { + "language": "russian_1k", + "data": [39], + "target": [55296], + "expected": [39] + }, + { + "language": "russian_1k", + "data": [32], + "target": [101], + "expected": [32] + }, + { + "language": "russian_1k", + "data": [32], + "target": [1077], + "expected": [32] + }, + { + "language": "russian_1k", + "data": [32], + "target": [1105], + "expected": [32] + }, + { + "language": "russian_1k", + "data": [32], + "target": [39], + "expected": [32] + }, + { + "language": "russian_1k", + "data": [32], + "target": [32], + "expected": [32] + }, + { + "language": "russian_1k", + "data": [32], + "target": [8194], + "expected": [8194] + }, + { + "language": "russian_1k", + "data": [32], + "target": [55296], + "expected": [32] + }, + { + "language": "russian_1k", + "data": [8194], + "target": [101], + "expected": [32] + }, + { + "language": "russian_1k", + "data": [8194], + "target": [1077], + "expected": [32] + }, + { + "language": "russian_1k", + "data": [8194], + "target": [1105], + "expected": [32] + }, + { + "language": "russian_1k", + "data": [8194], + "target": [39], + "expected": [32] + }, + { + "language": "russian_1k", + "data": [8194], + "target": [32], + "expected": [32] + }, + { + "language": "russian_1k", + "data": [8194], + "target": [8194], + "expected": [8194] + }, + { + "language": "russian_1k", + "data": [8194], + "target": [55296], + "expected": [32] + }, + { + "language": "russian_1k", + "data": [8230], + "target": [101], + "expected": [8230] + }, + { + "language": "russian_1k", + "data": [8230], + "target": [1077], + "expected": [8230] + }, + { + "language": "russian_1k", + "data": [8230], + "target": [1105], + "expected": [8230] + }, + { + "language": "russian_1k", + "data": [8230], + "target": [39], + "expected": [8230] + }, + { + "language": "russian_1k", + "data": [8230], + "target": [32], + "expected": [8230] + }, + { + "language": "russian_1k", + "data": [8230], + "target": [8194], + "expected": [8230] + }, + { + "language": "russian_1k", + "data": [8230], + "target": [55296], + "expected": [8230] + }, + { + "language": "russian_1k", + "data": [55296], + "target": [101], + "expected": [55296] + }, + { + "language": "russian_1k", + "data": [55296], + "target": [1077], + "expected": [55296] + }, + { + "language": "russian_1k", + "data": [55296], + "target": [1105], + "expected": [55296] + }, + { + "language": "russian_1k", + "data": [55296], + "target": [39], + "expected": [55296] + }, + { + "language": "russian_1k", + "data": [55296], + "target": [32], + "expected": [55296] + }, + { + "language": "russian_1k", + "data": [55296], + "target": [8194], + "expected": [55296] + }, + { + "language": "russian_1k", + "data": [55296], + "target": [55296], + "expected": [55296] + }, + { + "language": "russian_25k", + "data": [101], + "target": [101], + "expected": [101] + }, + { + "language": "russian_25k", + "data": [101], + "target": [1077], + "expected": [1077] + }, + { + "language": "russian_25k", + "data": [101], + "target": [1105], + "expected": [1105] + }, + { + "language": "russian_25k", + "data": [101], + "target": [39], + "expected": [101] + }, + { + "language": "russian_25k", + "data": [101], + "target": [32], + "expected": [101] + }, + { + "language": "russian_25k", + "data": [101], + "target": [8194], + "expected": [101] + }, + { + "language": "russian_25k", + "data": [101], + "target": [55296], + "expected": [101] + }, + { + "language": "russian_25k", + "data": [1077], + "target": [101], + "expected": [101] + }, + { + "language": "russian_25k", + "data": [1077], + "target": [1077], + "expected": [1077] + }, + { + "language": "russian_25k", + "data": [1077], + "target": [1105], + "expected": [1105] + }, + { + "language": "russian_25k", + "data": [1077], + "target": [39], + "expected": [1077] + }, + { + "language": "russian_25k", + "data": [1077], + "target": [32], + "expected": [1077] + }, + { + "language": "russian_25k", + "data": [1077], + "target": [8194], + "expected": [1077] + }, + { + "language": "russian_25k", + "data": [1077], + "target": [55296], + "expected": [1077] + }, + { + "language": "russian_25k", + "data": [1105], + "target": [101], + "expected": [101] + }, + { + "language": "russian_25k", + "data": [1105], + "target": [1077], + "expected": [1077] + }, + { + "language": "russian_25k", + "data": [1105], + "target": [1105], + "expected": [1105] + }, + { + "language": "russian_25k", + "data": [1105], + "target": [39], + "expected": [1105] + }, + { + "language": "russian_25k", + "data": [1105], + "target": [32], + "expected": [1105] + }, + { + "language": "russian_25k", + "data": [1105], + "target": [8194], + "expected": [1105] + }, + { + "language": "russian_25k", + "data": [1105], + "target": [55296], + "expected": [1105] + }, + { + "language": "russian_25k", + "data": [8216], + "target": [101], + "expected": [8216] + }, + { + "language": "russian_25k", + "data": [8216], + "target": [1077], + "expected": [8216] + }, + { + "language": "russian_25k", + "data": [8216], + "target": [1105], + "expected": [8216] + }, + { + "language": "russian_25k", + "data": [8216], + "target": [39], + "expected": [39] + }, + { + "language": "russian_25k", + "data": [8216], + "target": [32], + "expected": [8216] + }, + { + "language": "russian_25k", + "data": [8216], + "target": [8194], + "expected": [8216] + }, + { + "language": "russian_25k", + "data": [8216], + "target": [55296], + "expected": [8216] + }, + { + "language": "russian_25k", + "data": [39], + "target": [101], + "expected": [39] + }, + { + "language": "russian_25k", + "data": [39], + "target": [1077], + "expected": [39] + }, + { + "language": "russian_25k", + "data": [39], + "target": [1105], + "expected": [39] + }, + { + "language": "russian_25k", + "data": [39], + "target": [39], + "expected": [39] + }, + { + "language": "russian_25k", + "data": [39], + "target": [32], + "expected": [39] + }, + { + "language": "russian_25k", + "data": [39], + "target": [8194], + "expected": [39] + }, + { + "language": "russian_25k", + "data": [39], + "target": [55296], + "expected": [39] + }, + { + "language": "russian_25k", + "data": [32], + "target": [101], + "expected": [32] + }, + { + "language": "russian_25k", + "data": [32], + "target": [1077], + "expected": [32] + }, + { + "language": "russian_25k", + "data": [32], + "target": [1105], + "expected": [32] + }, + { + "language": "russian_25k", + "data": [32], + "target": [39], + "expected": [32] + }, + { + "language": "russian_25k", + "data": [32], + "target": [32], + "expected": [32] + }, + { + "language": "russian_25k", + "data": [32], + "target": [8194], + "expected": [8194] + }, + { + "language": "russian_25k", + "data": [32], + "target": [55296], + "expected": [32] + }, + { + "language": "russian_25k", + "data": [8194], + "target": [101], + "expected": [32] + }, + { + "language": "russian_25k", + "data": [8194], + "target": [1077], + "expected": [32] + }, + { + "language": "russian_25k", + "data": [8194], + "target": [1105], + "expected": [32] + }, + { + "language": "russian_25k", + "data": [8194], + "target": [39], + "expected": [32] + }, + { + "language": "russian_25k", + "data": [8194], + "target": [32], + "expected": [32] + }, + { + "language": "russian_25k", + "data": [8194], + "target": [8194], + "expected": [8194] + }, + { + "language": "russian_25k", + "data": [8194], + "target": [55296], + "expected": [32] + }, + { + "language": "russian_25k", + "data": [8230], + "target": [101], + "expected": [8230] + }, + { + "language": "russian_25k", + "data": [8230], + "target": [1077], + "expected": [8230] + }, + { + "language": "russian_25k", + "data": [8230], + "target": [1105], + "expected": [8230] + }, + { + "language": "russian_25k", + "data": [8230], + "target": [39], + "expected": [8230] + }, + { + "language": "russian_25k", + "data": [8230], + "target": [32], + "expected": [8230] + }, + { + "language": "russian_25k", + "data": [8230], + "target": [8194], + "expected": [8230] + }, + { + "language": "russian_25k", + "data": [8230], + "target": [55296], + "expected": [8230] + }, + { + "language": "russian_25k", + "data": [55296], + "target": [101], + "expected": [55296] + }, + { + "language": "russian_25k", + "data": [55296], + "target": [1077], + "expected": [55296] + }, + { + "language": "russian_25k", + "data": [55296], + "target": [1105], + "expected": [55296] + }, + { + "language": "russian_25k", + "data": [55296], + "target": [39], + "expected": [55296] + }, + { + "language": "russian_25k", + "data": [55296], + "target": [32], + "expected": [55296] + }, + { + "language": "russian_25k", + "data": [55296], + "target": [8194], + "expected": [55296] + }, + { + "language": "russian_25k", + "data": [55296], + "target": [55296], + "expected": [55296] + }, + { + "language": "russian_phonetic", + "data": [101], + "target": [101], + "expected": [101] + }, + { + "language": "russian_phonetic", + "data": [101], + "target": [1077], + "expected": [101] + }, + { + "language": "russian_phonetic", + "data": [101], + "target": [1105], + "expected": [101] + }, + { + "language": "russian_phonetic", + "data": [101], + "target": [39], + "expected": [101] + }, + { + "language": "russian_phonetic", + "data": [101], + "target": [32], + "expected": [101] + }, + { + "language": "russian_phonetic", + "data": [101], + "target": [8194], + "expected": [101] + }, + { + "language": "russian_phonetic", + "data": [101], + "target": [55296], + "expected": [101] + }, + { + "language": "russian_phonetic", + "data": [1077], + "target": [101], + "expected": [1077] + }, + { + "language": "russian_phonetic", + "data": [1077], + "target": [1077], + "expected": [1077] + }, + { + "language": "russian_phonetic", + "data": [1077], + "target": [1105], + "expected": [1077] + }, + { + "language": "russian_phonetic", + "data": [1077], + "target": [39], + "expected": [1077] + }, + { + "language": "russian_phonetic", + "data": [1077], + "target": [32], + "expected": [1077] + }, + { + "language": "russian_phonetic", + "data": [1077], + "target": [8194], + "expected": [1077] + }, + { + "language": "russian_phonetic", + "data": [1077], + "target": [55296], + "expected": [1077] + }, + { + "language": "russian_phonetic", + "data": [1105], + "target": [101], + "expected": [1105] + }, + { + "language": "russian_phonetic", + "data": [1105], + "target": [1077], + "expected": [1105] + }, + { + "language": "russian_phonetic", + "data": [1105], + "target": [1105], + "expected": [1105] + }, + { + "language": "russian_phonetic", + "data": [1105], + "target": [39], + "expected": [1105] + }, + { + "language": "russian_phonetic", + "data": [1105], + "target": [32], + "expected": [1105] + }, + { + "language": "russian_phonetic", + "data": [1105], + "target": [8194], + "expected": [1105] + }, + { + "language": "russian_phonetic", + "data": [1105], + "target": [55296], + "expected": [1105] + }, + { + "language": "russian_phonetic", + "data": [8216], + "target": [101], + "expected": [8216] + }, + { + "language": "russian_phonetic", + "data": [8216], + "target": [1077], + "expected": [8216] + }, + { + "language": "russian_phonetic", + "data": [8216], + "target": [1105], + "expected": [8216] + }, + { + "language": "russian_phonetic", + "data": [8216], + "target": [39], + "expected": [39] + }, + { + "language": "russian_phonetic", + "data": [8216], + "target": [32], + "expected": [8216] + }, + { + "language": "russian_phonetic", + "data": [8216], + "target": [8194], + "expected": [8216] + }, + { + "language": "russian_phonetic", + "data": [8216], + "target": [55296], + "expected": [8216] + }, + { + "language": "russian_phonetic", + "data": [39], + "target": [101], + "expected": [39] + }, + { + "language": "russian_phonetic", + "data": [39], + "target": [1077], + "expected": [39] + }, + { + "language": "russian_phonetic", + "data": [39], + "target": [1105], + "expected": [39] + }, + { + "language": "russian_phonetic", + "data": [39], + "target": [39], + "expected": [39] + }, + { + "language": "russian_phonetic", + "data": [39], + "target": [32], + "expected": [39] + }, + { + "language": "russian_phonetic", + "data": [39], + "target": [8194], + "expected": [39] + }, + { + "language": "russian_phonetic", + "data": [39], + "target": [55296], + "expected": [39] + }, + { + "language": "russian_phonetic", + "data": [32], + "target": [101], + "expected": [32] + }, + { + "language": "russian_phonetic", + "data": [32], + "target": [1077], + "expected": [32] + }, + { + "language": "russian_phonetic", + "data": [32], + "target": [1105], + "expected": [32] + }, + { + "language": "russian_phonetic", + "data": [32], + "target": [39], + "expected": [32] + }, + { + "language": "russian_phonetic", + "data": [32], + "target": [32], + "expected": [32] + }, + { + "language": "russian_phonetic", + "data": [32], + "target": [8194], + "expected": [8194] + }, + { + "language": "russian_phonetic", + "data": [32], + "target": [55296], + "expected": [32] + }, + { + "language": "russian_phonetic", + "data": [8194], + "target": [101], + "expected": [32] + }, + { + "language": "russian_phonetic", + "data": [8194], + "target": [1077], + "expected": [32] + }, + { + "language": "russian_phonetic", + "data": [8194], + "target": [1105], + "expected": [32] + }, + { + "language": "russian_phonetic", + "data": [8194], + "target": [39], + "expected": [32] + }, + { + "language": "russian_phonetic", + "data": [8194], + "target": [32], + "expected": [32] + }, + { + "language": "russian_phonetic", + "data": [8194], + "target": [8194], + "expected": [8194] + }, + { + "language": "russian_phonetic", + "data": [8194], + "target": [55296], + "expected": [32] + }, + { + "language": "russian_phonetic", + "data": [8230], + "target": [101], + "expected": [8230] + }, + { + "language": "russian_phonetic", + "data": [8230], + "target": [1077], + "expected": [8230] + }, + { + "language": "russian_phonetic", + "data": [8230], + "target": [1105], + "expected": [8230] + }, + { + "language": "russian_phonetic", + "data": [8230], + "target": [39], + "expected": [8230] + }, + { + "language": "russian_phonetic", + "data": [8230], + "target": [32], + "expected": [8230] + }, + { + "language": "russian_phonetic", + "data": [8230], + "target": [8194], + "expected": [8230] + }, + { + "language": "russian_phonetic", + "data": [8230], + "target": [55296], + "expected": [8230] + }, + { + "language": "russian_phonetic", + "data": [55296], + "target": [101], + "expected": [55296] + }, + { + "language": "russian_phonetic", + "data": [55296], + "target": [1077], + "expected": [55296] + }, + { + "language": "russian_phonetic", + "data": [55296], + "target": [1105], + "expected": [55296] + }, + { + "language": "russian_phonetic", + "data": [55296], + "target": [39], + "expected": [55296] + }, + { + "language": "russian_phonetic", + "data": [55296], + "target": [32], + "expected": [55296] + }, + { + "language": "russian_phonetic", + "data": [55296], + "target": [8194], + "expected": [55296] + }, + { + "language": "russian_phonetic", + "data": [55296], + "target": [55296], + "expected": [55296] + }, + { + "language": "russian_phonetic_1k", + "data": [101], + "target": [101], + "expected": [101] + }, + { + "language": "russian_phonetic_1k", + "data": [101], + "target": [1077], + "expected": [101] + }, + { + "language": "russian_phonetic_1k", + "data": [101], + "target": [1105], + "expected": [101] + }, + { + "language": "russian_phonetic_1k", + "data": [101], + "target": [39], + "expected": [101] + }, + { + "language": "russian_phonetic_1k", + "data": [101], + "target": [32], + "expected": [101] + }, + { + "language": "russian_phonetic_1k", + "data": [101], + "target": [8194], + "expected": [101] + }, + { + "language": "russian_phonetic_1k", + "data": [101], + "target": [55296], + "expected": [101] + }, + { + "language": "russian_phonetic_1k", + "data": [1077], + "target": [101], + "expected": [1077] + }, + { + "language": "russian_phonetic_1k", + "data": [1077], + "target": [1077], + "expected": [1077] + }, + { + "language": "russian_phonetic_1k", + "data": [1077], + "target": [1105], + "expected": [1077] + }, + { + "language": "russian_phonetic_1k", + "data": [1077], + "target": [39], + "expected": [1077] + }, + { + "language": "russian_phonetic_1k", + "data": [1077], + "target": [32], + "expected": [1077] + }, + { + "language": "russian_phonetic_1k", + "data": [1077], + "target": [8194], + "expected": [1077] + }, + { + "language": "russian_phonetic_1k", + "data": [1077], + "target": [55296], + "expected": [1077] + }, + { + "language": "russian_phonetic_1k", + "data": [1105], + "target": [101], + "expected": [1105] + }, + { + "language": "russian_phonetic_1k", + "data": [1105], + "target": [1077], + "expected": [1105] + }, + { + "language": "russian_phonetic_1k", + "data": [1105], + "target": [1105], + "expected": [1105] + }, + { + "language": "russian_phonetic_1k", + "data": [1105], + "target": [39], + "expected": [1105] + }, + { + "language": "russian_phonetic_1k", + "data": [1105], + "target": [32], + "expected": [1105] + }, + { + "language": "russian_phonetic_1k", + "data": [1105], + "target": [8194], + "expected": [1105] + }, + { + "language": "russian_phonetic_1k", + "data": [1105], + "target": [55296], + "expected": [1105] + }, + { + "language": "russian_phonetic_1k", + "data": [8216], + "target": [101], + "expected": [8216] + }, + { + "language": "russian_phonetic_1k", + "data": [8216], + "target": [1077], + "expected": [8216] + }, + { + "language": "russian_phonetic_1k", + "data": [8216], + "target": [1105], + "expected": [8216] + }, + { + "language": "russian_phonetic_1k", + "data": [8216], + "target": [39], + "expected": [39] + }, + { + "language": "russian_phonetic_1k", + "data": [8216], + "target": [32], + "expected": [8216] + }, + { + "language": "russian_phonetic_1k", + "data": [8216], + "target": [8194], + "expected": [8216] + }, + { + "language": "russian_phonetic_1k", + "data": [8216], + "target": [55296], + "expected": [8216] + }, + { + "language": "russian_phonetic_1k", + "data": [39], + "target": [101], + "expected": [39] + }, + { + "language": "russian_phonetic_1k", + "data": [39], + "target": [1077], + "expected": [39] + }, + { + "language": "russian_phonetic_1k", + "data": [39], + "target": [1105], + "expected": [39] + }, + { + "language": "russian_phonetic_1k", + "data": [39], + "target": [39], + "expected": [39] + }, + { + "language": "russian_phonetic_1k", + "data": [39], + "target": [32], + "expected": [39] + }, + { + "language": "russian_phonetic_1k", + "data": [39], + "target": [8194], + "expected": [39] + }, + { + "language": "russian_phonetic_1k", + "data": [39], + "target": [55296], + "expected": [39] + }, + { + "language": "russian_phonetic_1k", + "data": [32], + "target": [101], + "expected": [32] + }, + { + "language": "russian_phonetic_1k", + "data": [32], + "target": [1077], + "expected": [32] + }, + { + "language": "russian_phonetic_1k", + "data": [32], + "target": [1105], + "expected": [32] + }, + { + "language": "russian_phonetic_1k", + "data": [32], + "target": [39], + "expected": [32] + }, + { + "language": "russian_phonetic_1k", + "data": [32], + "target": [32], + "expected": [32] + }, + { + "language": "russian_phonetic_1k", + "data": [32], + "target": [8194], + "expected": [8194] + }, + { + "language": "russian_phonetic_1k", + "data": [32], + "target": [55296], + "expected": [32] + }, + { + "language": "russian_phonetic_1k", + "data": [8194], + "target": [101], + "expected": [32] + }, + { + "language": "russian_phonetic_1k", + "data": [8194], + "target": [1077], + "expected": [32] + }, + { + "language": "russian_phonetic_1k", + "data": [8194], + "target": [1105], + "expected": [32] + }, + { + "language": "russian_phonetic_1k", + "data": [8194], + "target": [39], + "expected": [32] + }, + { + "language": "russian_phonetic_1k", + "data": [8194], + "target": [32], + "expected": [32] + }, + { + "language": "russian_phonetic_1k", + "data": [8194], + "target": [8194], + "expected": [8194] + }, + { + "language": "russian_phonetic_1k", + "data": [8194], + "target": [55296], + "expected": [32] + }, + { + "language": "russian_phonetic_1k", + "data": [8230], + "target": [101], + "expected": [8230] + }, + { + "language": "russian_phonetic_1k", + "data": [8230], + "target": [1077], + "expected": [8230] + }, + { + "language": "russian_phonetic_1k", + "data": [8230], + "target": [1105], + "expected": [8230] + }, + { + "language": "russian_phonetic_1k", + "data": [8230], + "target": [39], + "expected": [8230] + }, + { + "language": "russian_phonetic_1k", + "data": [8230], + "target": [32], + "expected": [8230] + }, + { + "language": "russian_phonetic_1k", + "data": [8230], + "target": [8194], + "expected": [8230] + }, + { + "language": "russian_phonetic_1k", + "data": [8230], + "target": [55296], + "expected": [8230] + }, + { + "language": "russian_phonetic_1k", + "data": [55296], + "target": [101], + "expected": [55296] + }, + { + "language": "russian_phonetic_1k", + "data": [55296], + "target": [1077], + "expected": [55296] + }, + { + "language": "russian_phonetic_1k", + "data": [55296], + "target": [1105], + "expected": [55296] + }, + { + "language": "russian_phonetic_1k", + "data": [55296], + "target": [39], + "expected": [55296] + }, + { + "language": "russian_phonetic_1k", + "data": [55296], + "target": [32], + "expected": [55296] + }, + { + "language": "russian_phonetic_1k", + "data": [55296], + "target": [8194], + "expected": [55296] + }, + { + "language": "russian_phonetic_1k", + "data": [55296], + "target": [55296], + "expected": [55296] + }, + { + "language": "russian_k", + "data": [101], + "target": [101], + "expected": [101] + }, + { + "language": "russian_k", + "data": [101], + "target": [1077], + "expected": [1077] + }, + { + "language": "russian_k", + "data": [101], + "target": [1105], + "expected": [1105] + }, + { + "language": "russian_k", + "data": [101], + "target": [39], + "expected": [101] + }, + { + "language": "russian_k", + "data": [101], + "target": [32], + "expected": [101] + }, + { + "language": "russian_k", + "data": [101], + "target": [8194], + "expected": [101] + }, + { + "language": "russian_k", + "data": [101], + "target": [55296], + "expected": [101] + }, + { + "language": "russian_k", + "data": [1077], + "target": [101], + "expected": [101] + }, + { + "language": "russian_k", + "data": [1077], + "target": [1077], + "expected": [1077] + }, + { + "language": "russian_k", + "data": [1077], + "target": [1105], + "expected": [1105] + }, + { + "language": "russian_k", + "data": [1077], + "target": [39], + "expected": [1077] + }, + { + "language": "russian_k", + "data": [1077], + "target": [32], + "expected": [1077] + }, + { + "language": "russian_k", + "data": [1077], + "target": [8194], + "expected": [1077] + }, + { + "language": "russian_k", + "data": [1077], + "target": [55296], + "expected": [1077] + }, + { + "language": "russian_k", + "data": [1105], + "target": [101], + "expected": [101] + }, + { + "language": "russian_k", + "data": [1105], + "target": [1077], + "expected": [1077] + }, + { + "language": "russian_k", + "data": [1105], + "target": [1105], + "expected": [1105] + }, + { + "language": "russian_k", + "data": [1105], + "target": [39], + "expected": [1105] + }, + { + "language": "russian_k", + "data": [1105], + "target": [32], + "expected": [1105] + }, + { + "language": "russian_k", + "data": [1105], + "target": [8194], + "expected": [1105] + }, + { + "language": "russian_k", + "data": [1105], + "target": [55296], + "expected": [1105] + }, + { + "language": "russian_k", + "data": [8216], + "target": [101], + "expected": [8216] + }, + { + "language": "russian_k", + "data": [8216], + "target": [1077], + "expected": [8216] + }, + { + "language": "russian_k", + "data": [8216], + "target": [1105], + "expected": [8216] + }, + { + "language": "russian_k", + "data": [8216], + "target": [39], + "expected": [39] + }, + { + "language": "russian_k", + "data": [8216], + "target": [32], + "expected": [8216] + }, + { + "language": "russian_k", + "data": [8216], + "target": [8194], + "expected": [8216] + }, + { + "language": "russian_k", + "data": [8216], + "target": [55296], + "expected": [8216] + }, + { + "language": "russian_k", + "data": [39], + "target": [101], + "expected": [39] + }, + { + "language": "russian_k", + "data": [39], + "target": [1077], + "expected": [39] + }, + { + "language": "russian_k", + "data": [39], + "target": [1105], + "expected": [39] + }, + { + "language": "russian_k", + "data": [39], + "target": [39], + "expected": [39] + }, + { + "language": "russian_k", + "data": [39], + "target": [32], + "expected": [39] + }, + { + "language": "russian_k", + "data": [39], + "target": [8194], + "expected": [39] + }, + { + "language": "russian_k", + "data": [39], + "target": [55296], + "expected": [39] + }, + { + "language": "russian_k", + "data": [32], + "target": [101], + "expected": [32] + }, + { + "language": "russian_k", + "data": [32], + "target": [1077], + "expected": [32] + }, + { + "language": "russian_k", + "data": [32], + "target": [1105], + "expected": [32] + }, + { + "language": "russian_k", + "data": [32], + "target": [39], + "expected": [32] + }, + { + "language": "russian_k", + "data": [32], + "target": [32], + "expected": [32] + }, + { + "language": "russian_k", + "data": [32], + "target": [8194], + "expected": [8194] + }, + { + "language": "russian_k", + "data": [32], + "target": [55296], + "expected": [32] + }, + { + "language": "russian_k", + "data": [8194], + "target": [101], + "expected": [32] + }, + { + "language": "russian_k", + "data": [8194], + "target": [1077], + "expected": [32] + }, + { + "language": "russian_k", + "data": [8194], + "target": [1105], + "expected": [32] + }, + { + "language": "russian_k", + "data": [8194], + "target": [39], + "expected": [32] + }, + { + "language": "russian_k", + "data": [8194], + "target": [32], + "expected": [32] + }, + { + "language": "russian_k", + "data": [8194], + "target": [8194], + "expected": [8194] + }, + { + "language": "russian_k", + "data": [8194], + "target": [55296], + "expected": [32] + }, + { + "language": "russian_k", + "data": [8230], + "target": [101], + "expected": [8230] + }, + { + "language": "russian_k", + "data": [8230], + "target": [1077], + "expected": [8230] + }, + { + "language": "russian_k", + "data": [8230], + "target": [1105], + "expected": [8230] + }, + { + "language": "russian_k", + "data": [8230], + "target": [39], + "expected": [8230] + }, + { + "language": "russian_k", + "data": [8230], + "target": [32], + "expected": [8230] + }, + { + "language": "russian_k", + "data": [8230], + "target": [8194], + "expected": [8230] + }, + { + "language": "russian_k", + "data": [8230], + "target": [55296], + "expected": [8230] + }, + { + "language": "russian_k", + "data": [55296], + "target": [101], + "expected": [55296] + }, + { + "language": "russian_k", + "data": [55296], + "target": [1077], + "expected": [55296] + }, + { + "language": "russian_k", + "data": [55296], + "target": [1105], + "expected": [55296] + }, + { + "language": "russian_k", + "data": [55296], + "target": [39], + "expected": [55296] + }, + { + "language": "russian_k", + "data": [55296], + "target": [32], + "expected": [55296] + }, + { + "language": "russian_k", + "data": [55296], + "target": [8194], + "expected": [55296] + }, + { + "language": "russian_k", + "data": [55296], + "target": [55296], + "expected": [55296] + }, + { + "language": "russian_1kk", + "data": [101], + "target": [101], + "expected": [101] + }, + { + "language": "russian_1kk", + "data": [101], + "target": [1077], + "expected": [101] + }, + { + "language": "russian_1kk", + "data": [101], + "target": [1105], + "expected": [101] + }, + { + "language": "russian_1kk", + "data": [101], + "target": [39], + "expected": [101] + }, + { + "language": "russian_1kk", + "data": [101], + "target": [32], + "expected": [101] + }, + { + "language": "russian_1kk", + "data": [101], + "target": [8194], + "expected": [101] + }, + { + "language": "russian_1kk", + "data": [101], + "target": [55296], + "expected": [101] + }, + { + "language": "russian_1kk", + "data": [1077], + "target": [101], + "expected": [1077] + }, + { + "language": "russian_1kk", + "data": [1077], + "target": [1077], + "expected": [1077] + }, + { + "language": "russian_1kk", + "data": [1077], + "target": [1105], + "expected": [1077] + }, + { + "language": "russian_1kk", + "data": [1077], + "target": [39], + "expected": [1077] + }, + { + "language": "russian_1kk", + "data": [1077], + "target": [32], + "expected": [1077] + }, + { + "language": "russian_1kk", + "data": [1077], + "target": [8194], + "expected": [1077] + }, + { + "language": "russian_1kk", + "data": [1077], + "target": [55296], + "expected": [1077] + }, + { + "language": "russian_1kk", + "data": [1105], + "target": [101], + "expected": [1105] + }, + { + "language": "russian_1kk", + "data": [1105], + "target": [1077], + "expected": [1105] + }, + { + "language": "russian_1kk", + "data": [1105], + "target": [1105], + "expected": [1105] + }, + { + "language": "russian_1kk", + "data": [1105], + "target": [39], + "expected": [1105] + }, + { + "language": "russian_1kk", + "data": [1105], + "target": [32], + "expected": [1105] + }, + { + "language": "russian_1kk", + "data": [1105], + "target": [8194], + "expected": [1105] + }, + { + "language": "russian_1kk", + "data": [1105], + "target": [55296], + "expected": [1105] + }, + { + "language": "russian_1kk", + "data": [8216], + "target": [101], + "expected": [8216] + }, + { + "language": "russian_1kk", + "data": [8216], + "target": [1077], + "expected": [8216] + }, + { + "language": "russian_1kk", + "data": [8216], + "target": [1105], + "expected": [8216] + }, + { + "language": "russian_1kk", + "data": [8216], + "target": [39], + "expected": [39] + }, + { + "language": "russian_1kk", + "data": [8216], + "target": [32], + "expected": [8216] + }, + { + "language": "russian_1kk", + "data": [8216], + "target": [8194], + "expected": [8216] + }, + { + "language": "russian_1kk", + "data": [8216], + "target": [55296], + "expected": [8216] + }, + { + "language": "russian_1kk", + "data": [39], + "target": [101], + "expected": [39] + }, + { + "language": "russian_1kk", + "data": [39], + "target": [1077], + "expected": [39] + }, + { + "language": "russian_1kk", + "data": [39], + "target": [1105], + "expected": [39] + }, + { + "language": "russian_1kk", + "data": [39], + "target": [39], + "expected": [39] + }, + { + "language": "russian_1kk", + "data": [39], + "target": [32], + "expected": [39] + }, + { + "language": "russian_1kk", + "data": [39], + "target": [8194], + "expected": [39] + }, + { + "language": "russian_1kk", + "data": [39], + "target": [55296], + "expected": [39] + }, + { + "language": "russian_1kk", + "data": [32], + "target": [101], + "expected": [32] + }, + { + "language": "russian_1kk", + "data": [32], + "target": [1077], + "expected": [32] + }, + { + "language": "russian_1kk", + "data": [32], + "target": [1105], + "expected": [32] + }, + { + "language": "russian_1kk", + "data": [32], + "target": [39], + "expected": [32] + }, + { + "language": "russian_1kk", + "data": [32], + "target": [32], + "expected": [32] + }, + { + "language": "russian_1kk", + "data": [32], + "target": [8194], + "expected": [8194] + }, + { + "language": "russian_1kk", + "data": [32], + "target": [55296], + "expected": [32] + }, + { + "language": "russian_1kk", + "data": [8194], + "target": [101], + "expected": [32] + }, + { + "language": "russian_1kk", + "data": [8194], + "target": [1077], + "expected": [32] + }, + { + "language": "russian_1kk", + "data": [8194], + "target": [1105], + "expected": [32] + }, + { + "language": "russian_1kk", + "data": [8194], + "target": [39], + "expected": [32] + }, + { + "language": "russian_1kk", + "data": [8194], + "target": [32], + "expected": [32] + }, + { + "language": "russian_1kk", + "data": [8194], + "target": [8194], + "expected": [8194] + }, + { + "language": "russian_1kk", + "data": [8194], + "target": [55296], + "expected": [32] + }, + { + "language": "russian_1kk", + "data": [8230], + "target": [101], + "expected": [8230] + }, + { + "language": "russian_1kk", + "data": [8230], + "target": [1077], + "expected": [8230] + }, + { + "language": "russian_1kk", + "data": [8230], + "target": [1105], + "expected": [8230] + }, + { + "language": "russian_1kk", + "data": [8230], + "target": [39], + "expected": [8230] + }, + { + "language": "russian_1kk", + "data": [8230], + "target": [32], + "expected": [8230] + }, + { + "language": "russian_1kk", + "data": [8230], + "target": [8194], + "expected": [8230] + }, + { + "language": "russian_1kk", + "data": [8230], + "target": [55296], + "expected": [8230] + }, + { + "language": "russian_1kk", + "data": [55296], + "target": [101], + "expected": [55296] + }, + { + "language": "russian_1kk", + "data": [55296], + "target": [1077], + "expected": [55296] + }, + { + "language": "russian_1kk", + "data": [55296], + "target": [1105], + "expected": [55296] + }, + { + "language": "russian_1kk", + "data": [55296], + "target": [39], + "expected": [55296] + }, + { + "language": "russian_1kk", + "data": [55296], + "target": [32], + "expected": [55296] + }, + { + "language": "russian_1kk", + "data": [55296], + "target": [8194], + "expected": [55296] + }, + { + "language": "russian_1kk", + "data": [55296], + "target": [55296], + "expected": [55296] + }, + { + "language": "dutch", + "data": [101], + "target": [101], + "expected": [101] + }, + { + "language": "dutch", + "data": [101], + "target": [1077], + "expected": [101] + }, + { + "language": "dutch", + "data": [101], + "target": [1105], + "expected": [101] + }, + { + "language": "dutch", + "data": [101], + "target": [39], + "expected": [101] + }, + { + "language": "dutch", + "data": [101], + "target": [32], + "expected": [101] + }, + { + "language": "dutch", + "data": [101], + "target": [8194], + "expected": [101] + }, + { + "language": "dutch", + "data": [101], + "target": [55296], + "expected": [101] + }, + { + "language": "dutch", + "data": [1077], + "target": [101], + "expected": [1077] + }, + { + "language": "dutch", + "data": [1077], + "target": [1077], + "expected": [1077] + }, + { + "language": "dutch", + "data": [1077], + "target": [1105], + "expected": [1077] + }, + { + "language": "dutch", + "data": [1077], + "target": [39], + "expected": [1077] + }, + { + "language": "dutch", + "data": [1077], + "target": [32], + "expected": [1077] + }, + { + "language": "dutch", + "data": [1077], + "target": [8194], + "expected": [1077] + }, + { + "language": "dutch", + "data": [1077], + "target": [55296], + "expected": [1077] + }, + { + "language": "dutch", + "data": [1105], + "target": [101], + "expected": [1105] + }, + { + "language": "dutch", + "data": [1105], + "target": [1077], + "expected": [1105] + }, + { + "language": "dutch", + "data": [1105], + "target": [1105], + "expected": [1105] + }, + { + "language": "dutch", + "data": [1105], + "target": [39], + "expected": [1105] + }, + { + "language": "dutch", + "data": [1105], + "target": [32], + "expected": [1105] + }, + { + "language": "dutch", + "data": [1105], + "target": [8194], + "expected": [1105] + }, + { + "language": "dutch", + "data": [1105], + "target": [55296], + "expected": [1105] + }, + { + "language": "dutch", + "data": [8216], + "target": [101], + "expected": [8216] + }, + { + "language": "dutch", + "data": [8216], + "target": [1077], + "expected": [8216] + }, + { + "language": "dutch", + "data": [8216], + "target": [1105], + "expected": [8216] + }, + { + "language": "dutch", + "data": [8216], + "target": [39], + "expected": [39] + }, + { + "language": "dutch", + "data": [8216], + "target": [32], + "expected": [8216] + }, + { + "language": "dutch", + "data": [8216], + "target": [8194], + "expected": [8216] + }, + { + "language": "dutch", + "data": [8216], + "target": [55296], + "expected": [8216] + }, + { + "language": "dutch", + "data": [39], + "target": [101], + "expected": [39] + }, + { + "language": "dutch", + "data": [39], + "target": [1077], + "expected": [39] + }, + { + "language": "dutch", + "data": [39], + "target": [1105], + "expected": [39] + }, + { + "language": "dutch", + "data": [39], + "target": [39], + "expected": [39] + }, + { + "language": "dutch", + "data": [39], + "target": [32], + "expected": [39] + }, + { + "language": "dutch", + "data": [39], + "target": [8194], + "expected": [39] + }, + { + "language": "dutch", + "data": [39], + "target": [55296], + "expected": [39] + }, + { + "language": "dutch", + "data": [32], + "target": [101], + "expected": [32] + }, + { + "language": "dutch", + "data": [32], + "target": [1077], + "expected": [32] + }, + { + "language": "dutch", + "data": [32], + "target": [1105], + "expected": [32] + }, + { + "language": "dutch", + "data": [32], + "target": [39], + "expected": [32] + }, + { + "language": "dutch", + "data": [32], + "target": [32], + "expected": [32] + }, + { + "language": "dutch", + "data": [32], + "target": [8194], + "expected": [8194] + }, + { + "language": "dutch", + "data": [32], + "target": [55296], + "expected": [32] + }, + { + "language": "dutch", + "data": [8194], + "target": [101], + "expected": [32] + }, + { + "language": "dutch", + "data": [8194], + "target": [1077], + "expected": [32] + }, + { + "language": "dutch", + "data": [8194], + "target": [1105], + "expected": [32] + }, + { + "language": "dutch", + "data": [8194], + "target": [39], + "expected": [32] + }, + { + "language": "dutch", + "data": [8194], + "target": [32], + "expected": [32] + }, + { + "language": "dutch", + "data": [8194], + "target": [8194], + "expected": [8194] + }, + { + "language": "dutch", + "data": [8194], + "target": [55296], + "expected": [32] + }, + { + "language": "dutch", + "data": [8230], + "target": [101], + "expected": [8230] + }, + { + "language": "dutch", + "data": [8230], + "target": [1077], + "expected": [8230] + }, + { + "language": "dutch", + "data": [8230], + "target": [1105], + "expected": [8230] + }, + { + "language": "dutch", + "data": [8230], + "target": [39], + "expected": [8230] + }, + { + "language": "dutch", + "data": [8230], + "target": [32], + "expected": [8230] + }, + { + "language": "dutch", + "data": [8230], + "target": [8194], + "expected": [8230] + }, + { + "language": "dutch", + "data": [8230], + "target": [55296], + "expected": [8230] + }, + { + "language": "dutch", + "data": [55296], + "target": [101], + "expected": [55296] + }, + { + "language": "dutch", + "data": [55296], + "target": [1077], + "expected": [55296] + }, + { + "language": "dutch", + "data": [55296], + "target": [1105], + "expected": [55296] + }, + { + "language": "dutch", + "data": [55296], + "target": [39], + "expected": [55296] + }, + { + "language": "dutch", + "data": [55296], + "target": [32], + "expected": [55296] + }, + { + "language": "dutch", + "data": [55296], + "target": [8194], + "expected": [55296] + }, + { + "language": "dutch", + "data": [55296], + "target": [55296], + "expected": [55296] + }, + { + "language": "dutch_1k", + "data": [101], + "target": [101], + "expected": [101] + }, + { + "language": "dutch_1k", + "data": [101], + "target": [1077], + "expected": [101] + }, + { + "language": "dutch_1k", + "data": [101], + "target": [1105], + "expected": [101] + }, + { + "language": "dutch_1k", + "data": [101], + "target": [39], + "expected": [101] + }, + { + "language": "dutch_1k", + "data": [101], + "target": [32], + "expected": [101] + }, + { + "language": "dutch_1k", + "data": [101], + "target": [8194], + "expected": [101] + }, + { + "language": "dutch_1k", + "data": [101], + "target": [55296], + "expected": [101] + }, + { + "language": "dutch_1k", + "data": [1077], + "target": [101], + "expected": [1077] + }, + { + "language": "dutch_1k", + "data": [1077], + "target": [1077], + "expected": [1077] + }, + { + "language": "dutch_1k", + "data": [1077], + "target": [1105], + "expected": [1077] + }, + { + "language": "dutch_1k", + "data": [1077], + "target": [39], + "expected": [1077] + }, + { + "language": "dutch_1k", + "data": [1077], + "target": [32], + "expected": [1077] + }, + { + "language": "dutch_1k", + "data": [1077], + "target": [8194], + "expected": [1077] + }, + { + "language": "dutch_1k", + "data": [1077], + "target": [55296], + "expected": [1077] + }, + { + "language": "dutch_1k", + "data": [1105], + "target": [101], + "expected": [1105] + }, + { + "language": "dutch_1k", + "data": [1105], + "target": [1077], + "expected": [1105] + }, + { + "language": "dutch_1k", + "data": [1105], + "target": [1105], + "expected": [1105] + }, + { + "language": "dutch_1k", + "data": [1105], + "target": [39], + "expected": [1105] + }, + { + "language": "dutch_1k", + "data": [1105], + "target": [32], + "expected": [1105] + }, + { + "language": "dutch_1k", + "data": [1105], + "target": [8194], + "expected": [1105] + }, + { + "language": "dutch_1k", + "data": [1105], + "target": [55296], + "expected": [1105] + }, + { + "language": "dutch_1k", + "data": [8216], + "target": [101], + "expected": [8216] + }, + { + "language": "dutch_1k", + "data": [8216], + "target": [1077], + "expected": [8216] + }, + { + "language": "dutch_1k", + "data": [8216], + "target": [1105], + "expected": [8216] + }, + { + "language": "dutch_1k", + "data": [8216], + "target": [39], + "expected": [39] + }, + { + "language": "dutch_1k", + "data": [8216], + "target": [32], + "expected": [8216] + }, + { + "language": "dutch_1k", + "data": [8216], + "target": [8194], + "expected": [8216] + }, + { + "language": "dutch_1k", + "data": [8216], + "target": [55296], + "expected": [8216] + }, + { + "language": "dutch_1k", + "data": [39], + "target": [101], + "expected": [39] + }, + { + "language": "dutch_1k", + "data": [39], + "target": [1077], + "expected": [39] + }, + { + "language": "dutch_1k", + "data": [39], + "target": [1105], + "expected": [39] + }, + { + "language": "dutch_1k", + "data": [39], + "target": [39], + "expected": [39] + }, + { + "language": "dutch_1k", + "data": [39], + "target": [32], + "expected": [39] + }, + { + "language": "dutch_1k", + "data": [39], + "target": [8194], + "expected": [39] + }, + { + "language": "dutch_1k", + "data": [39], + "target": [55296], + "expected": [39] + }, + { + "language": "dutch_1k", + "data": [32], + "target": [101], + "expected": [32] + }, + { + "language": "dutch_1k", + "data": [32], + "target": [1077], + "expected": [32] + }, + { + "language": "dutch_1k", + "data": [32], + "target": [1105], + "expected": [32] + }, + { + "language": "dutch_1k", + "data": [32], + "target": [39], + "expected": [32] + }, + { + "language": "dutch_1k", + "data": [32], + "target": [32], + "expected": [32] + }, + { + "language": "dutch_1k", + "data": [32], + "target": [8194], + "expected": [8194] + }, + { + "language": "dutch_1k", + "data": [32], + "target": [55296], + "expected": [32] + }, + { + "language": "dutch_1k", + "data": [8194], + "target": [101], + "expected": [32] + }, + { + "language": "dutch_1k", + "data": [8194], + "target": [1077], + "expected": [32] + }, + { + "language": "dutch_1k", + "data": [8194], + "target": [1105], + "expected": [32] + }, + { + "language": "dutch_1k", + "data": [8194], + "target": [39], + "expected": [32] + }, + { + "language": "dutch_1k", + "data": [8194], + "target": [32], + "expected": [32] + }, + { + "language": "dutch_1k", + "data": [8194], + "target": [8194], + "expected": [8194] + }, + { + "language": "dutch_1k", + "data": [8194], + "target": [55296], + "expected": [32] + }, + { + "language": "dutch_1k", + "data": [8230], + "target": [101], + "expected": [8230] + }, + { + "language": "dutch_1k", + "data": [8230], + "target": [1077], + "expected": [8230] + }, + { + "language": "dutch_1k", + "data": [8230], + "target": [1105], + "expected": [8230] + }, + { + "language": "dutch_1k", + "data": [8230], + "target": [39], + "expected": [8230] + }, + { + "language": "dutch_1k", + "data": [8230], + "target": [32], + "expected": [8230] + }, + { + "language": "dutch_1k", + "data": [8230], + "target": [8194], + "expected": [8230] + }, + { + "language": "dutch_1k", + "data": [8230], + "target": [55296], + "expected": [8230] + }, + { + "language": "dutch_1k", + "data": [55296], + "target": [101], + "expected": [55296] + }, + { + "language": "dutch_1k", + "data": [55296], + "target": [1077], + "expected": [55296] + }, + { + "language": "dutch_1k", + "data": [55296], + "target": [1105], + "expected": [55296] + }, + { + "language": "dutch_1k", + "data": [55296], + "target": [39], + "expected": [55296] + }, + { + "language": "dutch_1k", + "data": [55296], + "target": [32], + "expected": [55296] + }, + { + "language": "dutch_1k", + "data": [55296], + "target": [8194], + "expected": [55296] + }, + { + "language": "dutch_1k", + "data": [55296], + "target": [55296], + "expected": [55296] + } +] From 8271cf5ff6cb82d1d27d93d7f761cf207e7d1919 Mon Sep 17 00:00:00 2001 From: Lakshmi Tanmay Date: Fri, 23 Oct 2026 05:41:17 +0530 Subject: [PATCH 56/93] Retain app-owned learning across session replacement and expose typed pace commands --- crates/typing-engine-wasm/src/lib.rs | 24 ++ .../typing-engine/src/bin/export-bindings.rs | 2 + crates/typing-engine/src/generation.rs | 13 +- crates/typing-engine/src/pace.rs | 97 ++++- crates/typing-engine/src/session.rs | 14 + crates/typing-engine/src/weak_spot.rs | 4 +- crates/typing-engine/tests/learning.rs | 113 ++++++ crates/typing-engine/tests/pace.rs | 14 + docs/rust-engine/contract.md | 4 + docs/rust-engine/progress.md | 2 + .../scripts/capture-pace-oracle.ts | 52 +++ packages/typing-fixtures/oracle/pace.json | 363 ++++++++++++++++++ 12 files changed, 687 insertions(+), 15 deletions(-) create mode 100644 crates/typing-engine/tests/learning.rs create mode 100644 packages/typing-core/scripts/capture-pace-oracle.ts create mode 100644 packages/typing-fixtures/oracle/pace.json diff --git a/crates/typing-engine-wasm/src/lib.rs b/crates/typing-engine-wasm/src/lib.rs index 6889b2f66..aad1e981c 100644 --- a/crates/typing-engine-wasm/src/lib.rs +++ b/crates/typing-engine-wasm/src/lib.rs @@ -46,6 +46,18 @@ impl WasmSession { .ok_or_else(|| JsError::new("Session disposed"))?; Ok(serde_json::to_string(&session.preflight(data, &layout))?) } + pub fn pace(&mut self, command: &str) -> Result { + let command = serde_json::from_str(command)?; + let session = self + .session + .as_mut() + .ok_or_else(|| JsError::new("Session disposed"))?; + Ok(serde_json::to_string( + &session + .pace_command(command) + .map_err(|message| JsError::new(&message))?, + )?) + } pub fn snapshot(&self) -> Result { let session = self .session @@ -83,6 +95,18 @@ pub struct WasmContent { } #[wasm_bindgen] impl WasmContent { + pub fn recycle_session(&mut self, session: &mut WasmSession) -> Result<(), JsError> { + let content = self + .content + .as_mut() + .ok_or_else(|| JsError::new("Content disposed"))?; + let session = session + .session + .take() + .ok_or_else(|| JsError::new("Session disposed"))?; + content.recycle_session(session); + Ok(()) + } #[wasm_bindgen(constructor)] pub fn new(version: u32) -> Result { if version != BINDING_VERSION { diff --git a/crates/typing-engine/src/bin/export-bindings.rs b/crates/typing-engine/src/bin/export-bindings.rs index c80c8bb6b..0643ffcc1 100644 --- a/crates/typing-engine/src/bin/export-bindings.rs +++ b/crates/typing-engine/src/bin/export-bindings.rs @@ -32,6 +32,8 @@ fn main() -> Result<(), Box> { typing_engine::replay::Seek::export_all(&config)?; typing_engine::replay::PlaybackSnapshot::export_all(&config)?; typing_engine::replay::PlaybackUpdate::export_all(&config)?; + typing_engine::pace::PaceCommand::export_all(&config)?; + typing_engine::pace::PaceUpdate::export_all(&config)?; typing_engine::generation::Prepare::export_all(&config)?; typing_engine::generation::Generated::export_all(&config)?; typing_engine::generation::NextWord::export_all(&config)?; diff --git a/crates/typing-engine/src/generation.rs b/crates/typing-engine/src/generation.rs index 0effc9f00..76920a520 100644 --- a/crates/typing-engine/src/generation.rs +++ b/crates/typing-engine/src/generation.rs @@ -807,8 +807,16 @@ pub struct Prepare { pub struct Content { assets: Assets, generator: Option, + weak_spot: crate::weak_spot::WeakSpot, + last_pace_wpm: f64, } impl Content { + /// Consume the prior session before preparing its replacement. Only learning + /// survives; logs, pending replies and input state are dropped with the session. + pub fn recycle_session(&mut self, session: crate::Session) { + (self.weak_spot, self.last_pace_wpm) = session.into_learning(); + self.generator = None; + } pub fn prepare(&mut self, input: Prepare) -> Result<(), String> { self.generator = None; self.assets.quote_asset = input.quotes; @@ -823,12 +831,15 @@ impl Content { input.random, &mut self.assets, )?); + self.generator.as_mut().unwrap().weak_spot = self.weak_spot.clone(); Ok(()) } pub fn start_session(&mut self, repeating: bool) -> Result { let generated = self.generator()?.generate(repeating)?; let generator = self.generator.take().ok_or("Content not prepared")?; - crate::Session::from_generated(generator, generated) + let mut session = crate::Session::from_generated(generator, generated)?; + session.pace.remember(self.last_pace_wpm, false); + Ok(session) } pub fn generator(&mut self) -> Result<&mut Generator, String> { self.generator.as_mut().ok_or("Content not prepared".into()) diff --git a/crates/typing-engine/src/pace.rs b/crates/typing-engine/src/pace.rs index be77a8074..ba2191f16 100644 --- a/crates/typing-engine/src/pace.rs +++ b/crates/typing-engine/src/pace.rs @@ -1,15 +1,31 @@ use crate::Text; -use serde::Serialize; +use serde::{Deserialize, Serialize}; use std::collections::BTreeSet; use ts_rs::TS; -#[derive(Clone, Debug, Serialize, TS, PartialEq)] +#[derive(Clone, Debug, Deserialize, Serialize, TS, PartialEq)] #[serde(rename_all = "camelCase")] pub struct Position { pub word_index: usize, pub letter_index: usize, pub wpm: f64, } +#[derive(Deserialize, TS)] +#[serde(tag = "op", rename_all = "camelCase")] +pub enum PaceCommand { + Reset { wpm: Option }, + Start { now: f64 }, + Step, + Advance { now: f64 }, + Remember { wpm: f64, repeating: bool }, +} +#[derive(Debug, Deserialize, Serialize, TS, PartialEq)] +#[serde(rename_all = "camelCase")] +pub struct PaceUpdate { + pub position: Option, + pub step_end: f64, + pub last_wpm: f64, +} #[derive(Default)] pub struct Pace { wpm: Option, @@ -56,12 +72,19 @@ impl Pace { } } pub fn step(&mut self, words: &[Text], blind: bool) -> Option { + self.step_lengths(|index| words.get(index).map(Text::len), blind) + } + fn step_lengths( + &mut self, + length: impl Fn(usize) -> Option, + blind: bool, + ) -> Option { self.wpm?; - let Some(word) = words.get(self.word_index) else { + let Some(word_length) = length(self.word_index) else { self.wpm = None; return None; }; - if self.letter_index >= word.len() { + if self.letter_index >= word_length { self.letter_index = 0; self.word_index += 1; } else { @@ -70,28 +93,24 @@ impl Pace { if !blind { while self.correction < 0 { if self.letter_index == 0 { - let Some(previous) = self - .word_index - .checked_sub(1) - .and_then(|index| words.get(index)) - else { + let Some(previous) = self.word_index.checked_sub(1).and_then(&length) else { self.wpm = None; return None; }; self.word_index -= 1; - self.letter_index = previous.len(); + self.letter_index = previous; } else { self.letter_index -= 1; } self.correction += 1; } while self.correction > 0 { - let Some(word) = words.get(self.word_index) else { + let Some(word_length) = length(self.word_index) else { self.wpm = None; return None; }; self.letter_index += 1; - if self.letter_index > word.len() { + if self.letter_index > word_length { self.letter_index = 0; self.word_index += 1; } @@ -121,6 +140,60 @@ impl Pace { pub fn last_wpm(&self) -> f64 { self.last_wpm } + /// Session targets include commits; the browser pace caret uses bare text. + pub fn command( + &mut self, + command: PaceCommand, + targets: &[Text], + blind: bool, + ) -> Result { + let length = |index| { + targets + .get(index) + .map(|word: &Text| word.len() - usize::from(matches!(word.0.last(), Some(32 | 10)))) + }; + match &command { + PaceCommand::Start { now } | PaceCommand::Advance { now } => { + if !now.is_finite() || now.abs() > 9_007_199_254_740_991.0 { + return Err("Invalid pace time".into()); + } + } + PaceCommand::Reset { wpm: Some(wpm) } | PaceCommand::Remember { wpm, .. } + if !wpm.is_finite() => + { + return Err("Invalid pace speed".into()); + } + _ => {} + } + match command { + PaceCommand::Reset { wpm } => { + self.reset(wpm); + } + PaceCommand::Start { now } => self.start(now), + PaceCommand::Step => { + self.step_lengths(length, blind); + } + PaceCommand::Advance { now } => { + if let Some(wpm) = self.wpm { + let due = ((now - self.start_ms) / (60.0 / (wpm * 5.0) * 1000.0)).floor(); + if due - self.steps as f64 > 1_000_000.0 { + return Err("Pace catchup exceeds bound".into()); + } + while (self.steps as f64) < due { + if self.step_lengths(length, blind).is_none() { + break; + } + } + } + } + PaceCommand::Remember { wpm, repeating } => self.remember(wpm, repeating), + } + Ok(PaceUpdate { + position: self.position(), + step_end: self.step_end(), + last_wpm: self.last_wpm(), + }) + } } #[derive(Default)] diff --git a/crates/typing-engine/src/session.rs b/crates/typing-engine/src/session.rs index efc5f9e54..8f7390b49 100644 --- a/crates/typing-engine/src/session.rs +++ b/crates/typing-engine/src/session.rs @@ -441,6 +441,20 @@ pub struct Session { } impl Session { + pub fn pace_command( + &mut self, + command: crate::pace::PaceCommand, + ) -> Result { + self.pace + .command(command, &self.words, self.config.blind_mode) + } + pub(crate) fn into_learning(self) -> (crate::weak_spot::WeakSpot, f64) { + ( + self.generator + .map_or(self.weak_spot, |generator| generator.weak_spot), + self.pace.last_wpm(), + ) + } pub fn new(config: Config, words: Vec) -> Result { config.validate()?; if words.len() > 1_000_000 || words.iter().any(|word| word.len() > 1_000_000) { diff --git a/crates/typing-engine/src/weak_spot.rs b/crates/typing-engine/src/weak_spot.rs index bcb2b8a05..a2fc7e614 100644 --- a/crates/typing-engine/src/weak_spot.rs +++ b/crates/typing-engine/src/weak_spot.rs @@ -1,11 +1,11 @@ use crate::Text; use std::collections::BTreeMap; -#[derive(Default)] +#[derive(Clone, Default)] pub struct WeakSpot { scores: BTreeMap, Score>, } -#[derive(Default)] +#[derive(Clone, Default)] struct Score { average: f64, count: u32, diff --git a/crates/typing-engine/tests/learning.rs b/crates/typing-engine/tests/learning.rs new file mode 100644 index 000000000..41036d6cd --- /dev/null +++ b/crates/typing-engine/tests/learning.rs @@ -0,0 +1,113 @@ +use serde_json::json; +use typing_engine::{ + Session, Text, + generation::{Content, Prepare}, + pace::PaceCommand, +}; +fn input() -> Prepare { + serde_json::from_value(json!({ + "config":{"mode":"words","words":30,"funbox":["weakspot"]}, + "language":{"name":"english","words":[[97],[120]]}, + "random":{"kind":"seeded","state":1} + })) + .unwrap() +} +#[test] +fn recycling_retains_learning_and_pace_but_discards_input_and_pending_work() { + let mut owner = Content::default(); + owner.prepare(input()).unwrap(); + let mut session = owner.start_session(false).unwrap(); + for (now, data) in [(0, "a"), (100, "x")] { + session + .apply( + serde_json::from_value(json!({"op":"insert","now":now,"data":Text::from(data)})) + .unwrap(), + ) + .unwrap(); + } + session + .pace_command(PaceCommand::Remember { + wpm: 77.0, + repeating: false, + }) + .unwrap(); + session + .pace_command(PaceCommand::Remember { + wpm: 40.0, + repeating: true, + }) + .unwrap(); + owner.recycle_session(session); + owner.prepare(input()).unwrap(); + let mut next = owner.start_session(false).unwrap(); + assert_eq!( + next.pace_command(PaceCommand::Reset { wpm: None }) + .unwrap() + .last_wpm, + 77.0 + ); + assert!( + next.snapshot() + .words + .iter() + .all(|word| word.0 == [120, 32] || word.0 == [120]) + ); + assert!(next.log()["events"].as_array().unwrap().is_empty()); + assert!(!next.snapshot().state.active); + let mut isolated = Content::default(); + isolated.prepare(input()).unwrap(); + let mut fresh = isolated.start_session(false).unwrap(); + assert_eq!( + fresh + .pace_command(PaceCommand::Reset { wpm: None }) + .unwrap() + .last_wpm, + 0.0 + ); + assert!( + fresh + .snapshot() + .words + .iter() + .any(|word| word.0 == [97, 32] || word.0 == [97]) + ); +} +#[test] +fn session_pace_uses_bare_utf16_words_and_rejects_excessive_catchup_before_mutation() { + let mut session = Session::new( + Default::default(), + vec![Text::from("a "), Text::from("😀\n")], + ) + .unwrap(); + session + .pace_command(PaceCommand::Reset { wpm: Some(120.0) }) + .unwrap(); + session + .pace_command(PaceCommand::Start { now: 100.0 }) + .unwrap(); + assert_eq!( + session + .pace_command(PaceCommand::Step) + .unwrap() + .position + .unwrap() + .letter_index, + 1 + ); + let second = session.pace_command(PaceCommand::Step).unwrap(); + assert_eq!(second.position.unwrap().word_index, 1); + assert_eq!(second.step_end, 300.0); + assert!( + session + .pace_command(PaceCommand::Advance { now: f64::NAN }) + .is_err() + ); + assert!( + session + .pace_command(PaceCommand::Advance { now: 1e12 }) + .is_err() + ); + let third = session.pace_command(PaceCommand::Step).unwrap(); + assert_eq!(third.position.unwrap().letter_index, 1); + assert_eq!(third.step_end, 400.00000000000006); +} diff --git a/crates/typing-engine/tests/pace.rs b/crates/typing-engine/tests/pace.rs index feeaf4a14..6d69f823e 100644 --- a/crates/typing-engine/tests/pace.rs +++ b/crates/typing-engine/tests/pace.rs @@ -3,6 +3,20 @@ use typing_engine::{ pace::{Pace, Position, TypingTime}, }; #[test] +fn pace_commands_match_frozen_typescript_positions_and_clock_values_exactly() { + let path = std::path::Path::new(env!("CARGO_MANIFEST_DIR")) + .join("../../packages/typing-fixtures/oracle/pace.json"); + let fixture: serde_json::Value = serde_json::from_slice(&std::fs::read(path).unwrap()).unwrap(); + let words: Vec = serde_json::from_value(fixture["targets"].clone()).unwrap(); + let mut pace = Pace::default(); + for frame in fixture["frames"].as_array().unwrap() { + let command = serde_json::from_value(frame["command"].clone()).unwrap(); + let expected: typing_engine::pace::PaceUpdate = + serde_json::from_value(frame["expected"].clone()).unwrap(); + assert_eq!(pace.command(command, &words, false).unwrap(), expected); + } +} +#[test] fn pace_corrects_each_error_once_and_remembers_repeated_speed() { let words = vec![Text::from("abc"), Text::from("def"), Text::from("ghi")]; let mut pace = Pace::default(); diff --git a/docs/rust-engine/contract.md b/docs/rust-engine/contract.md index 03c9c6054..56e66bcc4 100644 --- a/docs/rust-engine/contract.md +++ b/docs/rust-engine/contract.md @@ -125,3 +125,7 @@ overwrite files. Existing snapshots are never updated by the harness. ## Observed web completion `view {deferFinish:true}` enables a finish-request/acknowledgement handshake. Rust decides completion and returns `finish` in the update; hosts disable input and stop scheduling, perform the same platform navigation/hooks as before, then send `acknowledgeFinish {now,date}`. The end event uses that explicit observation, preserving the website's later completion clock without a hidden Rust clock. New typing/ticks cannot change the decision while awaiting acknowledgement. Reset/end cancel it; stale/duplicate acknowledgement is an error. Native callers can keep immediate completion. + +## Retained learning and pace + +An app owns `Content` across test replacements. Call `recycle_session` before preparing a replacement to consume the old session and retain only weak-spot learning and remembered pace WPM. Its log, input, pending content and completion state are dropped; the consumed handle rejects subsequent calls. Independent content owners retain independent learning. Explicit disposal drops an owner entirely. Pace commands are synchronous; Rust uses bare UTF-16 word lengths for caret stepping and full targets for mistake correction. Hosts supply speed selection from account/PB data and schedule/render steps. diff --git a/docs/rust-engine/progress.md b/docs/rust-engine/progress.md index 88a6e90e2..118e7fd58 100644 --- a/docs/rust-engine/progress.md +++ b/docs/rust-engine/progress.md @@ -145,3 +145,5 @@ do not rerun completed checks without a new reason. - Cloudflare production build and combined browser migration run pass: 9 tests, 25.5 seconds. Includes all 19 traces, 66 generators, eight historical result/replay references, persistent playback deltas/sounds/seeks/disposal, completion timing, active content and actual web failure ordering. Concurrent cold initialization preserves five live handles; one failed Wasm download retries, one owner's load promise is shared, disposal rejects later loads. Type-aware lint passes. Asset: 766.89 kB raw / 297.25 kB gzip (Vite); both frozen payload limits pass. App-owned loader resource added; production website cutover remains pending. - Captured 567 additional normalization boundaries from unchanged TypeScript. They exposed and now guard Russian named-variant equivalence: strip only legacy /_\d*k$/ suffixes; preserve named variants. Dutch expansion uses the same exact language boundary. Existing 2,048 input decisions and new normalization matrix pass natively. New expectations captured once with exclusive file creation; no previous fixture changed. Browser rerun pending. + +- App-owned content can now consume/recycle a prior session, retaining weak-spot learning and remembered pace while dropping its log/input/pending work. Independent owners remain isolated. Typed Wasm pace operations added; native regressions cover learned generation after replacement, remembered repeat speed and UTF-16 bare-word stepping. All 26 newly frozen TypeScript pace frames match native positions and floating-point schedule values exactly. Native learning/input/pace suites pass; browser integration pending. diff --git a/packages/typing-core/scripts/capture-pace-oracle.ts b/packages/typing-core/scripts/capture-pace-oracle.ts new file mode 100644 index 000000000..30140a61d --- /dev/null +++ b/packages/typing-core/scripts/capture-pace-oracle.ts @@ -0,0 +1,52 @@ +import { writeFileSync } from "node:fs"; +import { resolve } from "node:path"; +import { PaceTracker } from "../src/pace-caret"; + +const words = ["one", "two", "😀"]; +type Command = + | { op: "reset"; wpm: number | null } + | { op: "start" | "advance"; now: number } + | { op: "step" } + | { op: "remember"; wpm: number; repeating: boolean }; +const commands: Command[] = [ + { op: "reset", wpm: 120 }, + { op: "start", now: 100 }, + ...Array.from({ length: 5 }, () => ({ op: "step" as const })), + { op: "advance", now: 1200 }, + { op: "advance", now: 5000 }, + { op: "remember", wpm: 77, repeating: false }, + { op: "remember", wpm: 40, repeating: true }, + { op: "remember", wpm: 90, repeating: true }, + { op: "reset", wpm: null }, + { op: "reset", wpm: 0 }, + { op: "reset", wpm: 33 }, + { op: "start", now: 6000 }, + ...Array.from({ length: 10 }, () => ({ op: "step" as const })), +]; +const pace = new PaceTracker(); +const frames = commands.map((command) => { + if (command.op === "reset") pace.reset(command.wpm); + if (command.op === "start") pace.start(command.now); + if (command.op === "step") pace.step((index) => words[index], false); + if (command.op === "advance") { + pace.advanceTo(command.now, (index) => words[index], false); + } + if (command.op === "remember") { + pace.rememberWpm(command.wpm, command.repeating); + } + return { + command, + expected: { + position: pace.getPosition(), + stepEnd: pace.getStepEnd(), + lastWpm: pace.getLastWpm(), + }, + }; +}); +const units = (text: string): number[] => + Array.from({ length: text.length }, (_, i) => text.charCodeAt(i)); +writeFileSync( + resolve(import.meta.dirname, "../../typing-fixtures/oracle/pace.json"), + `${JSON.stringify({ targets: words.map((word) => units(`${word} `)), frames }, null, 2)}\n`, + { flag: "wx" }, +); diff --git a/packages/typing-fixtures/oracle/pace.json b/packages/typing-fixtures/oracle/pace.json new file mode 100644 index 000000000..75f4fe4d3 --- /dev/null +++ b/packages/typing-fixtures/oracle/pace.json @@ -0,0 +1,363 @@ +{ + "targets": [ + [111, 110, 101, 32], + [116, 119, 111, 32], + [55357, 56832, 32] + ], + "frames": [ + { + "command": { + "op": "reset", + "wpm": 120 + }, + "expected": { + "position": { + "wordIndex": 0, + "letterIndex": 0, + "wpm": 120 + }, + "stepEnd": 0, + "lastWpm": 0 + } + }, + { + "command": { + "op": "start", + "now": 100 + }, + "expected": { + "position": { + "wordIndex": 0, + "letterIndex": 0, + "wpm": 120 + }, + "stepEnd": 100, + "lastWpm": 0 + } + }, + { + "command": { + "op": "step" + }, + "expected": { + "position": { + "wordIndex": 0, + "letterIndex": 1, + "wpm": 120 + }, + "stepEnd": 200, + "lastWpm": 0 + } + }, + { + "command": { + "op": "step" + }, + "expected": { + "position": { + "wordIndex": 0, + "letterIndex": 2, + "wpm": 120 + }, + "stepEnd": 300, + "lastWpm": 0 + } + }, + { + "command": { + "op": "step" + }, + "expected": { + "position": { + "wordIndex": 0, + "letterIndex": 3, + "wpm": 120 + }, + "stepEnd": 400.00000000000006, + "lastWpm": 0 + } + }, + { + "command": { + "op": "step" + }, + "expected": { + "position": { + "wordIndex": 1, + "letterIndex": 0, + "wpm": 120 + }, + "stepEnd": 500, + "lastWpm": 0 + } + }, + { + "command": { + "op": "step" + }, + "expected": { + "position": { + "wordIndex": 1, + "letterIndex": 1, + "wpm": 120 + }, + "stepEnd": 600, + "lastWpm": 0 + } + }, + { + "command": { + "op": "advance", + "now": 1200 + }, + "expected": { + "position": { + "wordIndex": 3, + "letterIndex": 0, + "wpm": 120 + }, + "stepEnd": 1200, + "lastWpm": 0 + } + }, + { + "command": { + "op": "advance", + "now": 5000 + }, + "expected": { + "position": null, + "stepEnd": 100, + "lastWpm": 0 + } + }, + { + "command": { + "op": "remember", + "wpm": 77, + "repeating": false + }, + "expected": { + "position": null, + "stepEnd": 100, + "lastWpm": 77 + } + }, + { + "command": { + "op": "remember", + "wpm": 40, + "repeating": true + }, + "expected": { + "position": null, + "stepEnd": 100, + "lastWpm": 77 + } + }, + { + "command": { + "op": "remember", + "wpm": 90, + "repeating": true + }, + "expected": { + "position": null, + "stepEnd": 100, + "lastWpm": 90 + } + }, + { + "command": { + "op": "reset", + "wpm": null + }, + "expected": { + "position": null, + "stepEnd": 0, + "lastWpm": 90 + } + }, + { + "command": { + "op": "reset", + "wpm": 0 + }, + "expected": { + "position": null, + "stepEnd": 0, + "lastWpm": 90 + } + }, + { + "command": { + "op": "reset", + "wpm": 33 + }, + "expected": { + "position": { + "wordIndex": 0, + "letterIndex": 0, + "wpm": 33 + }, + "stepEnd": 0, + "lastWpm": 90 + } + }, + { + "command": { + "op": "start", + "now": 6000 + }, + "expected": { + "position": { + "wordIndex": 0, + "letterIndex": 0, + "wpm": 33 + }, + "stepEnd": 6000, + "lastWpm": 90 + } + }, + { + "command": { + "op": "step" + }, + "expected": { + "position": { + "wordIndex": 0, + "letterIndex": 1, + "wpm": 33 + }, + "stepEnd": 6363.636363636364, + "lastWpm": 90 + } + }, + { + "command": { + "op": "step" + }, + "expected": { + "position": { + "wordIndex": 0, + "letterIndex": 2, + "wpm": 33 + }, + "stepEnd": 6727.272727272727, + "lastWpm": 90 + } + }, + { + "command": { + "op": "step" + }, + "expected": { + "position": { + "wordIndex": 0, + "letterIndex": 3, + "wpm": 33 + }, + "stepEnd": 7090.909090909091, + "lastWpm": 90 + } + }, + { + "command": { + "op": "step" + }, + "expected": { + "position": { + "wordIndex": 1, + "letterIndex": 0, + "wpm": 33 + }, + "stepEnd": 7454.545454545454, + "lastWpm": 90 + } + }, + { + "command": { + "op": "step" + }, + "expected": { + "position": { + "wordIndex": 1, + "letterIndex": 1, + "wpm": 33 + }, + "stepEnd": 7818.181818181818, + "lastWpm": 90 + } + }, + { + "command": { + "op": "step" + }, + "expected": { + "position": { + "wordIndex": 1, + "letterIndex": 2, + "wpm": 33 + }, + "stepEnd": 8181.818181818182, + "lastWpm": 90 + } + }, + { + "command": { + "op": "step" + }, + "expected": { + "position": { + "wordIndex": 1, + "letterIndex": 3, + "wpm": 33 + }, + "stepEnd": 8545.454545454546, + "lastWpm": 90 + } + }, + { + "command": { + "op": "step" + }, + "expected": { + "position": { + "wordIndex": 2, + "letterIndex": 0, + "wpm": 33 + }, + "stepEnd": 8909.090909090908, + "lastWpm": 90 + } + }, + { + "command": { + "op": "step" + }, + "expected": { + "position": { + "wordIndex": 2, + "letterIndex": 1, + "wpm": 33 + }, + "stepEnd": 9272.727272727272, + "lastWpm": 90 + } + }, + { + "command": { + "op": "step" + }, + "expected": { + "position": { + "wordIndex": 2, + "letterIndex": 2, + "wpm": 33 + }, + "stepEnd": 9636.363636363636, + "lastWpm": 90 + } + } + ] +} From c29ac3585916f6b3a7b82dbca201d198c53f0965 Mon Sep 17 00:00:00 2001 From: Lakshmi Tanmay Date: Fri, 23 Oct 2026 05:44:18 +0530 Subject: [PATCH 57/93] Verify retained learning, exact pace clocks and language normalization in browser Wasm --- docs/rust-engine/progress.md | 2 + frontend/e2e/engine-proof.migration.ts | 48 +++++++++ frontend/src/routes/engine-proof.tsx | 101 +++++++++++++++++++ packages/typing-engine/src/browser.client.ts | 21 ++++ 4 files changed, 172 insertions(+) diff --git a/docs/rust-engine/progress.md b/docs/rust-engine/progress.md index 118e7fd58..8d71b7f7b 100644 --- a/docs/rust-engine/progress.md +++ b/docs/rust-engine/progress.md @@ -147,3 +147,5 @@ do not rerun completed checks without a new reason. - Captured 567 additional normalization boundaries from unchanged TypeScript. They exposed and now guard Russian named-variant equivalence: strip only legacy /_\d*k$/ suffixes; preserve named variants. Dutch expansion uses the same exact language boundary. Existing 2,048 input decisions and new normalization matrix pass natively. New expectations captured once with exclusive file creation; no previous fixture changed. Browser rerun pending. - App-owned content can now consume/recycle a prior session, retaining weak-spot learning and remembered pace while dropping its log/input/pending work. Independent owners remain isolated. Typed Wasm pace operations added; native regressions cover learned generation after replacement, remembered repeat speed and UTF-16 bare-word stepping. All 26 newly frozen TypeScript pace frames match native positions and floating-point schedule values exactly. Native learning/input/pace suites pass; browser integration pending. + +- Current built Cloudflare app passes 10 migration browser tests (26.1 s), adding all 567 language-normalization boundaries, 26 pace frames, learned word generation across replacement, consumed-handle rejection and owner isolation. Type-aware lint passes. Payload: 786.00 kB raw / 302.46 kB gzip (Vite), below the unchanged 1 MiB / 300 KiB limits. diff --git a/frontend/e2e/engine-proof.migration.ts b/frontend/e2e/engine-proof.migration.ts index abb17e676..c609a7228 100644 --- a/frontend/e2e/engine-proof.migration.ts +++ b/frontend/e2e/engine-proof.migration.ts @@ -608,3 +608,51 @@ test("owned Wasm loading retries failed downloads and preserves concurrent handl }); expect(downloads).toBe(6); }); + +test("browser Wasm preserves learning, pace clocks and language normalization", async ({ + page, +}) => { + const pace = JSON.parse( + await readFile(resolve(root, "oracle/pace.json"), "utf8"), + ) as { frames: { expected: unknown }[] }; + const normalization = JSON.parse( + await readFile(resolve(root, "oracle/normalization.json"), "utf8"), + ) as { expected: number[] }[]; + const learning = { + config: { mode: "words", words: 30, funbox: ["weakspot"] }, + language: { name: "english", words: [[97], [120]] }, + random: { kind: "seeded", state: 1 }, + }; + await page.goto("/engine-proof"); + await page + .getByRole("textbox", { name: "Command fixture" }) + .fill(JSON.stringify({ pace, normalization, learning })); + await page.getByRole("button", { name: "Run context", exact: true }).click(); + await expect(page.getByTestId("engine-proof-result")).not.toBeEmpty(); + await expect(page.getByTestId("engine-proof-error")).toBeEmpty(); + const result = JSON.parse( + await page.getByTestId("engine-proof-result").innerText(), + ) as { + frames: unknown[]; + normalization: number[][]; + stale: boolean; + remembered: number; + isolated: number; + learnedWords: number[][]; + freshWords: number[][]; + emptyLog: boolean; + active: boolean; + }; + expect(result.frames).toEqual(pace.frames.map((frame) => frame.expected)); + expect(result.normalization).toEqual( + normalization.map((item) => item.expected), + ); + expect(result.stale).toBe(true); + expect(result.remembered).toBe(77); + expect(result.isolated).toBe(0); + expect(result.learnedWords).toHaveLength(30); + expect(result.learnedWords.every((word) => word[0] === 120)).toBe(true); + expect(result.freshWords.some((word) => word[0] === 97)).toBe(true); + expect(result.emptyLog).toBe(true); + expect(result.active).toBe(false); +}); diff --git a/frontend/src/routes/engine-proof.tsx b/frontend/src/routes/engine-proof.tsx index e3a3c436f..7a5e76c88 100644 --- a/frontend/src/routes/engine-proof.tsx +++ b/frontend/src/routes/engine-proof.tsx @@ -10,6 +10,7 @@ import type { ContentRequest, Update, Cursor, + PaceCommand, } from "@oxytype/typing-engine/browser.client"; import { createFileRoute } from "@tanstack/solid-router"; @@ -164,6 +165,98 @@ function EngineProof() { loaders.delete(loader); } }; + const runContext = async (): Promise => { + setResult(""); + setFailure(""); + try { + const input = JSON.parse(fixture()) as { + pace: { targets: Text[]; frames: { command: PaceCommand }[] }; + normalization: { language: string; data: Text; target: Text }[]; + learning: Prepare; + }; + const { loadEngine, encodeText } = + await import("@oxytype/typing-engine/browser.client"); + const engine = await loadEngine(); + if (disposed) return; + const pace = engine.createSession( + { mode: "words", words: 0 }, + input.pace.targets, + ); + const content = engine.createContent(); + const isolated = engine.createContent(); + const sessions: BrowserSession[] = [pace]; + try { + const frames = input.pace.frames.map(({ command }) => + pace.pace(command), + ); + const normalization = input.normalization.map((item) => { + const session = engine.createSession({ language: item.language }, [ + item.target, + ]); + try { + return session.preflight(item.data).normalized; + } finally { + session.dispose(); + } + }); + content.prepare(input.learning); + const first = content.createSession(); + sessions.push(first); + first.command({ + op: "insert", + data: encodeText("a"), + now: 0, + date: 0, + automatic: null, + isCompositionEnding: null, + correctShiftUsed: null, + }); + first.command({ + op: "insert", + data: encodeText("x"), + now: 100, + date: 100, + automatic: null, + isCompositionEnding: null, + correctShiftUsed: null, + }); + first.pace({ op: "remember", wpm: 77, repeating: false }); + first.pace({ op: "remember", wpm: 40, repeating: true }); + content.recycleSession(first); + let stale = false; + try { + first.snapshot(); + } catch { + stale = true; + } + content.prepare(input.learning); + const next = content.createSession(); + sessions.push(next); + isolated.prepare(input.learning); + const fresh = isolated.createSession(); + sessions.push(fresh); + setResult( + JSON.stringify({ + frames, + normalization, + stale, + learnedWords: next.snapshot().words, + freshWords: fresh.snapshot().words, + remembered: next.pace({ op: "reset", wpm: null }).lastWpm, + isolated: fresh.pace({ op: "reset", wpm: null }).lastWpm, + emptyLog: (next.log() as Log).events.length === 0, + active: next.snapshot().state.active, + }), + ); + } finally { + for (const session of sessions) session.dispose(); + content.dispose(); + isolated.dispose(); + } + } catch (error) { + setFailure(error instanceof Error ? error.message : String(error)); + } + }; const runGeneration = async (): Promise => { setResult(""); setFailure(""); @@ -416,6 +509,14 @@ function EngineProof() { > Run loading +
{result()}
{failure()}
{benchmark()}
diff --git a/packages/typing-engine/src/browser.client.ts b/packages/typing-engine/src/browser.client.ts index 546c08318..36d87b860 100644 --- a/packages/typing-engine/src/browser.client.ts +++ b/packages/typing-engine/src/browser.client.ts @@ -23,6 +23,8 @@ import type { Source } from "../generated/types/Source"; import type { Preflight } from "../generated/types/Preflight"; import type { InsertionLayout } from "../generated/types/InsertionLayout"; import type { Snapshot } from "../generated/types/Snapshot"; +import type { PaceCommand } from "../generated/types/PaceCommand"; +import type { PaceUpdate } from "../generated/types/PaceUpdate"; import type { PlaybackSnapshot } from "../generated/types/PlaybackSnapshot"; import type { PlaybackUpdate } from "../generated/types/PlaybackUpdate"; import type { Cursor } from "../generated/types/Cursor"; @@ -30,6 +32,8 @@ import type { Log } from "../generated/types/Log"; export type { Query, Response, + PaceCommand, + PaceUpdate, PlaybackSnapshot, PlaybackUpdate, Cursor, @@ -146,6 +150,20 @@ export class BrowserSession { } return JSON.parse(this.handle.command(JSON.stringify(command))) as Update; } + pace(command: PaceCommand): PaceUpdate { + if (this.handle === undefined) { + throw new Error("Typing engine session disposed"); + } + return JSON.parse(this.handle.pace(JSON.stringify(command))) as PaceUpdate; + } + /** @internal Ownership transfer used by the app's content resource. */ + recycleTo(content: WasmContent): void { + if (this.handle === undefined) { + throw new Error("Typing engine session disposed"); + } + content.recycle_session(this.handle); + this.dispose(); + } preflight(data: Text, layout: Partial = {}): Preflight { if (this.handle === undefined) { throw new Error("Typing engine session disposed"); @@ -229,6 +247,9 @@ export class BrowserContent { } return this.handle; } + recycleSession(session: BrowserSession): void { + session.recycleTo(this.live()); + } prepare(input: Prepare): void { this.live().prepare(JSON.stringify(input)); } From 28579559747213a3040be58aaa7d45fc32e198a6 Mon Sep 17 00:00:00 2001 From: Lakshmi Tanmay Date: Fri, 23 Oct 2026 05:45:50 +0530 Subject: [PATCH 58/93] Verify generated artifact hashes before reusing Rust and Wasm build outputs --- docs/rust-engine/progress.md | 2 + scripts/engine.ts | 94 +++++++++++++++++++++++++++++++++++- 2 files changed, 94 insertions(+), 2 deletions(-) diff --git a/docs/rust-engine/progress.md b/docs/rust-engine/progress.md index 8d71b7f7b..19fd1b984 100644 --- a/docs/rust-engine/progress.md +++ b/docs/rust-engine/progress.md @@ -149,3 +149,5 @@ do not rerun completed checks without a new reason. - App-owned content can now consume/recycle a prior session, retaining weak-spot learning and remembered pace while dropping its log/input/pending work. Independent owners remain isolated. Typed Wasm pace operations added; native regressions cover learned generation after replacement, remembered repeat speed and UTF-16 bare-word stepping. All 26 newly frozen TypeScript pace frames match native positions and floating-point schedule values exactly. Native learning/input/pace suites pass; browser integration pending. - Current built Cloudflare app passes 10 migration browser tests (26.1 s), adding all 567 language-normalization boundaries, 26 pace frames, learned word generation across replacement, consumed-handle rejection and owner isolation. Type-aware lint passes. Payload: 786.00 kB raw / 302.46 kB gzip (Vite), below the unchanged 1 MiB / 300 KiB limits. + +- Build bootstrap now fingerprints sources/tool config and verifies every generated output hash before reusing bindings. The actual Vite multi-environment build performs one Rust/Wasm build and two verified cache hits. Deleted generated Wasm experimentally: bootstrap rejected the cache and rebuilt the identical 786,005-byte artifact (SHA256 91ff5be6f2168e9c6ae699ae111fd869f23fff698e91dc028830dadd0522f0b4); the following build verified and reused it. Rebuild removes obsolete generated declarations. No fresh-checkout gate claimed yet. diff --git a/scripts/engine.ts b/scripts/engine.ts index f859d7722..26f7a5257 100644 --- a/scripts/engine.ts +++ b/scripts/engine.ts @@ -1,10 +1,80 @@ import { spawnSync } from "node:child_process"; -import { mkdirSync } from "node:fs"; -import { dirname, resolve } from "node:path"; +import { + existsSync, + mkdirSync, + readFileSync, + readdirSync, + rmSync, + renameSync, + writeFileSync, +} from "node:fs"; +import { dirname, relative, resolve } from "node:path"; import { createRequire } from "node:module"; +import { createHash } from "node:crypto"; const root = resolve(import.meta.dirname, ".."); const output = resolve(root, "packages/typing-engine/generated"); +const cacheFile = resolve(output, "build-cache.json"); +function files(directory: string): string[] { + return readdirSync(directory, { withFileTypes: true }) + .flatMap((entry) => { + const path = resolve(directory, entry.name); + return entry.isDirectory() ? files(path) : [path]; + }) + .sort(); +} +function digest(path: string): string { + return createHash("sha256").update(readFileSync(path)).digest("hex"); +} +function fingerprint(): string { + const sources = [ + "Cargo.toml", + "Cargo.lock", + "rust-toolchain.toml", + "package.json", + "scripts/engine.ts", + ].map((path) => resolve(root, path)); + for (const path of ["crates", ".cargo"]) { + const directory = resolve(root, path); + if (existsSync(directory)) sources.push(...files(directory)); + } + const environment = Object.fromEntries( + Object.entries(process.env).filter( + ([key]) => + key.startsWith("RUST") || + key.startsWith("CARGO_PROFILE_") || + key.startsWith("CARGO_BUILD_") || + key === "CARGO_ENCODED_RUSTFLAGS", + ), + ); + return createHash("sha256") + .update( + JSON.stringify({ + sources: sources + .sort() + .map((path) => [relative(root, path), digest(path)]), + environment, + platform: process.platform, + architecture: process.arch, + }), + ) + .digest("hex"); +} +type BuildCache = { fingerprint: string; outputs: [string, string][] }; +function cached(key: string): boolean { + try { + const cache = JSON.parse(readFileSync(cacheFile, "utf8")) as BuildCache; + return ( + cache.fingerprint === key && + cache.outputs.length > 0 && + cache.outputs.every( + ([path, hash]) => digest(resolve(output, path)) === hash, + ) + ); + } catch { + return false; + } +} function run(command: string, args: string[]): void { const result = spawnSync(command, args, { cwd: root, stdio: "inherit" }); if (result.error !== undefined) throw result.error; @@ -13,6 +83,13 @@ function run(command: string, args: string[]): void { switch (process.argv[2]) { case "build": { + const key = fingerprint(); + if (!process.argv.includes("--force") && cached(key)) { + process.stdout.write( + "Typing engine bindings verified; build cache hit\n", + ); + break; + } const bindingVersion = "0.2.129"; const installed = spawnSync("wasm-bindgen", ["--version"], { encoding: "utf8", @@ -36,6 +113,7 @@ switch (process.argv[2]) { "-p", "typing-engine-wasm", ]); + rmSync(output, { recursive: true, force: true }); mkdirSync(output, { recursive: true }); run("wasm-bindgen", [ "--target", @@ -74,6 +152,18 @@ switch (process.argv[2]) { "--bin", "export-bindings", ]); + const cache: BuildCache = { + fingerprint: key, + outputs: files(output) + .filter( + (path) => + path !== cacheFile && !path.includes("build-cache.json.tmp-"), + ) + .map((path) => [relative(output, path), digest(path)]), + }; + const temporary = `${cacheFile}.tmp-${process.pid}`; + writeFileSync(temporary, JSON.stringify(cache)); + renameSync(temporary, cacheFile); break; } case "test": From 244acf0be3bb97c0fb5ec15dc3d89a60af66170b Mon Sep 17 00:00:00 2001 From: Lakshmi Tanmay Date: Fri, 23 Oct 2026 05:48:41 +0530 Subject: [PATCH 59/93] Preserve repeated wordsets and quote queues across owned session replacement --- crates/typing-engine-wasm/src/lib.rs | 4 +- crates/typing-engine/src/generation.rs | 39 +++++++++++++++-- crates/typing-engine/src/session.rs | 22 +++++++--- crates/typing-engine/tests/learning.rs | 60 ++++++++++++++++++++++++++ docs/rust-engine/progress.md | 2 + 5 files changed, 116 insertions(+), 11 deletions(-) diff --git a/crates/typing-engine-wasm/src/lib.rs b/crates/typing-engine-wasm/src/lib.rs index aad1e981c..a276af779 100644 --- a/crates/typing-engine-wasm/src/lib.rs +++ b/crates/typing-engine-wasm/src/lib.rs @@ -116,12 +116,12 @@ impl WasmContent { content: Some(typing_engine::generation::Content::default()), }) } - pub fn prepare(&mut self, input: &str) -> Result<(), JsError> { + pub fn prepare(&mut self, input: &str, repeating: bool) -> Result<(), JsError> { let input = serde_json::from_str(input)?; self.content .as_mut() .ok_or_else(|| JsError::new("Content disposed"))? - .prepare(input) + .prepare_repeating(input, repeating) .map_err(|message| JsError::new(&message)) } pub fn generate(&mut self, repeating: bool) -> Result { diff --git a/crates/typing-engine/src/generation.rs b/crates/typing-engine/src/generation.rs index 76920a520..574de5dc5 100644 --- a/crates/typing-engine/src/generation.rs +++ b/crates/typing-engine/src/generation.rs @@ -74,7 +74,7 @@ pub struct Generator { section: VecDeque, section_index: usize, section_history: Vec, - previous: Vec, + pub(crate) previous: Vec, repeating: bool, generated: usize, punctuation: crate::punctuation::Punctuation, @@ -163,11 +163,21 @@ impl Generator { Self::with_assets(config, language, custom, random, &mut Assets::default()) } pub fn with_assets( + config: GenerationConfig, + language: Language, + custom: Option, + random: Random, + assets: &mut Assets, + ) -> Result { + Self::with_repeated_quote(config, language, custom, random, assets, None) + } + fn with_repeated_quote( config: GenerationConfig, mut language: Language, custom: Option, mut random: Random, assets: &mut Assets, + repeated_quote: Option, ) -> Result { config.validate()?; let section_queue = if let Some(name) = config.funbox.iter().find(|name| { @@ -197,7 +207,9 @@ impl Generator { { return Err("Invalid custom mode or limit".into()); } - let current_quote = if config.mode == "quote" { + let current_quote = if repeated_quote.is_some() && config.mode == "quote" { + repeated_quote + } else if config.mode == "quote" { if let Some(asset) = assets.quote_asset.take() { assets .quotes @@ -809,29 +821,48 @@ pub struct Content { generator: Option, weak_spot: crate::weak_spot::WeakSpot, last_pace_wpm: f64, + previous: Vec, + previous_quote: Option, } impl Content { /// Consume the prior session before preparing its replacement. Only learning /// survives; logs, pending replies and input state are dropped with the session. pub fn recycle_session(&mut self, session: crate::Session) { - (self.weak_spot, self.last_pace_wpm) = session.into_learning(); + ( + self.weak_spot, + self.last_pace_wpm, + self.previous, + self.previous_quote, + ) = session.into_learning(); self.generator = None; } pub fn prepare(&mut self, input: Prepare) -> Result<(), String> { + self.prepare_repeating(input, false) + } + pub fn prepare_repeating(&mut self, input: Prepare, repeating: bool) -> Result<(), String> { + if repeating && input.config.mode == "quote" && self.previous_quote.is_none() { + return Err("Missing previous quote".into()); + } self.generator = None; self.assets.quote_asset = input.quotes; self.assets.polyglot = input.polyglot; self.assets.selected_quote_id = input.selected_quote_id; self.assets.favorite_quote_ids = input.favorite_quote_ids; self.assets.sections = input.sections; - self.generator = Some(Generator::with_assets( + self.generator = Some(Generator::with_repeated_quote( input.config, input.language, input.custom, input.random, &mut self.assets, + if repeating { + self.previous_quote.clone() + } else { + None + }, )?); self.generator.as_mut().unwrap().weak_spot = self.weak_spot.clone(); + self.generator.as_mut().unwrap().previous = self.previous.clone(); Ok(()) } pub fn start_session(&mut self, repeating: bool) -> Result { diff --git a/crates/typing-engine/src/session.rs b/crates/typing-engine/src/session.rs index 8f7390b49..209679216 100644 --- a/crates/typing-engine/src/session.rs +++ b/crates/typing-engine/src/session.rs @@ -448,12 +448,24 @@ impl Session { self.pace .command(command, &self.words, self.config.blind_mode) } - pub(crate) fn into_learning(self) -> (crate::weak_spot::WeakSpot, f64) { - ( + pub(crate) fn into_learning( + self, + ) -> ( + crate::weak_spot::WeakSpot, + f64, + Vec, + Option, + ) { + let (learner, previous, quote) = self.generator - .map_or(self.weak_spot, |generator| generator.weak_spot), - self.pace.last_wpm(), - ) + .map_or((self.weak_spot, vec![], None), |generator| { + ( + generator.weak_spot, + generator.previous, + generator.current_quote, + ) + }); + (learner, self.pace.last_wpm(), previous, quote) } pub fn new(config: Config, words: Vec) -> Result { config.validate()?; diff --git a/crates/typing-engine/tests/learning.rs b/crates/typing-engine/tests/learning.rs index 41036d6cd..b8d5644d7 100644 --- a/crates/typing-engine/tests/learning.rs +++ b/crates/typing-engine/tests/learning.rs @@ -111,3 +111,63 @@ fn session_pace_uses_bare_utf16_words_and_rejects_excessive_catchup_before_mutat assert_eq!(third.position.unwrap().letter_index, 1); assert_eq!(third.step_end, 400.00000000000006); } + +#[test] +fn repeated_session_keeps_prepared_words_after_recycling_with_a_different_seed() { + let mut content = Content::default(); + content.prepare(input()).unwrap(); + let session = content.start_session(false).unwrap(); + let original = session.snapshot().words; + content.recycle_session(session); + let mut preparation = input(); + preparation.random = typing_engine::random::Random::seeded(99); + content.prepare_repeating(preparation, true).unwrap(); + let repeated = content.start_session(true).unwrap(); + assert_eq!(repeated.snapshot().words, original); + content.recycle_session(repeated); + content.prepare(input()).unwrap(); + assert_eq!( + content.start_session(false).unwrap().snapshot().words, + original + ); +} + +#[test] +fn repeated_quote_keeps_identity_and_does_not_consume_the_next_queue_entry() { + fn quote_input(with_asset: bool) -> Prepare { + let mut value = json!({ + "config":{"mode":"quote","quoteLength":[0]}, + "language":{"name":"english","words":[[97]]}, + "random":{"kind":"seeded","state":1} + }); + if with_asset { + value["quotes"] = json!({"language":"english","groups":[[0,1000]],"quotes":[ + {"text":Text::from("one two three"),"id":1,"length":13,"source":"fixture"}, + {"text":Text::from("four five six"),"id":2,"length":13,"source":"fixture"} + ]}); + } + serde_json::from_value(value).unwrap() + } + let mut content = Content::default(); + content.prepare(quote_input(true)).unwrap(); + let first = content.start_session(false).unwrap(); + let quote = first.snapshot().current_quote.unwrap().id; + let words = first.snapshot().words; + content.recycle_session(first); + content.prepare_repeating(quote_input(false), true).unwrap(); + let repeated = content.start_session(true).unwrap(); + assert_eq!(repeated.snapshot().current_quote.unwrap().id, quote); + assert_eq!(repeated.snapshot().words, words); + content.recycle_session(repeated); + content.prepare(quote_input(false)).unwrap(); + assert_ne!( + content + .start_session(false) + .unwrap() + .snapshot() + .current_quote + .unwrap() + .id, + quote + ); +} diff --git a/docs/rust-engine/progress.md b/docs/rust-engine/progress.md index 19fd1b984..867a9a08f 100644 --- a/docs/rust-engine/progress.md +++ b/docs/rust-engine/progress.md @@ -151,3 +151,5 @@ do not rerun completed checks without a new reason. - Current built Cloudflare app passes 10 migration browser tests (26.1 s), adding all 567 language-normalization boundaries, 26 pace frames, learned word generation across replacement, consumed-handle rejection and owner isolation. Type-aware lint passes. Payload: 786.00 kB raw / 302.46 kB gzip (Vite), below the unchanged 1 MiB / 300 KiB limits. - Build bootstrap now fingerprints sources/tool config and verifies every generated output hash before reusing bindings. The actual Vite multi-environment build performs one Rust/Wasm build and two verified cache hits. Deleted generated Wasm experimentally: bootstrap rejected the cache and rebuilt the identical 786,005-byte artifact (SHA256 91ff5be6f2168e9c6ae699ae111fd869f23fff698e91dc028830dadd0522f0b4); the following build verified and reused it. Rebuild removes obsolete generated declarations. No fresh-checkout gate claimed yet. + +- Recycling now retains prior generated words and quote identity for explicit repeats. Repeating preparation reuses the previous quote without drawing/advancing its selection queue. Native regressions cover changed seeds, wordset reuse and the next unconsumed quote after repeat; four learning/repeat tests and Clippy pass. Browser adapter signature changed; regeneration and built-browser repeat checks pending. A further lifecycle audit identified request-generation reuse across newly prepared sessions; resolving before the engine gate. From 8530c05488f82e98770d1ba00f6825d987d7798a Mon Sep 17 00:00:00 2001 From: Lakshmi Tanmay Date: Fri, 23 Oct 2026 09:11:14 +0530 Subject: [PATCH 60/93] Invalidate old request generations across preparation and recycled session resets --- crates/typing-engine/src/content.rs | 4 ++ crates/typing-engine/src/generation.rs | 29 ++++++-- crates/typing-engine/src/session.rs | 25 ++++--- crates/typing-engine/tests/content.rs | 98 ++++++++++++++++++++++++++ docs/rust-engine/contract.md | 2 + docs/rust-engine/progress.md | 2 + 6 files changed, 145 insertions(+), 15 deletions(-) diff --git a/crates/typing-engine/src/content.rs b/crates/typing-engine/src/content.rs index f3cbd28fd..50338011f 100644 --- a/crates/typing-engine/src/content.rs +++ b/crates/typing-engine/src/content.rs @@ -224,4 +224,8 @@ impl Queue { self.pending = None; self.ready.clear(); } + pub(crate) fn set_generation(&mut self, generation: u32) { + self.generation = generation; + self.pending = None; + } } diff --git a/crates/typing-engine/src/generation.rs b/crates/typing-engine/src/generation.rs index 574de5dc5..65edad593 100644 --- a/crates/typing-engine/src/generation.rs +++ b/crates/typing-engine/src/generation.rs @@ -87,6 +87,7 @@ pub struct Generator { word_languages: std::collections::BTreeMap, usize>, section_queue: Option, batch: Option, + pub(crate) prepared_generation: u32, } pub(crate) fn case(text: &Text, upper: bool) -> Text { let mut output = vec![]; @@ -299,6 +300,7 @@ impl Generator { word_languages, section_queue, batch: None, + prepared_generation: 0, }) } fn limit(&self) -> usize { @@ -823,17 +825,20 @@ pub struct Content { last_pace_wpm: f64, previous: Vec, previous_quote: Option, + generation: u32, + has_prepared: bool, } impl Content { /// Consume the prior session before preparing its replacement. Only learning /// survives; logs, pending replies and input state are dropped with the session. pub fn recycle_session(&mut self, session: crate::Session) { - ( - self.weak_spot, - self.last_pace_wpm, - self.previous, - self.previous_quote, - ) = session.into_learning(); + let memory = session.into_learning(); + self.weak_spot = memory.weak_spot; + self.last_pace_wpm = memory.last_pace_wpm; + self.previous = memory.previous; + self.previous_quote = memory.quote; + self.generation = self.generation.max(memory.generation); + self.has_prepared = true; self.generator = None; } pub fn prepare(&mut self, input: Prepare) -> Result<(), String> { @@ -843,6 +848,13 @@ impl Content { if repeating && input.config.mode == "quote" && self.previous_quote.is_none() { return Err("Missing previous quote".into()); } + if self.has_prepared { + self.generation = self + .generation + .checked_add(1) + .ok_or("Content generation exhausted")?; + } + self.has_prepared = true; self.generator = None; self.assets.quote_asset = input.quotes; self.assets.polyglot = input.polyglot; @@ -863,6 +875,11 @@ impl Content { )?); self.generator.as_mut().unwrap().weak_spot = self.weak_spot.clone(); self.generator.as_mut().unwrap().previous = self.previous.clone(); + let generator = self.generator.as_mut().unwrap(); + generator.prepared_generation = self.generation; + if let Some(queue) = &mut generator.section_queue { + queue.set_generation(self.generation); + } Ok(()) } pub fn start_session(&mut self, repeating: bool) -> Result { diff --git a/crates/typing-engine/src/session.rs b/crates/typing-engine/src/session.rs index 209679216..119b6dc2b 100644 --- a/crates/typing-engine/src/session.rs +++ b/crates/typing-engine/src/session.rs @@ -397,6 +397,13 @@ struct DeferredInput { composition: bool, final_in_batch: bool, } +pub(crate) struct Retained { + pub weak_spot: crate::weak_spot::WeakSpot, + pub last_pace_wpm: f64, + pub previous: Vec, + pub quote: Option, + pub generation: u32, +} pub struct Session { pub pace: crate::pace::Pace, pub weak_spot: crate::weak_spot::WeakSpot, @@ -448,14 +455,7 @@ impl Session { self.pace .command(command, &self.words, self.config.blind_mode) } - pub(crate) fn into_learning( - self, - ) -> ( - crate::weak_spot::WeakSpot, - f64, - Vec, - Option, - ) { + pub(crate) fn into_learning(self) -> Retained { let (learner, previous, quote) = self.generator .map_or((self.weak_spot, vec![], None), |generator| { @@ -465,7 +465,13 @@ impl Session { generator.current_quote, ) }); - (learner, self.pace.last_wpm(), previous, quote) + Retained { + weak_spot: learner, + last_pace_wpm: self.pace.last_wpm(), + previous, + quote, + generation: self.generation, + } } pub fn new(config: Config, words: Vec) -> Result { config.validate()?; @@ -534,6 +540,7 @@ impl Session { generated: crate::generation::Generated, ) -> Result { let mut session = Self::new(generator.config().clone(), generated.words)?; + session.generation = generator.prepared_generation; session.sections = generated.section_indexes; session.has_tab = generated.has_tab; session.has_newline = generated.has_newline; diff --git a/crates/typing-engine/tests/content.rs b/crates/typing-engine/tests/content.rs index 76ec59234..62af20089 100644 --- a/crates/typing-engine/tests/content.rs +++ b/crates/typing-engine/tests/content.rs @@ -27,6 +27,104 @@ fn poem(word: &str) -> Source { lines: vec![Text::from(word)], } } +fn source_preparation(sections: Vec) -> typing_engine::generation::Prepare { + serde_json::from_value(serde_json::json!({ + "config":{"mode":"time","time":0,"funbox":["poetry"]}, + "language":{"name":"english","words":[[98,97,115,101]]}, + "random":{"kind":"seeded","state":1}, + "sections":sections + })) + .unwrap() +} +#[test] +fn replacing_initial_preparation_cannot_reuse_an_old_pending_request_token() { + let mut content = typing_engine::generation::Content::default(); + content.prepare(source_preparation(vec![])).unwrap(); + assert!(content.start_session(false).is_err()); + let old = content + .generator() + .unwrap() + .pending_request() + .unwrap() + .clone(); + content.prepare(source_preparation(vec![])).unwrap(); + assert!(content.start_session(false).is_err()); + let current = content + .generator() + .unwrap() + .pending_request() + .unwrap() + .clone(); + assert_eq!(old.id, current.id); + assert_eq!(old.generation + 1, current.generation); + assert!( + content + .generator() + .unwrap() + .supply(&old, poem("stale")) + .is_err() + ); + assert_eq!( + content.generator().unwrap().pending_request(), + Some(¤t) + ); + content + .generator() + .unwrap() + .supply(¤t, poem(&vec!["one"; 100].join(" "))) + .unwrap(); + let mut session = content.start_session(false).unwrap(); + assert_eq!( + session + .apply(serde_json::from_value(serde_json::json!({"op":"view","now":0})).unwrap()) + .unwrap() + .generation, + current.generation + ); +} +#[test] +fn recycling_after_session_resets_advances_automatic_and_content_generations() { + let mut content = typing_engine::generation::Content::default(); + let sources = || vec![poem(&vec!["one"; 100].join(" "))]; + content.prepare(source_preparation(sources())).unwrap(); + let mut first = content.start_session(false).unwrap(); + let command = |value| serde_json::from_value(value).unwrap(); + first + .apply(command(serde_json::json!({"op":"reset","now":0}))) + .unwrap(); + first + .apply(command(serde_json::json!({"op":"reset","now":0}))) + .unwrap(); + let old = first + .apply(command( + serde_json::json!({"op":"insert","data":Text::from("one "),"now":0}), + )) + .unwrap() + .content_request + .unwrap(); + assert_eq!(old.generation, 2); + content.recycle_session(first); + content.prepare(source_preparation(sources())).unwrap(); + let mut second = content.start_session(false).unwrap(); + let update = second + .apply(command( + serde_json::json!({"op":"insert","data":Text::from("one "),"now":0}), + )) + .unwrap(); + let current = update.content_request.unwrap(); + assert_eq!(update.generation, 3); + assert_eq!(current.generation, 3); + let log = second.log(); + assert!(second.apply(command(serde_json::json!({"op":"supplyContent","request":old,"source":poem("stale"),"now":10}))).is_err()); + assert_eq!(second.log(), log); + assert_eq!( + second + .apply(command(serde_json::json!({"op":"view","now":10}))) + .unwrap() + .content_request, + Some(current) + ); +} #[test] fn section_requests_are_owned_bounded_and_cancelled_by_generation() { let mut queue = Queue::new("poetry".into(), "english".into(), true); diff --git a/docs/rust-engine/contract.md b/docs/rust-engine/contract.md index 56e66bcc4..2b9737657 100644 --- a/docs/rust-engine/contract.md +++ b/docs/rust-engine/contract.md @@ -129,3 +129,5 @@ overwrite files. Existing snapshots are never updated by the harness. ## Retained learning and pace An app owns `Content` across test replacements. Call `recycle_session` before preparing a replacement to consume the old session and retain only weak-spot learning and remembered pace WPM. Its log, input, pending content and completion state are dropped; the consumed handle rejects subsequent calls. Independent content owners retain independent learning. Explicit disposal drops an owner entirely. Pace commands are synchronous; Rust uses bare UTF-16 word lengths for caret stepping and full targets for mistake correction. Hosts supply speed selection from account/PB data and schedule/render steps. + +Content preparation advances an owner-local generation. Recycling takes the maximum of the owner's generation and the consumed session's reset generation; the next preparation advances it. Old content/automatic tokens cannot match a replacement session. Owner instances route their own replies. Explicit repeated preparation retains generated words and quote identity; quote repetition does not advance the selection queue. diff --git a/docs/rust-engine/progress.md b/docs/rust-engine/progress.md index 867a9a08f..b2aab6a36 100644 --- a/docs/rust-engine/progress.md +++ b/docs/rust-engine/progress.md @@ -153,3 +153,5 @@ do not rerun completed checks without a new reason. - Build bootstrap now fingerprints sources/tool config and verifies every generated output hash before reusing bindings. The actual Vite multi-environment build performs one Rust/Wasm build and two verified cache hits. Deleted generated Wasm experimentally: bootstrap rejected the cache and rebuilt the identical 786,005-byte artifact (SHA256 91ff5be6f2168e9c6ae699ae111fd869f23fff698e91dc028830dadd0522f0b4); the following build verified and reused it. Rebuild removes obsolete generated declarations. No fresh-checkout gate claimed yet. - Recycling now retains prior generated words and quote identity for explicit repeats. Repeating preparation reuses the previous quote without drawing/advancing its selection queue. Native regressions cover changed seeds, wordset reuse and the next unconsumed quote after repeat; four learning/repeat tests and Clippy pass. Browser adapter signature changed; regeneration and built-browser repeat checks pending. A further lifecycle audit identified request-generation reuse across newly prepared sessions; resolving before the engine gate. + +- Preparation generations now advance across replacement, including prior session resets. Prefetched sources survive assigning the new generation; pending tokens do not. Native tests reject an old initial-preparation token and an active token from a recycled/reset session without changing the new log or pending request. Ten content tests and Clippy pass. Matching generations also protect deferred automatic input. Browser replacement/repeat checks pending. From a22cb5c7a6b168cb9e7fe02a601e9a4fb635605e Mon Sep 17 00:00:00 2001 From: Lakshmi Tanmay Date: Fri, 23 Oct 2026 09:15:11 +0530 Subject: [PATCH 61/93] Prove browser repeats preserve targets, quote identity and cancelled request ownership --- docs/rust-engine/progress.md | 2 + frontend/e2e/engine-proof.migration.ts | 72 ++++++++++++ frontend/src/routes/engine-proof.tsx | 112 +++++++++++++++++++ packages/typing-engine/src/browser.client.ts | 4 +- 4 files changed, 188 insertions(+), 2 deletions(-) diff --git a/docs/rust-engine/progress.md b/docs/rust-engine/progress.md index b2aab6a36..650602a13 100644 --- a/docs/rust-engine/progress.md +++ b/docs/rust-engine/progress.md @@ -155,3 +155,5 @@ do not rerun completed checks without a new reason. - Recycling now retains prior generated words and quote identity for explicit repeats. Repeating preparation reuses the previous quote without drawing/advancing its selection queue. Native regressions cover changed seeds, wordset reuse and the next unconsumed quote after repeat; four learning/repeat tests and Clippy pass. Browser adapter signature changed; regeneration and built-browser repeat checks pending. A further lifecycle audit identified request-generation reuse across newly prepared sessions; resolving before the engine gate. - Preparation generations now advance across replacement, including prior session resets. Prefetched sources survive assigning the new generation; pending tokens do not. Native tests reject an old initial-preparation token and an active token from a recycled/reset session without changing the new log or pending request. Ten content tests and Clippy pass. Matching generations also protect deferred automatic input. Browser replacement/repeat checks pending. + +- Built Cloudflare migration run now passes 11 tests (26.9 s): replacement sessions preserve repeated words/quote identity, quote repetition leaves the next queue entry untouched, and replaced source tokens are rejected. Asset: 787.06 kB raw / 303.14 kB gzip (Vite), within frozen limits. Restart-policy port is subsequent work and not part of this browser evidence. diff --git a/frontend/e2e/engine-proof.migration.ts b/frontend/e2e/engine-proof.migration.ts index c609a7228..0b6f9b126 100644 --- a/frontend/e2e/engine-proof.migration.ts +++ b/frontend/e2e/engine-proof.migration.ts @@ -656,3 +656,75 @@ test("browser Wasm preserves learning, pace clocks and language normalization", expect(result.emptyLog).toBe(true); expect(result.active).toBe(false); }); + +test("replacement Wasm sessions repeat exact targets and reject old source replies", async ({ + page, +}) => { + const units = (text: string): number[] => + Array.from({ length: text.length }, (_, i) => text.charCodeAt(i)); + const language = { + name: "english", + words: ["one", "two", "three", "four", "five", "six"].map(units), + }; + const random = { kind: "seeded", state: 1 }; + const input = { + words: { config: { mode: "words", words: 30 }, language, random }, + quote: { + config: { mode: "quote", quoteLength: [0] }, + language, + random, + quotes: { + language: "english", + groups: [[0, 1000]], + quotes: [ + { + id: 1, + text: units("one two three"), + length: 13, + source: "fixture", + }, + { + id: 2, + text: units("four five six"), + length: 13, + source: "fixture", + }, + ], + }, + }, + sections: { + config: { mode: "time", time: 0, funbox: ["poetry"] }, + language, + random, + }, + }; + await page.goto("/engine-proof"); + await page + .getByRole("textbox", { name: "Command fixture" }) + .fill(JSON.stringify(input)); + await page.getByRole("button", { name: "Run restarts", exact: true }).click(); + await expect(page.getByTestId("engine-proof-result")).not.toBeEmpty(); + await expect(page.getByTestId("engine-proof-error")).toBeEmpty(); + const actual = JSON.parse( + await page.getByTestId("engine-proof-result").innerText(), + ) as { + before: number[][]; + after: number[][]; + quoteBefore: { words: number[][]; quote: { id: number } }; + quoteAfter: { words: number[][]; quote: { id: number } }; + nextQuote: { id: number }; + old: { id: number; generation: number }; + current: { id: number; generation: number }; + staleRejected: boolean; + pending: unknown; + }; + expect(actual.before).toHaveLength(30); + expect(actual.after).toEqual(actual.before); + expect(actual.quoteAfter).toEqual(actual.quoteBefore); + expect(actual.nextQuote.id).not.toBe(actual.quoteBefore.quote.id); + expect(actual.old.id).toBe(actual.current.id); + expect(actual.old.generation).toBe(0); + expect(actual.current.generation).toBe(1); + expect(actual.staleRejected).toBe(true); + expect(actual.pending).toEqual(actual.current); +}); diff --git a/frontend/src/routes/engine-proof.tsx b/frontend/src/routes/engine-proof.tsx index 7a5e76c88..8f642545d 100644 --- a/frontend/src/routes/engine-proof.tsx +++ b/frontend/src/routes/engine-proof.tsx @@ -257,6 +257,110 @@ function EngineProof() { setFailure(error instanceof Error ? error.message : String(error)); } }; + const runRestarts = async (): Promise => { + setResult(""); + setFailure(""); + try { + const input = JSON.parse(fixture()) as { + words: Prepare; + quote: Prepare; + sections: Prepare; + }; + const { loadEngine } = + await import("@oxytype/typing-engine/browser.client"); + const engine = await loadEngine(); + if (disposed) return; + const owners = Array.from({ length: 3 }, () => engine.createContent()); + const sessions: BrowserSession[] = []; + const [words, quote, sections] = owners; + if ( + words === undefined || + quote === undefined || + sections === undefined + ) { + throw new Error("Missing owner"); + } + try { + words.prepare(input.words); + const first = words.createSession(); + sessions.push(first); + const before = first.snapshot().words; + words.recycleSession(first); + words.prepare( + { ...input.words, random: { kind: "seeded", state: 99 } }, + true, + ); + const repeated = words.createSession(true); + sessions.push(repeated); + const after = repeated.snapshot().words; + quote.prepare(input.quote); + const firstQuote = quote.createSession(); + sessions.push(firstQuote); + const quoteBefore = firstQuote.snapshot(); + quote.recycleSession(firstQuote); + quote.prepare({ ...input.quote, quotes: null }, true); + const repeatQuote = quote.createSession(true); + sessions.push(repeatQuote); + const quoteAfter = repeatQuote.snapshot(); + quote.recycleSession(repeatQuote); + quote.prepare({ ...input.quote, quotes: null }); + const nextQuote = quote.createSession(); + sessions.push(nextQuote); + sections.prepare(input.sections); + try { + sections.createSession(); + } catch { + /* Expected typed source request. */ + } + const old = sections.request(); + sections.prepare(input.sections); + try { + sections.createSession(); + } catch { + /* Expected replacement request. */ + } + const current = sections.request(); + if (old === null || current === null) { + throw new Error("Missing request"); + } + let staleRejected = false; + try { + sections.supply(old, { + kind: "poem", + title: [], + author: [], + lines: [[111, 110, 101]], + }); + } catch { + staleRejected = true; + } + setResult( + JSON.stringify({ + before, + after, + quoteBefore: { + words: quoteBefore.words, + quote: quoteBefore.currentQuote, + }, + quoteAfter: { + words: quoteAfter.words, + quote: quoteAfter.currentQuote, + }, + nextQuote: nextQuote.snapshot().currentQuote, + old, + current, + staleRejected, + pending: sections.request(), + }), + ); + } finally { + for (const session of sessions) session.dispose(); + for (const owner of owners) owner.dispose(); + } + } catch (error) { + setFailure(error instanceof Error ? error.message : String(error)); + } + }; const runGeneration = async (): Promise => { setResult(""); setFailure(""); @@ -517,6 +621,14 @@ function EngineProof() { > Run context +
{result()}
{failure()}
{benchmark()}
diff --git a/packages/typing-engine/src/browser.client.ts b/packages/typing-engine/src/browser.client.ts index 36d87b860..ba8d32b12 100644 --- a/packages/typing-engine/src/browser.client.ts +++ b/packages/typing-engine/src/browser.client.ts @@ -250,8 +250,8 @@ export class BrowserContent { recycleSession(session: BrowserSession): void { session.recycleTo(this.live()); } - prepare(input: Prepare): void { - this.live().prepare(JSON.stringify(input)); + prepare(input: Prepare, repeating = false): void { + this.live().prepare(JSON.stringify(input), repeating); } generate(repeating = false): Generated { return JSON.parse(this.live().generate(repeating)) as Generated; From c9d1e0c93b94375b022b45e0e1bb396c42b4f166 Mon Sep 17 00:00:00 2001 From: Lakshmi Tanmay Date: Fri, 23 Oct 2026 09:20:38 +0530 Subject: [PATCH 62/93] Centralize restart eligibility, repeat policy and long custom-text progress in Rust --- crates/typing-engine-wasm/src/lib.rs | 8 + crates/typing-engine/src/analysis.rs | 9 + .../typing-engine/src/bin/export-bindings.rs | 2 + crates/typing-engine/src/custom_progress.rs | 48 + crates/typing-engine/src/generation.rs | 3 + crates/typing-engine/src/lib.rs | 2 + crates/typing-engine/src/restart.rs | 81 + crates/typing-engine/src/session.rs | 30 + crates/typing-engine/tests/custom_progress.rs | 64 + crates/typing-engine/tests/restart.rs | 158 ++ docs/rust-engine/progress.md | 3 + .../scripts/capture-restart-oracle.ts | 40 + packages/typing-fixtures/oracle/restart.json | 2036 +++++++++++++++++ 13 files changed, 2484 insertions(+) create mode 100644 crates/typing-engine/src/custom_progress.rs create mode 100644 crates/typing-engine/src/restart.rs create mode 100644 crates/typing-engine/tests/custom_progress.rs create mode 100644 crates/typing-engine/tests/restart.rs create mode 100644 packages/typing-core/scripts/capture-restart-oracle.ts create mode 100644 packages/typing-fixtures/oracle/restart.json diff --git a/crates/typing-engine-wasm/src/lib.rs b/crates/typing-engine-wasm/src/lib.rs index a276af779..f8188e663 100644 --- a/crates/typing-engine-wasm/src/lib.rs +++ b/crates/typing-engine-wasm/src/lib.rs @@ -58,6 +58,14 @@ impl WasmSession { .map_err(|message| JsError::new(&message))?, )?) } + pub fn restart_policy(&self, options: &str) -> Result { + let options = serde_json::from_str(options)?; + let session = self + .session + .as_ref() + .ok_or_else(|| JsError::new("Session disposed"))?; + Ok(serde_json::to_string(&session.restart_policy(options))?) + } pub fn snapshot(&self) -> Result { let session = self .session diff --git a/crates/typing-engine/src/analysis.rs b/crates/typing-engine/src/analysis.rs index f7369edc7..f6bf69883 100644 --- a/crates/typing-engine/src/analysis.rs +++ b/crates/typing-engine/src/analysis.rs @@ -23,6 +23,9 @@ pub enum Query { Frame { time: f64, }, + CustomProgress { + previous: u32, + }, Practice { words: Vec, missed: String, @@ -51,6 +54,9 @@ pub enum Response { Frame { frame: replay::Frame, }, + CustomProgress { + progress: crate::custom_progress::LongProgress, + }, Practice { selection: practice::Selection, }, @@ -108,6 +114,9 @@ pub fn analyze(log: &Log, query: Query) -> Response { Query::Frame { time } => Response::Frame { frame: replay::frame(log, time), }, + Query::CustomProgress { previous } => Response::CustomProgress { + progress: crate::custom_progress::calculate(log, previous), + }, Query::Practice { words, missed, diff --git a/crates/typing-engine/src/bin/export-bindings.rs b/crates/typing-engine/src/bin/export-bindings.rs index 0643ffcc1..3bd7b4ec3 100644 --- a/crates/typing-engine/src/bin/export-bindings.rs +++ b/crates/typing-engine/src/bin/export-bindings.rs @@ -34,6 +34,8 @@ fn main() -> Result<(), Box> { typing_engine::replay::PlaybackUpdate::export_all(&config)?; typing_engine::pace::PaceCommand::export_all(&config)?; typing_engine::pace::PaceUpdate::export_all(&config)?; + typing_engine::restart::RestartOptions::export_all(&config)?; + typing_engine::restart::RestartDecision::export_all(&config)?; typing_engine::generation::Prepare::export_all(&config)?; typing_engine::generation::Generated::export_all(&config)?; typing_engine::generation::NextWord::export_all(&config)?; diff --git a/crates/typing-engine/src/custom_progress.rs b/crates/typing-engine/src/custom_progress.rs new file mode 100644 index 000000000..96d9bc422 --- /dev/null +++ b/crates/typing-engine/src/custom_progress.rs @@ -0,0 +1,48 @@ +use crate::{ + Text, + stats::{self, Log}, +}; +use serde::Serialize; +use ts_rs::TS; + +#[derive(Debug, PartialEq, Eq, Serialize, TS)] +pub struct LongProgress { + pub completed: bool, + // u32 prior progress + bounded word count stays exactly representable in JS. + #[ts(type = "number")] + pub progress: u64, +} +/// Matches completion.client.ts: newline commits belong to display length; +/// space commits do not. Hosts retain the book text and persist this result. +pub fn from_history( + previous: u32, + bailed_out: bool, + history: &[Text], + targets: &[Text], +) -> LongProgress { + if !bailed_out && history.len() >= targets.len() { + return LongProgress { + completed: true, + progress: 0, + }; + } + let incomplete = history.last().is_some_and(|typed| { + let target = targets.get(history.len() - 1); + let display_length = target.map_or(0, |word| { + word.len() - usize::from(word.0.last() == Some(&32)) + }); + typed.len() < display_length + }); + LongProgress { + completed: false, + progress: u64::from(previous) + (history.len() - usize::from(incomplete)) as u64, + } +} +pub fn calculate(log: &Log, previous: u32) -> LongProgress { + from_history( + previous, + log.context.bailed_out, + &stats::input_history(log), + &log.context.target_words, + ) +} diff --git a/crates/typing-engine/src/generation.rs b/crates/typing-engine/src/generation.rs index 65edad593..68d9440aa 100644 --- a/crates/typing-engine/src/generation.rs +++ b/crates/typing-engine/src/generation.rs @@ -149,6 +149,9 @@ impl Generator { pub fn config(&self) -> &GenerationConfig { &self.config } + pub fn is_repeating(&self) -> bool { + self.repeating + } pub fn custom_limit(&self) -> Option { self.custom.as_ref().map(|custom| crate::results::Limit { mode: custom.limit.mode.clone(), diff --git a/crates/typing-engine/src/lib.rs b/crates/typing-engine/src/lib.rs index 45aaa544d..52aabbd34 100644 --- a/crates/typing-engine/src/lib.rs +++ b/crates/typing-engine/src/lib.rs @@ -1,6 +1,7 @@ //! Platform-independent typing rules. Hosts provide content, observations and time. pub mod analysis; pub mod content; +pub mod custom_progress; pub mod funboxes; mod generated_funboxes; pub mod generation; @@ -12,6 +13,7 @@ pub mod punctuation; pub mod quotes; pub mod random; pub mod replay; +pub mod restart; pub mod results; pub mod session; pub mod stats; diff --git a/crates/typing-engine/src/restart.rs b/crates/typing-engine/src/restart.rs new file mode 100644 index 000000000..fbefa7601 --- /dev/null +++ b/crates/typing-engine/src/restart.rs @@ -0,0 +1,81 @@ +//! Typing restart decisions; hosts retain loading/animation/navigation guards. +use crate::{Config, results::Limit}; +use serde::{Deserialize, Serialize}; +use ts_rs::TS; + +#[derive(Default, Deserialize, TS)] +#[serde(default, rename_all = "camelCase")] +pub struct RestartOptions { + pub quick: bool, + pub with_same_wordset: bool, + pub custom_is_long: bool, +} +pub struct RestartState<'a> { + pub active: bool, + pub failed: bool, + pub repeated: bool, + pub quote_language: Option<&'a str>, +} +#[derive(Debug, PartialEq, Eq, Serialize, TS)] +#[serde(rename_all = "camelCase")] +pub enum RestartBlock { + NoQuit, + LongTest, +} +#[derive(Debug, Serialize, TS)] +#[serde(rename_all = "camelCase")] +pub struct RestartDecision { + pub allowed: bool, + pub reason: Option, + pub with_same_wordset: bool, + pub record_incomplete: bool, + pub prevent_default: bool, + pub repeat_with_pace: bool, +} +pub fn can_quick_restart(config: &Config, limit: Option<&Limit>, custom_is_long: bool) -> bool { + let words_long = config.mode == "words" && (config.words >= 1000 || config.words == 0); + let time_long = config.mode == "time" && (config.time >= 900.0 || config.time == 0.0); + let custom_long = config.mode == "custom" + && (custom_is_long + || limit.is_some_and(|limit| { + ((limit.mode == "word" || limit.mode == "section") + && (limit.value >= 1000.0 || limit.value == 0.0)) + || (limit.mode == "time" && (limit.value >= 900.0 || limit.value == 0.0)) + })); + !(words_long || time_long || custom_long) +} +pub fn decide( + config: &Config, + state: RestartState<'_>, + limit: Option<&Limit>, + options: RestartOptions, +) -> RestartDecision { + let reason = if state.active && config.funbox.iter().any(|name| name == "no_quit") { + Some(RestartBlock::NoQuit) + } else if state.active + && options.quick + && !can_quick_restart(config, limit, options.custom_is_long) + { + Some(RestartBlock::LongTest) + } else { + None + }; + let prevent_default = reason == Some(RestartBlock::NoQuit) + || (state.active && options.quick && config.mode != "zen"); + let same = options.with_same_wordset + || (state.active && state.repeated) + || (config.mode == "quote" + && config.repeat_quotes == "typing" + && state + .quote_language + .is_some_and(|language| config.language.starts_with(language)) + && (state.active || state.failed)); + RestartDecision { + allowed: reason.is_none(), + with_same_wordset: reason.is_none() && same, + record_incomplete: reason.is_none() && state.active && config.result_saving, + repeat_with_pace: reason.is_none() && config.repeated_pace && same, + prevent_default, + reason, + } +} diff --git a/crates/typing-engine/src/session.rs b/crates/typing-engine/src/session.rs index 119b6dc2b..73d566aff 100644 --- a/crates/typing-engine/src/session.rs +++ b/crates/typing-engine/src/session.rs @@ -19,6 +19,9 @@ pub struct Config { pub stop_on_error: String, pub delete_on_error: String, pub quick_end: bool, + pub repeat_quotes: String, + pub result_saving: bool, + pub repeated_pace: bool, pub freedom_mode: bool, pub confidence_mode: String, pub blind_mode: bool, @@ -52,6 +55,9 @@ impl Default for Config { stop_on_error: "off".into(), delete_on_error: "off".into(), quick_end: false, + repeat_quotes: "off".into(), + result_saving: true, + repeated_pace: true, freedom_mode: false, confidence_mode: "off".into(), blind_mode: false, @@ -115,6 +121,11 @@ impl Config { ), ("minWpm", self.min_wpm.as_str(), &["off", "custom"][..]), ("minAcc", self.min_acc.as_str(), &["off", "custom"][..]), + ( + "repeatQuotes", + self.repeat_quotes.as_str(), + &["off", "typing"][..], + ), ] { if !allowed.contains(&value) { return Err(format!("Invalid {name}")); @@ -448,6 +459,25 @@ pub struct Session { } impl Session { + pub fn restart_policy( + &self, + options: crate::restart::RestartOptions, + ) -> crate::restart::RestartDecision { + let state = crate::restart::RestartState { + active: self.active, + failed: self.reason.is_some(), + repeated: self + .generator + .as_ref() + .is_some_and(|generator| generator.is_repeating()), + quote_language: self + .generator + .as_ref() + .and_then(|generator| generator.current_quote.as_ref()) + .map(|quote| quote.language.as_str()), + }; + crate::restart::decide(&self.config, state, self.custom_limit.as_ref(), options) + } pub fn pace_command( &mut self, command: crate::pace::PaceCommand, diff --git a/crates/typing-engine/tests/custom_progress.rs b/crates/typing-engine/tests/custom_progress.rs new file mode 100644 index 000000000..4f307e0ae --- /dev/null +++ b/crates/typing-engine/tests/custom_progress.rs @@ -0,0 +1,64 @@ +use typing_engine::{ + Text, + custom_progress::{LongProgress, from_history}, +}; +#[test] +fn long_progress_preserves_space_newline_and_utf16_display_boundaries() { + for (target, typed, increment) in [ + ("one ", "on", 0), + ("one ", "one", 1), + ("one\n", "one", 0), + ("one\n", "one\n", 1), + ("😀 ", "😀", 1), + ("\t ", "\t", 1), + ] { + let targets = vec![Text::from(target), Text::from("later")]; + let history = vec![Text::from(typed)]; + assert_eq!( + from_history(30, false, &history, &targets), + LongProgress { + completed: false, + progress: 30 + increment + } + ); + } + assert_eq!( + from_history(0, true, &[Text(vec![0xd800])], &[Text(vec![0xd800, 32])]), + LongProgress { + completed: false, + progress: 1 + } + ); +} +#[test] +fn completion_resets_progress_and_bailout_saves_only_complete_words() { + let targets = vec![Text::from("one "), Text::from("two")]; + let history = vec![Text::from("one "), Text::from("t")]; + // Preserve the existing completion rule: a full-length history resets even + // if its final snapshot is partial; an explicit bailout saves progress. + assert_eq!( + from_history(10, false, &history, &targets), + LongProgress { + completed: true, + progress: 0 + } + ); + assert_eq!( + from_history(10, true, &history, &targets), + LongProgress { + completed: false, + progress: 11 + } + ); + assert_eq!( + from_history(10, true, &[], &targets), + LongProgress { + completed: false, + progress: 10 + } + ); + assert_eq!( + from_history(u32::MAX, true, &[Text::from("one")], &targets).progress, + 4_294_967_296 + ); +} diff --git a/crates/typing-engine/tests/restart.rs b/crates/typing-engine/tests/restart.rs new file mode 100644 index 000000000..c04f46262 --- /dev/null +++ b/crates/typing-engine/tests/restart.rs @@ -0,0 +1,158 @@ +use serde_json::json; +use typing_engine::{ + Config, Session, Text, + restart::{self, RestartBlock, RestartOptions, RestartState}, +}; +#[test] +fn long_test_thresholds_match_frozen_typescript_at_every_boundary() { + let path = std::path::Path::new(env!("CARGO_MANIFEST_DIR")) + .join("../../packages/typing-fixtures/oracle/restart.json"); + let reference: serde_json::Value = + serde_json::from_slice(&std::fs::read(path).unwrap()).unwrap(); + for case in reference["quick"].as_array().unwrap() { + let config: Config = serde_json::from_value(case.clone()).unwrap(); + let limit = serde_json::from_value(case["limit"].clone()).unwrap(); + assert_eq!( + restart::can_quick_restart( + &config, + Some(&limit), + case["customIsLong"].as_bool().unwrap() + ), + case["expected"].as_bool().unwrap(), + "{case}" + ); + } + for case in reference["quotes"].as_array().unwrap() { + let config: Config = serde_json::from_value(case.clone()).unwrap(); + let decision = restart::decide( + &config, + RestartState { + active: true, + failed: false, + repeated: false, + quote_language: Some("english"), + }, + None, + RestartOptions::default(), + ); + assert_eq!( + decision.with_same_wordset, + case["expected"].as_bool().unwrap(), + "{case}" + ); + } +} +fn apply(session: &mut Session, value: serde_json::Value) { + session + .apply(serde_json::from_value(value).unwrap()) + .unwrap(); +} +#[test] +fn no_quit_and_long_test_rejections_leave_active_sessions_unchanged() { + let mut session = Session::new( + Config { + mode: "words".into(), + words: 1000, + funbox: vec!["no_quit".into()], + ..Default::default() + }, + vec![Text::from("a ")], + ) + .unwrap(); + assert!(session.restart_policy(Default::default()).allowed); + apply(&mut session, json!({"op":"insert","data":[97],"now":0})); + let log = session.log(); + let blocked = session.restart_policy(RestartOptions { + quick: true, + ..Default::default() + }); + assert_eq!(blocked.reason, Some(RestartBlock::NoQuit)); + assert!(blocked.prevent_default); + assert!(!blocked.record_incomplete); + apply( + &mut session, + json!({"op":"configure","now":0,"config":{"mode":"words","words":1000}}), + ); + assert_eq!( + session + .restart_policy(RestartOptions { + quick: true, + ..Default::default() + }) + .reason, + Some(RestartBlock::LongTest) + ); + let ordinary = session.restart_policy(Default::default()); + assert!(ordinary.allowed && ordinary.record_incomplete); + assert!(!ordinary.prevent_default); + assert_eq!(session.log(), log); +} +#[test] +fn quote_repetition_requires_matching_language_and_active_or_failed_state() { + let config = Config { + mode: "quote".into(), + language: "english_1k".into(), + repeat_quotes: "typing".into(), + ..Default::default() + }; + for (active, failed, language, expected) in [ + (true, false, "english", true), + (false, true, "english", true), + (false, false, "english", false), + (true, false, "spanish", false), + ] { + let decision = restart::decide( + &config, + RestartState { + active, + failed, + repeated: false, + quote_language: Some(language), + }, + None, + Default::default(), + ); + assert_eq!(decision.with_same_wordset, expected); + assert_eq!(decision.repeat_with_pace, expected); + assert_eq!(decision.record_incomplete, active); + } +} +#[test] +fn active_repeated_tests_and_explicit_repeat_options_preserve_the_wordset() { + for saving in [false, true] { + let config = Config { + result_saving: saving, + repeated_pace: false, + ..Default::default() + }; + let decision = restart::decide( + &config, + RestartState { + active: true, + failed: false, + repeated: true, + quote_language: None, + }, + None, + Default::default(), + ); + assert!(decision.with_same_wordset); + assert_eq!(decision.record_incomplete, saving); + assert!(!decision.repeat_with_pace); + let mut zen = Session::new( + Config { + mode: "zen".into(), + ..Default::default() + }, + vec![], + ) + .unwrap(); + apply(&mut zen, json!({"op":"insert","data":[97],"now":0})); + let decision = zen.restart_policy(RestartOptions { + quick: true, + with_same_wordset: true, + ..Default::default() + }); + assert!(decision.allowed && decision.with_same_wordset && !decision.prevent_default); + } +} diff --git a/docs/rust-engine/progress.md b/docs/rust-engine/progress.md index 650602a13..ba356bbdd 100644 --- a/docs/rust-engine/progress.md +++ b/docs/rust-engine/progress.md @@ -157,3 +157,6 @@ do not rerun completed checks without a new reason. - Preparation generations now advance across replacement, including prior session resets. Prefetched sources survive assigning the new generation; pending tokens do not. Native tests reject an old initial-preparation token and an active token from a recycled/reset session without changing the new log or pending request. Ten content tests and Clippy pass. Matching generations also protect deferred automatic input. Browser replacement/repeat checks pending. - Built Cloudflare migration run now passes 11 tests (26.9 s): replacement sessions preserve repeated words/quote identity, quote repetition leaves the next queue entry untouched, and replaced source tokens are rejected. Asset: 787.06 kB raw / 303.14 kB gzip (Vite), within frozen limits. Restart-policy port is subsequent work and not part of this browser evidence. + +- Restart policy now derives no-quit blocking, long/infinite quick-restart limits, repeated targets, quote language/failure conditions, incomplete recording and repeated pace from the Rust session/config. Native checks cover 180 newly frozen quick-restart boundaries and 10 quote-repeat preferences, plus active/inactive/failed guards. Four policy tests pass; TypeScript oracle/integrity/defaults suite passes 23 tests; Clippy passes. Hosts retain loading/result/animation guards and key selection. +- Ported long custom-text progress from completion.client.ts: UTF-16 display lengths, space/newline distinction, partial-last-word bailout and completed-book reset. Two native regressions pass, including isolated surrogates and prior progress at u32::MAX; browser/API coverage pending. diff --git a/packages/typing-core/scripts/capture-restart-oracle.ts b/packages/typing-core/scripts/capture-restart-oracle.ts new file mode 100644 index 000000000..ca24e4135 --- /dev/null +++ b/packages/typing-core/scripts/capture-restart-oracle.ts @@ -0,0 +1,40 @@ +import { writeFileSync } from "node:fs"; +import { resolve } from "node:path"; +import { canQuickRestart } from "../src/quick-restart"; +import { shouldRepeatQuote } from "../src/restart"; +import { defaultCustomTextSettings } from "../src/custom-text"; + +const quick = []; +const quotes = []; +for (const mode of ["time", "words", "quote", "custom", "zen"] as const) { + for (const value of [0, 1, 899, 900, 999, 1000]) { + for (const limitMode of ["word", "section", "time"] as const) { + for (const customIsLong of [false, true]) { + const custom = { + ...defaultCustomTextSettings, + limit: { mode: limitMode, value }, + }; + quick.push({ + mode, + words: value, + time: value, + limit: custom.limit, + customIsLong, + expected: canQuickRestart(mode, value, value, custom, customIsLong), + }); + } + } + } + for (const repeatQuotes of ["off", "typing"] as const) { + quotes.push({ + mode, + repeatQuotes, + expected: shouldRepeatQuote({ mode, repeatQuotes }), + }); + } +} +writeFileSync( + resolve(import.meta.dirname, "../../typing-fixtures/oracle/restart.json"), + `${JSON.stringify({ quick, quotes }, null, 2)}\n`, + { flag: "wx" }, +); diff --git a/packages/typing-fixtures/oracle/restart.json b/packages/typing-fixtures/oracle/restart.json new file mode 100644 index 000000000..7ec3ce3bb --- /dev/null +++ b/packages/typing-fixtures/oracle/restart.json @@ -0,0 +1,2036 @@ +{ + "quick": [ + { + "mode": "time", + "words": 0, + "time": 0, + "limit": { + "mode": "word", + "value": 0 + }, + "customIsLong": false, + "expected": false + }, + { + "mode": "time", + "words": 0, + "time": 0, + "limit": { + "mode": "word", + "value": 0 + }, + "customIsLong": true, + "expected": false + }, + { + "mode": "time", + "words": 0, + "time": 0, + "limit": { + "mode": "section", + "value": 0 + }, + "customIsLong": false, + "expected": false + }, + { + "mode": "time", + "words": 0, + "time": 0, + "limit": { + "mode": "section", + "value": 0 + }, + "customIsLong": true, + "expected": false + }, + { + "mode": "time", + "words": 0, + "time": 0, + "limit": { + "mode": "time", + "value": 0 + }, + "customIsLong": false, + "expected": false + }, + { + "mode": "time", + "words": 0, + "time": 0, + "limit": { + "mode": "time", + "value": 0 + }, + "customIsLong": true, + "expected": false + }, + { + "mode": "time", + "words": 1, + "time": 1, + "limit": { + "mode": "word", + "value": 1 + }, + "customIsLong": false, + "expected": true + }, + { + "mode": "time", + "words": 1, + "time": 1, + "limit": { + "mode": "word", + "value": 1 + }, + "customIsLong": true, + "expected": true + }, + { + "mode": "time", + "words": 1, + "time": 1, + "limit": { + "mode": "section", + "value": 1 + }, + "customIsLong": false, + "expected": true + }, + { + "mode": "time", + "words": 1, + "time": 1, + "limit": { + "mode": "section", + "value": 1 + }, + "customIsLong": true, + "expected": true + }, + { + "mode": "time", + "words": 1, + "time": 1, + "limit": { + "mode": "time", + "value": 1 + }, + "customIsLong": false, + "expected": true + }, + { + "mode": "time", + "words": 1, + "time": 1, + "limit": { + "mode": "time", + "value": 1 + }, + "customIsLong": true, + "expected": true + }, + { + "mode": "time", + "words": 899, + "time": 899, + "limit": { + "mode": "word", + "value": 899 + }, + "customIsLong": false, + "expected": true + }, + { + "mode": "time", + "words": 899, + "time": 899, + "limit": { + "mode": "word", + "value": 899 + }, + "customIsLong": true, + "expected": true + }, + { + "mode": "time", + "words": 899, + "time": 899, + "limit": { + "mode": "section", + "value": 899 + }, + "customIsLong": false, + "expected": true + }, + { + "mode": "time", + "words": 899, + "time": 899, + "limit": { + "mode": "section", + "value": 899 + }, + "customIsLong": true, + "expected": true + }, + { + "mode": "time", + "words": 899, + "time": 899, + "limit": { + "mode": "time", + "value": 899 + }, + "customIsLong": false, + "expected": true + }, + { + "mode": "time", + "words": 899, + "time": 899, + "limit": { + "mode": "time", + "value": 899 + }, + "customIsLong": true, + "expected": true + }, + { + "mode": "time", + "words": 900, + "time": 900, + "limit": { + "mode": "word", + "value": 900 + }, + "customIsLong": false, + "expected": false + }, + { + "mode": "time", + "words": 900, + "time": 900, + "limit": { + "mode": "word", + "value": 900 + }, + "customIsLong": true, + "expected": false + }, + { + "mode": "time", + "words": 900, + "time": 900, + "limit": { + "mode": "section", + "value": 900 + }, + "customIsLong": false, + "expected": false + }, + { + "mode": "time", + "words": 900, + "time": 900, + "limit": { + "mode": "section", + "value": 900 + }, + "customIsLong": true, + "expected": false + }, + { + "mode": "time", + "words": 900, + "time": 900, + "limit": { + "mode": "time", + "value": 900 + }, + "customIsLong": false, + "expected": false + }, + { + "mode": "time", + "words": 900, + "time": 900, + "limit": { + "mode": "time", + "value": 900 + }, + "customIsLong": true, + "expected": false + }, + { + "mode": "time", + "words": 999, + "time": 999, + "limit": { + "mode": "word", + "value": 999 + }, + "customIsLong": false, + "expected": false + }, + { + "mode": "time", + "words": 999, + "time": 999, + "limit": { + "mode": "word", + "value": 999 + }, + "customIsLong": true, + "expected": false + }, + { + "mode": "time", + "words": 999, + "time": 999, + "limit": { + "mode": "section", + "value": 999 + }, + "customIsLong": false, + "expected": false + }, + { + "mode": "time", + "words": 999, + "time": 999, + "limit": { + "mode": "section", + "value": 999 + }, + "customIsLong": true, + "expected": false + }, + { + "mode": "time", + "words": 999, + "time": 999, + "limit": { + "mode": "time", + "value": 999 + }, + "customIsLong": false, + "expected": false + }, + { + "mode": "time", + "words": 999, + "time": 999, + "limit": { + "mode": "time", + "value": 999 + }, + "customIsLong": true, + "expected": false + }, + { + "mode": "time", + "words": 1000, + "time": 1000, + "limit": { + "mode": "word", + "value": 1000 + }, + "customIsLong": false, + "expected": false + }, + { + "mode": "time", + "words": 1000, + "time": 1000, + "limit": { + "mode": "word", + "value": 1000 + }, + "customIsLong": true, + "expected": false + }, + { + "mode": "time", + "words": 1000, + "time": 1000, + "limit": { + "mode": "section", + "value": 1000 + }, + "customIsLong": false, + "expected": false + }, + { + "mode": "time", + "words": 1000, + "time": 1000, + "limit": { + "mode": "section", + "value": 1000 + }, + "customIsLong": true, + "expected": false + }, + { + "mode": "time", + "words": 1000, + "time": 1000, + "limit": { + "mode": "time", + "value": 1000 + }, + "customIsLong": false, + "expected": false + }, + { + "mode": "time", + "words": 1000, + "time": 1000, + "limit": { + "mode": "time", + "value": 1000 + }, + "customIsLong": true, + "expected": false + }, + { + "mode": "words", + "words": 0, + "time": 0, + "limit": { + "mode": "word", + "value": 0 + }, + "customIsLong": false, + "expected": false + }, + { + "mode": "words", + "words": 0, + "time": 0, + "limit": { + "mode": "word", + "value": 0 + }, + "customIsLong": true, + "expected": false + }, + { + "mode": "words", + "words": 0, + "time": 0, + "limit": { + "mode": "section", + "value": 0 + }, + "customIsLong": false, + "expected": false + }, + { + "mode": "words", + "words": 0, + "time": 0, + "limit": { + "mode": "section", + "value": 0 + }, + "customIsLong": true, + "expected": false + }, + { + "mode": "words", + "words": 0, + "time": 0, + "limit": { + "mode": "time", + "value": 0 + }, + "customIsLong": false, + "expected": false + }, + { + "mode": "words", + "words": 0, + "time": 0, + "limit": { + "mode": "time", + "value": 0 + }, + "customIsLong": true, + "expected": false + }, + { + "mode": "words", + "words": 1, + "time": 1, + "limit": { + "mode": "word", + "value": 1 + }, + "customIsLong": false, + "expected": true + }, + { + "mode": "words", + "words": 1, + "time": 1, + "limit": { + "mode": "word", + "value": 1 + }, + "customIsLong": true, + "expected": true + }, + { + "mode": "words", + "words": 1, + "time": 1, + "limit": { + "mode": "section", + "value": 1 + }, + "customIsLong": false, + "expected": true + }, + { + "mode": "words", + "words": 1, + "time": 1, + "limit": { + "mode": "section", + "value": 1 + }, + "customIsLong": true, + "expected": true + }, + { + "mode": "words", + "words": 1, + "time": 1, + "limit": { + "mode": "time", + "value": 1 + }, + "customIsLong": false, + "expected": true + }, + { + "mode": "words", + "words": 1, + "time": 1, + "limit": { + "mode": "time", + "value": 1 + }, + "customIsLong": true, + "expected": true + }, + { + "mode": "words", + "words": 899, + "time": 899, + "limit": { + "mode": "word", + "value": 899 + }, + "customIsLong": false, + "expected": true + }, + { + "mode": "words", + "words": 899, + "time": 899, + "limit": { + "mode": "word", + "value": 899 + }, + "customIsLong": true, + "expected": true + }, + { + "mode": "words", + "words": 899, + "time": 899, + "limit": { + "mode": "section", + "value": 899 + }, + "customIsLong": false, + "expected": true + }, + { + "mode": "words", + "words": 899, + "time": 899, + "limit": { + "mode": "section", + "value": 899 + }, + "customIsLong": true, + "expected": true + }, + { + "mode": "words", + "words": 899, + "time": 899, + "limit": { + "mode": "time", + "value": 899 + }, + "customIsLong": false, + "expected": true + }, + { + "mode": "words", + "words": 899, + "time": 899, + "limit": { + "mode": "time", + "value": 899 + }, + "customIsLong": true, + "expected": true + }, + { + "mode": "words", + "words": 900, + "time": 900, + "limit": { + "mode": "word", + "value": 900 + }, + "customIsLong": false, + "expected": true + }, + { + "mode": "words", + "words": 900, + "time": 900, + "limit": { + "mode": "word", + "value": 900 + }, + "customIsLong": true, + "expected": true + }, + { + "mode": "words", + "words": 900, + "time": 900, + "limit": { + "mode": "section", + "value": 900 + }, + "customIsLong": false, + "expected": true + }, + { + "mode": "words", + "words": 900, + "time": 900, + "limit": { + "mode": "section", + "value": 900 + }, + "customIsLong": true, + "expected": true + }, + { + "mode": "words", + "words": 900, + "time": 900, + "limit": { + "mode": "time", + "value": 900 + }, + "customIsLong": false, + "expected": true + }, + { + "mode": "words", + "words": 900, + "time": 900, + "limit": { + "mode": "time", + "value": 900 + }, + "customIsLong": true, + "expected": true + }, + { + "mode": "words", + "words": 999, + "time": 999, + "limit": { + "mode": "word", + "value": 999 + }, + "customIsLong": false, + "expected": true + }, + { + "mode": "words", + "words": 999, + "time": 999, + "limit": { + "mode": "word", + "value": 999 + }, + "customIsLong": true, + "expected": true + }, + { + "mode": "words", + "words": 999, + "time": 999, + "limit": { + "mode": "section", + "value": 999 + }, + "customIsLong": false, + "expected": true + }, + { + "mode": "words", + "words": 999, + "time": 999, + "limit": { + "mode": "section", + "value": 999 + }, + "customIsLong": true, + "expected": true + }, + { + "mode": "words", + "words": 999, + "time": 999, + "limit": { + "mode": "time", + "value": 999 + }, + "customIsLong": false, + "expected": true + }, + { + "mode": "words", + "words": 999, + "time": 999, + "limit": { + "mode": "time", + "value": 999 + }, + "customIsLong": true, + "expected": true + }, + { + "mode": "words", + "words": 1000, + "time": 1000, + "limit": { + "mode": "word", + "value": 1000 + }, + "customIsLong": false, + "expected": false + }, + { + "mode": "words", + "words": 1000, + "time": 1000, + "limit": { + "mode": "word", + "value": 1000 + }, + "customIsLong": true, + "expected": false + }, + { + "mode": "words", + "words": 1000, + "time": 1000, + "limit": { + "mode": "section", + "value": 1000 + }, + "customIsLong": false, + "expected": false + }, + { + "mode": "words", + "words": 1000, + "time": 1000, + "limit": { + "mode": "section", + "value": 1000 + }, + "customIsLong": true, + "expected": false + }, + { + "mode": "words", + "words": 1000, + "time": 1000, + "limit": { + "mode": "time", + "value": 1000 + }, + "customIsLong": false, + "expected": false + }, + { + "mode": "words", + "words": 1000, + "time": 1000, + "limit": { + "mode": "time", + "value": 1000 + }, + "customIsLong": true, + "expected": false + }, + { + "mode": "quote", + "words": 0, + "time": 0, + "limit": { + "mode": "word", + "value": 0 + }, + "customIsLong": false, + "expected": true + }, + { + "mode": "quote", + "words": 0, + "time": 0, + "limit": { + "mode": "word", + "value": 0 + }, + "customIsLong": true, + "expected": true + }, + { + "mode": "quote", + "words": 0, + "time": 0, + "limit": { + "mode": "section", + "value": 0 + }, + "customIsLong": false, + "expected": true + }, + { + "mode": "quote", + "words": 0, + "time": 0, + "limit": { + "mode": "section", + "value": 0 + }, + "customIsLong": true, + "expected": true + }, + { + "mode": "quote", + "words": 0, + "time": 0, + "limit": { + "mode": "time", + "value": 0 + }, + "customIsLong": false, + "expected": true + }, + { + "mode": "quote", + "words": 0, + "time": 0, + "limit": { + "mode": "time", + "value": 0 + }, + "customIsLong": true, + "expected": true + }, + { + "mode": "quote", + "words": 1, + "time": 1, + "limit": { + "mode": "word", + "value": 1 + }, + "customIsLong": false, + "expected": true + }, + { + "mode": "quote", + "words": 1, + "time": 1, + "limit": { + "mode": "word", + "value": 1 + }, + "customIsLong": true, + "expected": true + }, + { + "mode": "quote", + "words": 1, + "time": 1, + "limit": { + "mode": "section", + "value": 1 + }, + "customIsLong": false, + "expected": true + }, + { + "mode": "quote", + "words": 1, + "time": 1, + "limit": { + "mode": "section", + "value": 1 + }, + "customIsLong": true, + "expected": true + }, + { + "mode": "quote", + "words": 1, + "time": 1, + "limit": { + "mode": "time", + "value": 1 + }, + "customIsLong": false, + "expected": true + }, + { + "mode": "quote", + "words": 1, + "time": 1, + "limit": { + "mode": "time", + "value": 1 + }, + "customIsLong": true, + "expected": true + }, + { + "mode": "quote", + "words": 899, + "time": 899, + "limit": { + "mode": "word", + "value": 899 + }, + "customIsLong": false, + "expected": true + }, + { + "mode": "quote", + "words": 899, + "time": 899, + "limit": { + "mode": "word", + "value": 899 + }, + "customIsLong": true, + "expected": true + }, + { + "mode": "quote", + "words": 899, + "time": 899, + "limit": { + "mode": "section", + "value": 899 + }, + "customIsLong": false, + "expected": true + }, + { + "mode": "quote", + "words": 899, + "time": 899, + "limit": { + "mode": "section", + "value": 899 + }, + "customIsLong": true, + "expected": true + }, + { + "mode": "quote", + "words": 899, + "time": 899, + "limit": { + "mode": "time", + "value": 899 + }, + "customIsLong": false, + "expected": true + }, + { + "mode": "quote", + "words": 899, + "time": 899, + "limit": { + "mode": "time", + "value": 899 + }, + "customIsLong": true, + "expected": true + }, + { + "mode": "quote", + "words": 900, + "time": 900, + "limit": { + "mode": "word", + "value": 900 + }, + "customIsLong": false, + "expected": true + }, + { + "mode": "quote", + "words": 900, + "time": 900, + "limit": { + "mode": "word", + "value": 900 + }, + "customIsLong": true, + "expected": true + }, + { + "mode": "quote", + "words": 900, + "time": 900, + "limit": { + "mode": "section", + "value": 900 + }, + "customIsLong": false, + "expected": true + }, + { + "mode": "quote", + "words": 900, + "time": 900, + "limit": { + "mode": "section", + "value": 900 + }, + "customIsLong": true, + "expected": true + }, + { + "mode": "quote", + "words": 900, + "time": 900, + "limit": { + "mode": "time", + "value": 900 + }, + "customIsLong": false, + "expected": true + }, + { + "mode": "quote", + "words": 900, + "time": 900, + "limit": { + "mode": "time", + "value": 900 + }, + "customIsLong": true, + "expected": true + }, + { + "mode": "quote", + "words": 999, + "time": 999, + "limit": { + "mode": "word", + "value": 999 + }, + "customIsLong": false, + "expected": true + }, + { + "mode": "quote", + "words": 999, + "time": 999, + "limit": { + "mode": "word", + "value": 999 + }, + "customIsLong": true, + "expected": true + }, + { + "mode": "quote", + "words": 999, + "time": 999, + "limit": { + "mode": "section", + "value": 999 + }, + "customIsLong": false, + "expected": true + }, + { + "mode": "quote", + "words": 999, + "time": 999, + "limit": { + "mode": "section", + "value": 999 + }, + "customIsLong": true, + "expected": true + }, + { + "mode": "quote", + "words": 999, + "time": 999, + "limit": { + "mode": "time", + "value": 999 + }, + "customIsLong": false, + "expected": true + }, + { + "mode": "quote", + "words": 999, + "time": 999, + "limit": { + "mode": "time", + "value": 999 + }, + "customIsLong": true, + "expected": true + }, + { + "mode": "quote", + "words": 1000, + "time": 1000, + "limit": { + "mode": "word", + "value": 1000 + }, + "customIsLong": false, + "expected": true + }, + { + "mode": "quote", + "words": 1000, + "time": 1000, + "limit": { + "mode": "word", + "value": 1000 + }, + "customIsLong": true, + "expected": true + }, + { + "mode": "quote", + "words": 1000, + "time": 1000, + "limit": { + "mode": "section", + "value": 1000 + }, + "customIsLong": false, + "expected": true + }, + { + "mode": "quote", + "words": 1000, + "time": 1000, + "limit": { + "mode": "section", + "value": 1000 + }, + "customIsLong": true, + "expected": true + }, + { + "mode": "quote", + "words": 1000, + "time": 1000, + "limit": { + "mode": "time", + "value": 1000 + }, + "customIsLong": false, + "expected": true + }, + { + "mode": "quote", + "words": 1000, + "time": 1000, + "limit": { + "mode": "time", + "value": 1000 + }, + "customIsLong": true, + "expected": true + }, + { + "mode": "custom", + "words": 0, + "time": 0, + "limit": { + "mode": "word", + "value": 0 + }, + "customIsLong": false, + "expected": false + }, + { + "mode": "custom", + "words": 0, + "time": 0, + "limit": { + "mode": "word", + "value": 0 + }, + "customIsLong": true, + "expected": false + }, + { + "mode": "custom", + "words": 0, + "time": 0, + "limit": { + "mode": "section", + "value": 0 + }, + "customIsLong": false, + "expected": false + }, + { + "mode": "custom", + "words": 0, + "time": 0, + "limit": { + "mode": "section", + "value": 0 + }, + "customIsLong": true, + "expected": false + }, + { + "mode": "custom", + "words": 0, + "time": 0, + "limit": { + "mode": "time", + "value": 0 + }, + "customIsLong": false, + "expected": false + }, + { + "mode": "custom", + "words": 0, + "time": 0, + "limit": { + "mode": "time", + "value": 0 + }, + "customIsLong": true, + "expected": false + }, + { + "mode": "custom", + "words": 1, + "time": 1, + "limit": { + "mode": "word", + "value": 1 + }, + "customIsLong": false, + "expected": true + }, + { + "mode": "custom", + "words": 1, + "time": 1, + "limit": { + "mode": "word", + "value": 1 + }, + "customIsLong": true, + "expected": false + }, + { + "mode": "custom", + "words": 1, + "time": 1, + "limit": { + "mode": "section", + "value": 1 + }, + "customIsLong": false, + "expected": true + }, + { + "mode": "custom", + "words": 1, + "time": 1, + "limit": { + "mode": "section", + "value": 1 + }, + "customIsLong": true, + "expected": false + }, + { + "mode": "custom", + "words": 1, + "time": 1, + "limit": { + "mode": "time", + "value": 1 + }, + "customIsLong": false, + "expected": true + }, + { + "mode": "custom", + "words": 1, + "time": 1, + "limit": { + "mode": "time", + "value": 1 + }, + "customIsLong": true, + "expected": false + }, + { + "mode": "custom", + "words": 899, + "time": 899, + "limit": { + "mode": "word", + "value": 899 + }, + "customIsLong": false, + "expected": true + }, + { + "mode": "custom", + "words": 899, + "time": 899, + "limit": { + "mode": "word", + "value": 899 + }, + "customIsLong": true, + "expected": false + }, + { + "mode": "custom", + "words": 899, + "time": 899, + "limit": { + "mode": "section", + "value": 899 + }, + "customIsLong": false, + "expected": true + }, + { + "mode": "custom", + "words": 899, + "time": 899, + "limit": { + "mode": "section", + "value": 899 + }, + "customIsLong": true, + "expected": false + }, + { + "mode": "custom", + "words": 899, + "time": 899, + "limit": { + "mode": "time", + "value": 899 + }, + "customIsLong": false, + "expected": true + }, + { + "mode": "custom", + "words": 899, + "time": 899, + "limit": { + "mode": "time", + "value": 899 + }, + "customIsLong": true, + "expected": false + }, + { + "mode": "custom", + "words": 900, + "time": 900, + "limit": { + "mode": "word", + "value": 900 + }, + "customIsLong": false, + "expected": true + }, + { + "mode": "custom", + "words": 900, + "time": 900, + "limit": { + "mode": "word", + "value": 900 + }, + "customIsLong": true, + "expected": false + }, + { + "mode": "custom", + "words": 900, + "time": 900, + "limit": { + "mode": "section", + "value": 900 + }, + "customIsLong": false, + "expected": true + }, + { + "mode": "custom", + "words": 900, + "time": 900, + "limit": { + "mode": "section", + "value": 900 + }, + "customIsLong": true, + "expected": false + }, + { + "mode": "custom", + "words": 900, + "time": 900, + "limit": { + "mode": "time", + "value": 900 + }, + "customIsLong": false, + "expected": false + }, + { + "mode": "custom", + "words": 900, + "time": 900, + "limit": { + "mode": "time", + "value": 900 + }, + "customIsLong": true, + "expected": false + }, + { + "mode": "custom", + "words": 999, + "time": 999, + "limit": { + "mode": "word", + "value": 999 + }, + "customIsLong": false, + "expected": true + }, + { + "mode": "custom", + "words": 999, + "time": 999, + "limit": { + "mode": "word", + "value": 999 + }, + "customIsLong": true, + "expected": false + }, + { + "mode": "custom", + "words": 999, + "time": 999, + "limit": { + "mode": "section", + "value": 999 + }, + "customIsLong": false, + "expected": true + }, + { + "mode": "custom", + "words": 999, + "time": 999, + "limit": { + "mode": "section", + "value": 999 + }, + "customIsLong": true, + "expected": false + }, + { + "mode": "custom", + "words": 999, + "time": 999, + "limit": { + "mode": "time", + "value": 999 + }, + "customIsLong": false, + "expected": false + }, + { + "mode": "custom", + "words": 999, + "time": 999, + "limit": { + "mode": "time", + "value": 999 + }, + "customIsLong": true, + "expected": false + }, + { + "mode": "custom", + "words": 1000, + "time": 1000, + "limit": { + "mode": "word", + "value": 1000 + }, + "customIsLong": false, + "expected": false + }, + { + "mode": "custom", + "words": 1000, + "time": 1000, + "limit": { + "mode": "word", + "value": 1000 + }, + "customIsLong": true, + "expected": false + }, + { + "mode": "custom", + "words": 1000, + "time": 1000, + "limit": { + "mode": "section", + "value": 1000 + }, + "customIsLong": false, + "expected": false + }, + { + "mode": "custom", + "words": 1000, + "time": 1000, + "limit": { + "mode": "section", + "value": 1000 + }, + "customIsLong": true, + "expected": false + }, + { + "mode": "custom", + "words": 1000, + "time": 1000, + "limit": { + "mode": "time", + "value": 1000 + }, + "customIsLong": false, + "expected": false + }, + { + "mode": "custom", + "words": 1000, + "time": 1000, + "limit": { + "mode": "time", + "value": 1000 + }, + "customIsLong": true, + "expected": false + }, + { + "mode": "zen", + "words": 0, + "time": 0, + "limit": { + "mode": "word", + "value": 0 + }, + "customIsLong": false, + "expected": true + }, + { + "mode": "zen", + "words": 0, + "time": 0, + "limit": { + "mode": "word", + "value": 0 + }, + "customIsLong": true, + "expected": true + }, + { + "mode": "zen", + "words": 0, + "time": 0, + "limit": { + "mode": "section", + "value": 0 + }, + "customIsLong": false, + "expected": true + }, + { + "mode": "zen", + "words": 0, + "time": 0, + "limit": { + "mode": "section", + "value": 0 + }, + "customIsLong": true, + "expected": true + }, + { + "mode": "zen", + "words": 0, + "time": 0, + "limit": { + "mode": "time", + "value": 0 + }, + "customIsLong": false, + "expected": true + }, + { + "mode": "zen", + "words": 0, + "time": 0, + "limit": { + "mode": "time", + "value": 0 + }, + "customIsLong": true, + "expected": true + }, + { + "mode": "zen", + "words": 1, + "time": 1, + "limit": { + "mode": "word", + "value": 1 + }, + "customIsLong": false, + "expected": true + }, + { + "mode": "zen", + "words": 1, + "time": 1, + "limit": { + "mode": "word", + "value": 1 + }, + "customIsLong": true, + "expected": true + }, + { + "mode": "zen", + "words": 1, + "time": 1, + "limit": { + "mode": "section", + "value": 1 + }, + "customIsLong": false, + "expected": true + }, + { + "mode": "zen", + "words": 1, + "time": 1, + "limit": { + "mode": "section", + "value": 1 + }, + "customIsLong": true, + "expected": true + }, + { + "mode": "zen", + "words": 1, + "time": 1, + "limit": { + "mode": "time", + "value": 1 + }, + "customIsLong": false, + "expected": true + }, + { + "mode": "zen", + "words": 1, + "time": 1, + "limit": { + "mode": "time", + "value": 1 + }, + "customIsLong": true, + "expected": true + }, + { + "mode": "zen", + "words": 899, + "time": 899, + "limit": { + "mode": "word", + "value": 899 + }, + "customIsLong": false, + "expected": true + }, + { + "mode": "zen", + "words": 899, + "time": 899, + "limit": { + "mode": "word", + "value": 899 + }, + "customIsLong": true, + "expected": true + }, + { + "mode": "zen", + "words": 899, + "time": 899, + "limit": { + "mode": "section", + "value": 899 + }, + "customIsLong": false, + "expected": true + }, + { + "mode": "zen", + "words": 899, + "time": 899, + "limit": { + "mode": "section", + "value": 899 + }, + "customIsLong": true, + "expected": true + }, + { + "mode": "zen", + "words": 899, + "time": 899, + "limit": { + "mode": "time", + "value": 899 + }, + "customIsLong": false, + "expected": true + }, + { + "mode": "zen", + "words": 899, + "time": 899, + "limit": { + "mode": "time", + "value": 899 + }, + "customIsLong": true, + "expected": true + }, + { + "mode": "zen", + "words": 900, + "time": 900, + "limit": { + "mode": "word", + "value": 900 + }, + "customIsLong": false, + "expected": true + }, + { + "mode": "zen", + "words": 900, + "time": 900, + "limit": { + "mode": "word", + "value": 900 + }, + "customIsLong": true, + "expected": true + }, + { + "mode": "zen", + "words": 900, + "time": 900, + "limit": { + "mode": "section", + "value": 900 + }, + "customIsLong": false, + "expected": true + }, + { + "mode": "zen", + "words": 900, + "time": 900, + "limit": { + "mode": "section", + "value": 900 + }, + "customIsLong": true, + "expected": true + }, + { + "mode": "zen", + "words": 900, + "time": 900, + "limit": { + "mode": "time", + "value": 900 + }, + "customIsLong": false, + "expected": true + }, + { + "mode": "zen", + "words": 900, + "time": 900, + "limit": { + "mode": "time", + "value": 900 + }, + "customIsLong": true, + "expected": true + }, + { + "mode": "zen", + "words": 999, + "time": 999, + "limit": { + "mode": "word", + "value": 999 + }, + "customIsLong": false, + "expected": true + }, + { + "mode": "zen", + "words": 999, + "time": 999, + "limit": { + "mode": "word", + "value": 999 + }, + "customIsLong": true, + "expected": true + }, + { + "mode": "zen", + "words": 999, + "time": 999, + "limit": { + "mode": "section", + "value": 999 + }, + "customIsLong": false, + "expected": true + }, + { + "mode": "zen", + "words": 999, + "time": 999, + "limit": { + "mode": "section", + "value": 999 + }, + "customIsLong": true, + "expected": true + }, + { + "mode": "zen", + "words": 999, + "time": 999, + "limit": { + "mode": "time", + "value": 999 + }, + "customIsLong": false, + "expected": true + }, + { + "mode": "zen", + "words": 999, + "time": 999, + "limit": { + "mode": "time", + "value": 999 + }, + "customIsLong": true, + "expected": true + }, + { + "mode": "zen", + "words": 1000, + "time": 1000, + "limit": { + "mode": "word", + "value": 1000 + }, + "customIsLong": false, + "expected": true + }, + { + "mode": "zen", + "words": 1000, + "time": 1000, + "limit": { + "mode": "word", + "value": 1000 + }, + "customIsLong": true, + "expected": true + }, + { + "mode": "zen", + "words": 1000, + "time": 1000, + "limit": { + "mode": "section", + "value": 1000 + }, + "customIsLong": false, + "expected": true + }, + { + "mode": "zen", + "words": 1000, + "time": 1000, + "limit": { + "mode": "section", + "value": 1000 + }, + "customIsLong": true, + "expected": true + }, + { + "mode": "zen", + "words": 1000, + "time": 1000, + "limit": { + "mode": "time", + "value": 1000 + }, + "customIsLong": false, + "expected": true + }, + { + "mode": "zen", + "words": 1000, + "time": 1000, + "limit": { + "mode": "time", + "value": 1000 + }, + "customIsLong": true, + "expected": true + } + ], + "quotes": [ + { + "mode": "time", + "repeatQuotes": "off", + "expected": false + }, + { + "mode": "time", + "repeatQuotes": "typing", + "expected": false + }, + { + "mode": "words", + "repeatQuotes": "off", + "expected": false + }, + { + "mode": "words", + "repeatQuotes": "typing", + "expected": false + }, + { + "mode": "quote", + "repeatQuotes": "off", + "expected": false + }, + { + "mode": "quote", + "repeatQuotes": "typing", + "expected": true + }, + { + "mode": "custom", + "repeatQuotes": "off", + "expected": false + }, + { + "mode": "custom", + "repeatQuotes": "typing", + "expected": false + }, + { + "mode": "zen", + "repeatQuotes": "off", + "expected": false + }, + { + "mode": "zen", + "repeatQuotes": "typing", + "expected": false + } + ] +} From 10dc9361d09ba1167d6d89d6bf7d41cce7af7ad4 Mon Sep 17 00:00:00 2001 From: Lakshmi Tanmay Date: Fri, 23 Oct 2026 09:26:47 +0530 Subject: [PATCH 63/93] Check Rust restart thresholds and long-text progress through production browser assets --- docs/rust-engine/progress.md | 2 + frontend/e2e/engine-proof.migration.ts | 89 ++++++++++++++++++++ frontend/src/routes/engine-proof.tsx | 70 +++++++++++++++ packages/typing-engine/src/browser.client.ts | 12 +++ 4 files changed, 173 insertions(+) diff --git a/docs/rust-engine/progress.md b/docs/rust-engine/progress.md index ba356bbdd..bc7cced38 100644 --- a/docs/rust-engine/progress.md +++ b/docs/rust-engine/progress.md @@ -160,3 +160,5 @@ do not rerun completed checks without a new reason. - Restart policy now derives no-quit blocking, long/infinite quick-restart limits, repeated targets, quote language/failure conditions, incomplete recording and repeated pace from the Rust session/config. Native checks cover 180 newly frozen quick-restart boundaries and 10 quote-repeat preferences, plus active/inactive/failed guards. Four policy tests pass; TypeScript oracle/integrity/defaults suite passes 23 tests; Clippy passes. Hosts retain loading/result/animation guards and key selection. - Ported long custom-text progress from completion.client.ts: UTF-16 display lengths, space/newline distinction, partial-last-word bailout and completed-book reset. Two native regressions pass, including isolated surrogates and prior progress at u32::MAX; browser/API coverage pending. + +- Built-browser restart/progress checks pass: 13 migration tests, 27.9 s, including all 180 quick-restart boundaries/10 quote preferences and seven long-text progress cases (Unicode/newline/bailout/u32 boundary). Type-aware lint passes. Wasm 796,283 bytes; gzip level 6 304,335 bytes (Vite 306.77 kB). Generated development glue is 34,048 bytes; actual deployed minified facade/glue asset remains below the 32 KiB transfer budget. Further additions need size work; the unused measured object boundary is next to retire. diff --git a/frontend/e2e/engine-proof.migration.ts b/frontend/e2e/engine-proof.migration.ts index 0b6f9b126..6df3d6803 100644 --- a/frontend/e2e/engine-proof.migration.ts +++ b/frontend/e2e/engine-proof.migration.ts @@ -728,3 +728,92 @@ test("replacement Wasm sessions repeat exact targets and reject old source repli expect(actual.staleRejected).toBe(true); expect(actual.pending).toEqual(actual.current); }); + +test("Wasm restart policy matches every frozen threshold and quote preference", async ({ + page, +}) => { + const fixture = JSON.parse( + await readFile(resolve(root, "oracle/restart.json"), "utf8"), + ) as { + quick: { expected: boolean }[]; + quotes: { expected: boolean }[]; + }; + await page.goto("/engine-proof"); + await page.getByRole("textbox", { name: "Command fixture" }).fill( + JSON.stringify({ + ...fixture, + quotePreparation: { + config: { mode: "quote", quoteLength: [0] }, + language: { name: "english", words: [[97]] }, + random: { kind: "seeded", state: 1 }, + quotes: { + language: "english", + groups: [[0, 100]], + quotes: [{ id: 1, text: [97, 32, 98], length: 3, source: "fixture" }], + }, + }, + }), + ); + await page.getByRole("button", { name: "Run policies", exact: true }).click(); + await expect(page.getByTestId("engine-proof-result")).not.toBeEmpty(); + await expect(page.getByTestId("engine-proof-error")).toBeEmpty(); + expect( + JSON.parse(await page.getByTestId("engine-proof-result").innerText()), + ).toEqual({ + quick: fixture.quick.map((item) => item.expected), + quotes: fixture.quotes.map((item) => item.expected), + }); +}); + +test("Wasm long custom progress keeps UTF-16, newline and bailout boundaries", async ({ + page, +}) => { + const units = (text: string): number[] => + Array.from({ length: text.length }, (_, i) => text.charCodeAt(i)); + const cases: [string[], string[], boolean, number, boolean, number][] = [ + [["one ", "two"], ["on"], false, 30, false, 30], + [["one ", "two"], ["one"], false, 30, false, 31], + [["one\n", "two"], ["one"], false, 30, false, 30], + [["😀 ", "two"], ["😀"], false, 30, false, 31], + [["one ", "two"], ["one ", "t"], false, 10, true, 0], + [["one ", "two"], ["one ", "t"], true, 10, false, 11], + [["\ud800 ", "two"], ["\ud800"], true, 4294967295, false, 4294967296], + ]; + const inputs = cases.map(([words, history, bailedOut, previous]) => ({ + log: { + version: 1, + context: { + mode: "custom", + mode2: "custom", + targetWords: words.map(units), + bailedOut, + }, + events: history.map((inputValue, wordIndex) => ({ + type: "input", + testMs: wordIndex, + data: { + inputType: "insertText", + inputValue: units(inputValue), + data: units(inputValue.slice(-1)), + wordIndex, + correct: true, + }, + })), + }, + queries: [{ op: "customProgress", previous }], + })); + await page.goto("/engine-proof"); + await page + .getByRole("textbox", { name: "Command fixture" }) + .fill(JSON.stringify(inputs)); + await page.getByRole("button", { name: "Run analysis", exact: true }).click(); + await expect(page.getByTestId("engine-proof-result")).not.toBeEmpty(); + await expect(page.getByTestId("engine-proof-error")).toBeEmpty(); + expect( + JSON.parse(await page.getByTestId("engine-proof-result").innerText()), + ).toEqual( + cases.map(([, , , , completed, progress]) => [ + { op: "customProgress", progress: { completed, progress } }, + ]), + ); +}); diff --git a/frontend/src/routes/engine-proof.tsx b/frontend/src/routes/engine-proof.tsx index 8f642545d..0c7fb4124 100644 --- a/frontend/src/routes/engine-proof.tsx +++ b/frontend/src/routes/engine-proof.tsx @@ -361,6 +361,68 @@ function EngineProof() { setFailure(error instanceof Error ? error.message : String(error)); } }; + const runPolicies = async (): Promise => { + setResult(""); + setFailure(""); + try { + const input = JSON.parse(fixture()) as { + quick: { + mode: string; + words: number; + time: number; + limit: { mode: string; value: number }; + customIsLong: boolean; + }[]; + quotes: { mode: string; repeatQuotes: string }[]; + quotePreparation: Prepare; + }; + const { loadEngine } = + await import("@oxytype/typing-engine/browser.client"); + const engine = await loadEngine(); + if (disposed) return; + const quick = input.quick.map((item) => { + const session = engine.createSession(item, [[97, 32]]); + try { + session.command({ + op: "customLimit", + now: 0, + mode: item.limit.mode, + value: item.limit.value, + }); + session.command({ op: "start", now: 0, date: 0 }); + return session.restartPolicy({ + quick: true, + customIsLong: item.customIsLong, + }).allowed; + } finally { + session.dispose(); + } + }); + const quotes = input.quotes.map((item) => { + const content = engine.createContent(); + let session: BrowserSession | undefined; + try { + if (item.mode === "quote") { + content.prepare({ + ...input.quotePreparation, + config: { ...input.quotePreparation.config, ...item }, + }); + session = content.createSession(); + } else { + session = engine.createSession(item, [[97, 32]]); + } + session.command({ op: "start", now: 0, date: 0 }); + return session.restartPolicy().withSameWordset; + } finally { + session?.dispose(); + content.dispose(); + } + }); + setResult(JSON.stringify({ quick, quotes })); + } catch (error) { + setFailure(error instanceof Error ? error.message : String(error)); + } + }; const runGeneration = async (): Promise => { setResult(""); setFailure(""); @@ -629,6 +691,14 @@ function EngineProof() { > Run restarts +
{result()}
{failure()}
{benchmark()}
diff --git a/packages/typing-engine/src/browser.client.ts b/packages/typing-engine/src/browser.client.ts index ba8d32b12..d98452665 100644 --- a/packages/typing-engine/src/browser.client.ts +++ b/packages/typing-engine/src/browser.client.ts @@ -23,6 +23,8 @@ import type { Source } from "../generated/types/Source"; import type { Preflight } from "../generated/types/Preflight"; import type { InsertionLayout } from "../generated/types/InsertionLayout"; import type { Snapshot } from "../generated/types/Snapshot"; +import type { RestartOptions } from "../generated/types/RestartOptions"; +import type { RestartDecision } from "../generated/types/RestartDecision"; import type { PaceCommand } from "../generated/types/PaceCommand"; import type { PaceUpdate } from "../generated/types/PaceUpdate"; import type { PlaybackSnapshot } from "../generated/types/PlaybackSnapshot"; @@ -32,6 +34,8 @@ import type { Log } from "../generated/types/Log"; export type { Query, Response, + RestartOptions, + RestartDecision, PaceCommand, PaceUpdate, PlaybackSnapshot, @@ -150,6 +154,14 @@ export class BrowserSession { } return JSON.parse(this.handle.command(JSON.stringify(command))) as Update; } + restartPolicy(options: Partial = {}): RestartDecision { + if (this.handle === undefined) { + throw new Error("Typing engine session disposed"); + } + return JSON.parse( + this.handle.restart_policy(JSON.stringify(options)), + ) as RestartDecision; + } pace(command: PaceCommand): PaceUpdate { if (this.handle === undefined) { throw new Error("Typing engine session disposed"); From d8346f13727d28932a3c3addca4a99c96a5ba43e Mon Sep 17 00:00:00 2001 From: Lakshmi Tanmay Date: Fri, 23 Oct 2026 09:31:28 +0530 Subject: [PATCH 64/93] Retire the slower object boundary and remove its Wasm conversion payload --- Cargo.lock | 60 ------------------- crates/typing-engine-wasm/Cargo.toml | 2 - crates/typing-engine-wasm/src/lib.rs | 13 ---- docs/rust-engine/decisions.md | 4 ++ docs/rust-engine/progress.md | 2 + .../selected-json-proof-performance.json | 37 ++++++++++++ frontend/e2e/engine-proof.migration.ts | 2 +- packages/typing-engine/src/browser.client.ts | 57 +++++++----------- 8 files changed, 65 insertions(+), 112 deletions(-) create mode 100644 docs/rust-engine/selected-json-proof-performance.json diff --git a/Cargo.lock b/Cargo.lock index c1c7beee1..4ec184744 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -26,30 +26,6 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" -[[package]] -name = "futures-core" -version = "0.3.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92d699e522242e69e3003b94ecc1f960f3a5e015aa7c5d7486e65ad01dd94f5e" - -[[package]] -name = "futures-task" -version = "0.3.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd417de3d1d015fc3bfd2b1ea46dfc7bab72ef86f1cc7cc9c78e728b34a6d1fd" - -[[package]] -name = "futures-util" -version = "0.3.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d50a92467f8ba5dd6e3ee5d4bd04d73ab2e4e1c44474a0674821dfce14b79bc" -dependencies = [ - "futures-core", - "futures-task", - "pin-project-lite", - "slab", -] - [[package]] name = "hashbrown" version = "0.17.1" @@ -72,17 +48,6 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" -[[package]] -name = "js-sys" -version = "0.3.106" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7883d941dae510fb2d978fc3fe018c71c9e2892fd38854de3e8b92c2e5ad9cc5" -dependencies = [ - "cfg-if", - "futures-util", - "wasm-bindgen", -] - [[package]] name = "memchr" version = "2.8.3" @@ -95,12 +60,6 @@ version = "1.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" -[[package]] -name = "pin-project-lite" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" - [[package]] name = "proc-macro2" version = "1.0.107" @@ -135,17 +94,6 @@ dependencies = [ "serde_derive", ] -[[package]] -name = "serde-wasm-bindgen" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8302e169f0eddcc139c70f139d19d6467353af16f9fce27e8c30158036a1e16b" -dependencies = [ - "js-sys", - "serde", - "wasm-bindgen", -] - [[package]] name = "serde_core" version = "1.0.229" @@ -181,12 +129,6 @@ dependencies = [ "zmij", ] -[[package]] -name = "slab" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" - [[package]] name = "syn" version = "2.0.119" @@ -274,8 +216,6 @@ dependencies = [ name = "typing-engine-wasm" version = "0.1.0" dependencies = [ - "serde", - "serde-wasm-bindgen", "serde_json", "typing-engine", "wasm-bindgen", diff --git a/crates/typing-engine-wasm/Cargo.toml b/crates/typing-engine-wasm/Cargo.toml index 830578b23..f2de450d1 100644 --- a/crates/typing-engine-wasm/Cargo.toml +++ b/crates/typing-engine-wasm/Cargo.toml @@ -13,8 +13,6 @@ crate-type = ["cdylib", "rlib"] typing-engine = { path = "../typing-engine" } serde_json.workspace = true wasm-bindgen.workspace = true -serde.workspace = true -serde-wasm-bindgen = "0.6.5" [lints] workspace = true diff --git a/crates/typing-engine-wasm/src/lib.rs b/crates/typing-engine-wasm/src/lib.rs index f8188e663..7d3677d5e 100644 --- a/crates/typing-engine-wasm/src/lib.rs +++ b/crates/typing-engine-wasm/src/lib.rs @@ -1,4 +1,3 @@ -use serde::Serialize; use typing_engine::{BINDING_VERSION, Config, Session, Text}; use wasm_bindgen::prelude::*; @@ -80,18 +79,6 @@ impl WasmSession { .ok_or_else(|| JsError::new("Session disposed"))?; Ok(serde_json::to_string(&session.log())?) } - pub fn command_value(&mut self, command: JsValue) -> Result { - let command = serde_wasm_bindgen::from_value(command)?; - let session = self - .session - .as_mut() - .ok_or_else(|| JsError::new("Session disposed"))?; - let update = session - .apply(command) - .map_err(|message| JsError::new(&message))?; - Ok(update - .serialize(&serde_wasm_bindgen::Serializer::new().serialize_maps_as_objects(true))?) - } pub fn dispose(&mut self) { self.session = None; } diff --git a/docs/rust-engine/decisions.md b/docs/rust-engine/decisions.md index 0fe64be9c..555cc7379 100644 --- a/docs/rust-engine/decisions.md +++ b/docs/rust-engine/decisions.md @@ -30,3 +30,7 @@ Actual built-Cloudflare browser tests show both master and minimum-burst failure ## Section request ordering One outstanding section request per generator, with at most 16 prefetched sections. Background prefetch does not disable typing while already prepared words remain. At exhaustion, preserve the current commit and its bounded remaining UTF-16 batch; reject new insertion/deletion until a matching response arrives. Generation and input timestamps remain stable. Reset/end cancel requests. Resume-time completion uses the reply's explicit monotonic and wall-clock timestamps, matching the browser's post-await finish ordering. This replaces the old possibility of concurrent, out-of-order section fetches with explicit ordering as required by the migration contract; the browser adapter must not start duplicate requests. + +## Selected command serialization + +Archived native/browser measurements compare incremental object conversion and JSON (proof-performance.json, profile-z-proof-performance.json). JSON is faster at the frozen 200-unit workload. Retire the benchmark-only object facade and serde-wasm-bindgen conversion dependency after the expanded 13-test browser parity pass; production uses one synchronous JSON interface. Keep original measurement evidence. Deployed glue is the minified Vite asset containing generated glue and the host facade (15,975 bytes at the policy checkpoint); generated development-source comments are not transferred. Numeric budgets remain unchanged. diff --git a/docs/rust-engine/progress.md b/docs/rust-engine/progress.md index bc7cced38..a7c3f6dbd 100644 --- a/docs/rust-engine/progress.md +++ b/docs/rust-engine/progress.md @@ -162,3 +162,5 @@ do not rerun completed checks without a new reason. - Ported long custom-text progress from completion.client.ts: UTF-16 display lengths, space/newline distinction, partial-last-word bailout and completed-book reset. Two native regressions pass, including isolated surrogates and prior progress at u32::MAX; browser/API coverage pending. - Built-browser restart/progress checks pass: 13 migration tests, 27.9 s, including all 180 quick-restart boundaries/10 quote preferences and seven long-text progress cases (Unicode/newline/bailout/u32 boundary). Type-aware lint passes. Wasm 796,283 bytes; gzip level 6 304,335 bytes (Vite 306.77 kB). Generated development glue is 34,048 bytes; actual deployed minified facade/glue asset remains below the 32 KiB transfer budget. Further additions need size work; the unused measured object boundary is next to retire. + +- Retiring the unused object-conversion benchmark removes serde-wasm-bindgen/js-sys and reduces Wasm to 688,430 raw / 274,470 gzip level 6 (Vite 276.52 kB), with deployed glue/facade 10,948 bytes. All 13 browser migration tests still pass (24.2 s); Clippy/type-aware lint pass. New JSON sample: median 0.0075 ms / p95 0.01 ms per UTF-16 unit; initialization 48.7 ms (single sample); paint maximum 35.7 ms; memory 1,376,256 bytes. Evidence: selected-json-proof-performance.json. Full initialization/soak/website performance gates remain open. diff --git a/docs/rust-engine/selected-json-proof-performance.json b/docs/rust-engine/selected-json-proof-performance.json new file mode 100644 index 000000000..b8f069f4a --- /dev/null +++ b/docs/rust-engine/selected-json-proof-performance.json @@ -0,0 +1,37 @@ +{ + "fixture": "built Cloudflare Start; Desktop Chrome; 50 warmed JSON samples; 200 UTF-16 units per sample; 7 real keyboard commits with double-rAF paint", + "browser": "153.0.8010.12", + "wasmBytes": 688430, + "wasmHeaders": { + "transfer-encoding": "chunked", + "cache-control": "public, max-age=31536000, immutable", + "content-encoding": "gzip", + "cf-cache-status": "HIT", + "etag": "\"1b4016ae8a949fe90f6800474106071b\"", + "content-type": "application/wasm", + "x-content-type-options": "nosniff" + }, + "inputToPaint": [ + 33.70000000298023, 29.200000002980232, 28.799999997019768, + 35.70000000298023, 33, 32.20000000298023, 30.700000002980232 + ], + "benchmark": { + "initializationMs": 48.70000000298023, + "jsonMs": [ + 0.01, 0.01, 0.009499999955296516, 0.009499999955296516, + 0.009500000029802322, 0.00899999998509884, 0.00850000001490116, + 0.00850000001490116, 0.00850000001490116, 0.00850000001490116, + 0.008000000044703484, 0.007999999970197678, 0.008000000044703484, + 0.008000000044703484, 0.0075, 0.0075, 0.0075, 0.0075, 0.0075, + 0.007999999970197678, 0.007000000029802322, 0.0075, 0.0075, 0.0075, + 0.0075, 0.0075, 0.0075, 0.006999999955296516, 0.006999999955296516, + 0.006999999955296516, 0.0075, 0.0075, 0.007000000029802322, 0.0075, + 0.0075, 0.007000000029802322, 0.007000000029802322, 0.01, + 0.007000000029802322, 0.0075, 0.007000000029802322, 0.006999999955296516, + 0.007000000029802322, 0.0075, 0.007000000029802322, 0.006999999955296516, + 0.007000000029802322, 0.006500000059604645, 0.006499999985098839, + 0.006499999985098839 + ], + "memoryBytes": 1376256 + } +} diff --git a/frontend/e2e/engine-proof.migration.ts b/frontend/e2e/engine-proof.migration.ts index 6df3d6803..2b977bcd7 100644 --- a/frontend/e2e/engine-proof.migration.ts +++ b/frontend/e2e/engine-proof.migration.ts @@ -66,7 +66,7 @@ test("native fixture matches browser Wasm through built Cloudflare assets", asyn ); await writeFile( output, - `${JSON.stringify({ fixture: "built Cloudflare Start; Desktop Chrome; 50 warmed samples per boundary; 200 UTF-16 units per sample; 7 real keyboard commits with double-rAF paint", browser: page.context().browser()?.version(), wasmBytes: (await wasm.body()).byteLength, wasmHeaders: wasm.headers(), inputToPaint, benchmark }, null, 2)}\n`, + `${JSON.stringify({ fixture: "built Cloudflare Start; Desktop Chrome; 50 warmed JSON samples; 200 UTF-16 units per sample; 7 real keyboard commits with double-rAF paint", browser: page.context().browser()?.version(), wasmBytes: (await wasm.body()).byteLength, wasmHeaders: wasm.headers(), inputToPaint, benchmark }, null, 2)}\n`, ); } // Invalid creation must be recoverable; subsequent valid creation still works. diff --git a/packages/typing-engine/src/browser.client.ts b/packages/typing-engine/src/browser.client.ts index d98452665..d2605d726 100644 --- a/packages/typing-engine/src/browser.client.ts +++ b/packages/typing-engine/src/browser.client.ts @@ -196,12 +196,6 @@ export class BrowserSession { } return JSON.parse(this.handle.log()) as unknown; } - commandObject(command: Command): Update { - if (this.handle === undefined) { - throw new Error("Typing engine session disposed"); - } - return this.handle.command_value(command) as Update; - } dispose(): void { const handle = this.handle; if (handle === undefined) return; @@ -211,44 +205,35 @@ export class BrowserSession { } } -/** Compare incremental object conversion with a JSON boundary, same core. */ -export function benchmarkBoundary(): { jsonMs: number[]; objectMs: number[] } { +/** Selected synchronous JSON boundary; prior object-control measurements are archived. */ +export function benchmarkBoundary(): { jsonMs: number[] } { const jsonMs: number[] = []; - const objectMs: number[] = []; const config: Partial = { mode: "words", words: 0 }; const words = Array.from({ length: 101 }, () => encodeText("a ")); for (let sample = 0; sample < 60; sample++) { - for (const [object, measurements] of [ - [false, jsonMs], - [true, objectMs], - ] as const) { - const session = new BrowserSession(config, words); - try { - const start = performance.now(); - for (let index = 0; index < 100; index++) { - const command: Command = { - op: "insert", - data: [97, 32], - now: index * 100, - date: 0, - automatic: null, - isCompositionEnding: null, - correctShiftUsed: null, - }; - const update = object - ? session.commandObject(command) - : session.command(command); - if (update.state.wordIndex !== index + 1) { - throw new Error("Boundary benchmark lost progress"); - } + const session = new BrowserSession(config, words); + try { + const start = performance.now(); + for (let index = 0; index < 100; index++) { + const update = session.command({ + op: "insert", + data: [97, 32], + now: index * 100, + date: 0, + automatic: null, + isCompositionEnding: null, + correctShiftUsed: null, + }); + if (update.state.wordIndex !== index + 1) { + throw new Error("Boundary benchmark lost progress"); } - if (sample >= 10) measurements.push((performance.now() - start) / 200); - } finally { - session.dispose(); } + if (sample >= 10) jsonMs.push((performance.now() - start) / 200); + } finally { + session.dispose(); } } - return { jsonMs, objectMs }; + return { jsonMs }; } export class BrowserContent { From ebf8e115b67534beb5593414b5ffd12b2e62be7e Mon Sep 17 00:00:00 2001 From: Lakshmi Tanmay Date: Fri, 23 Oct 2026 09:40:59 +0530 Subject: [PATCH 65/93] Preserve historical UTF-16 logs through typed browser conversion and replay --- crates/typing-engine/src/stats.rs | 4 + crates/typing-engine/tests/analysis.rs | 51 + docs/rust-engine/progress.md | 25 +- frontend/e2e/engine-proof.migration.ts | 104 ++ frontend/src/routes/engine-proof.tsx | 19 +- .../capture-historical-unicode-oracle.ts | 122 ++ packages/typing-engine/__test__/text.spec.ts | 81 ++ packages/typing-engine/package.json | 3 +- packages/typing-engine/src/browser.client.ts | 18 +- packages/typing-engine/src/text.ts | 86 ++ packages/typing-engine/tsconfig.json | 4 +- packages/typing-engine/vitest.config.ts | 3 + .../oracle/historical-unicode.json | 1230 +++++++++++++++++ 13 files changed, 1721 insertions(+), 29 deletions(-) create mode 100644 packages/typing-core/scripts/capture-historical-unicode-oracle.ts create mode 100644 packages/typing-engine/__test__/text.spec.ts create mode 100644 packages/typing-engine/src/text.ts create mode 100644 packages/typing-engine/vitest.config.ts create mode 100644 packages/typing-fixtures/oracle/historical-unicode.json diff --git a/crates/typing-engine/src/stats.rs b/crates/typing-engine/src/stats.rs index bca645bc6..666020a60 100644 --- a/crates/typing-engine/src/stats.rs +++ b/crates/typing-engine/src/stats.rs @@ -18,7 +18,11 @@ pub struct Context { pub bailed_out: bool, #[serde(default)] pub korean_status: bool, + #[serde(skip_serializing_if = "Option::is_none")] + #[ts(optional)] pub custom_text_limit_mode: Option, + #[serde(skip_serializing_if = "Option::is_none")] + #[ts(optional)] pub custom_text_limit_value: Option, } #[derive(Clone, Debug, Deserialize, Serialize, TS)] diff --git a/crates/typing-engine/tests/analysis.rs b/crates/typing-engine/tests/analysis.rs index e5f532ffb..7345ba4d7 100644 --- a/crates/typing-engine/tests/analysis.rs +++ b/crates/typing-engine/tests/analysis.rs @@ -19,6 +19,57 @@ fn equal(a: &Value, b: &Value, path: &str) { } } #[test] +fn historical_surrogate_halves_keep_exact_stats_frames_playback_and_seeking() { + let path = std::path::Path::new(env!("CARGO_MANIFEST_DIR")) + .join("../../packages/typing-fixtures/oracle/historical-unicode.json"); + let references: Vec = serde_json::from_slice(&std::fs::read(path).unwrap()).unwrap(); + for reference in references { + let name = reference["name"].as_str().unwrap(); + let log: Log = serde_json::from_value(reference["log"].clone()).unwrap(); + for (query, expected) in reference["queries"] + .as_array() + .unwrap() + .iter() + .zip(reference["expected"].as_array().unwrap()) + { + let query = serde_json::from_value(query.clone()).unwrap(); + equal( + &json!(typing_engine::analysis::analyze(&log, query)), + expected, + name, + ); + } + let stats = json!({ + "chars":typing_engine::stats::chars(&log,true,None), + "acc":typing_engine::stats::accuracy(&log,None), + "history":typing_engine::stats::input_history(&log), + "corrected":typing_engine::stats::corrected_history(&log), + "wpm":typing_engine::stats::speed_history(&log,false), + "raw":typing_engine::stats::speed_history(&log,true), + "burst":typing_engine::stats::word_burst_history(&log,1000.0) + }); + equal(&stats, &reference["stats"], name); + for expected in reference["playback"].as_array().unwrap() { + let mut playback = replay::Playback::new(&log, expected["zen"].as_bool().unwrap()); + let mut sounds = vec![]; + while playback.snapshot().next_time.is_some() { + sounds.push(playback.step().sound); + } + let snapshot = playback.snapshot(); + equal(&json!(snapshot.words), &expected["words"], name); + equal(&json!(snapshot.cursor), &expected["cursor"], name); + equal(&json!(sounds), &expected["sounds"], name); + for target in expected["seeks"].as_array().unwrap() { + let snapshot = + playback.seek(serde_json::from_value(target["target"].clone()).unwrap()); + equal(&json!(snapshot.words), &target["words"], name); + equal(&json!(snapshot.cursor), &target["cursor"], name); + equal(&json!(snapshot.next_index), &target["nextIndex"], name); + } + } + } +} +#[test] fn replay_seeking_practice_and_validity_match_frozen_typescript_oracle() { let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("../../packages/typing-fixtures"); diff --git a/docs/rust-engine/progress.md b/docs/rust-engine/progress.md index a7c3f6dbd..196d4c14c 100644 --- a/docs/rust-engine/progress.md +++ b/docs/rust-engine/progress.md @@ -21,9 +21,9 @@ Phase 1.1 and 1.2 gates passed: inventory/baselines, lossless contract, frozen o | 1.1 Inventory / baselines | Complete | See inventory.md, features.md, baseline.md and raw measurements. | | 1.2 Contracts / fixtures | Complete | 19 command traces, 21 oracle/integrity tests; contract and classified gaps. | | 1.3 Native / browser proof | Complete | Native 7 traces; Cloudflare real-browser finite slice; MIME/CSP, lifecycle, boundary benchmark. Budgets frozen. | -| 1.4 Input / lifecycle / timing | Active | 19 native/browser command traces; 8 original recordings. Composition, batch failure, deferred work, visible-word bounds and browser deletion observations covered. Full generator/session integration remains open. | -| 1.5 Generation / configuration | Active | 61/61 unchanged generator snapshots. Content requests, configuration validation and generator/session integration pending. | -| 1.6 Events / results / replay | Active | All computed summary fields match 8 original CompletedEvents exactly. Full results, eligibility, histories and replay pass native/browser references; malformed/historical edge coverage and incremental playback facade pending. | +| 1.4 Input / lifecycle / timing | Active | 19 native/browser command traces; 8 original recordings. Composition, batch failure, deferred work, visible-word bounds and browser deletion observations covered. Owned generation, delayed completion and restart rules now pass; randomized lifecycle/live-stat performance gate remains open. | +| 1.5 Generation / configuration | Active | 61/61 unchanged generator snapshots. Content requests and owned session continuation pass; structured quote/polyglot recovery and malformed-input coverage remain open. | +| 1.6 Events / results / replay | Active | All computed summary fields match 8 original CompletedEvents exactly. Full results, eligibility, histories and replay pass native/browser references; persistent playback and historical UTF-16 edges pass; schema/hash/backend and malformed-log gates remain open. | | 1.7 Binding / engine gate | Pending | Full three-path parity and budgets. | | 2.1–2.6 Website | Pending | Requires Stage 1 gate. | | 3.1–3.5 Terminal | Pending | Requires Stage 2 gate. | @@ -131,12 +131,12 @@ do not rerun completed checks without a new reason. ## Additional cutover dependencies identified during integration review -- Preserve app-lifetime weak-spot learning and remembered pace across session replacement through an explicit owned engine context; current newly created generators start fresh. -- Complete Wasm pace operations and a persistent replay handle. +- Completed: owned recycling retains learning, remembered pace, repeat words and quote identity; generations survive reset/replacement. +- Completed: typed Wasm pace operations and persistent replay handle. - Replace min-burst whole-log cloning and provide bounded live-stat queries before long-session/per-key performance acceptance. -- Initialization must be safe for concurrent owners without module-level app state; consider async module compilation followed by synchronous guarded wasm-bindgen initialization, plus app-owned load promises. +- Completed: concurrent compile/guarded initialization and owned retryable loader verified in the real browser. - Expose structured recovery actions for missing quote/favorite/polyglot content and active content failures; avoid string-matching rules in hosts. -- Port restart/no-quit/long-test and custom-progress calculations; preserve browser input normalization boundaries in helper extraction. +- Completed: restart/no-quit/long-test and custom progress ported and checked natively/in browser. Production input adapter still awaits Stage 2. - Slow-render policy now belongs to Rust: exact short-test thresholds/counters, failure before ticks, long/infinite catchup and overflow suppression. Native full suite/Clippy pass. Built Cloudflare browser proof passes all 5 Wasm tests, including observed completion time and slow-render failure; the actual website's slow-render contract passes separately. Initial web test hit an offscreen words-container click; corrected to the visible first word and reran (1 pass, 7.5 s). Existing master/min-burst tests also pass. Loader concurrency and persistent replay changes are subsequent work, not included in this evidence. @@ -164,3 +164,14 @@ do not rerun completed checks without a new reason. - Built-browser restart/progress checks pass: 13 migration tests, 27.9 s, including all 180 quick-restart boundaries/10 quote preferences and seven long-text progress cases (Unicode/newline/bailout/u32 boundary). Type-aware lint passes. Wasm 796,283 bytes; gzip level 6 304,335 bytes (Vite 306.77 kB). Generated development glue is 34,048 bytes; actual deployed minified facade/glue asset remains below the 32 KiB transfer budget. Further additions need size work; the unused measured object boundary is next to retire. - Retiring the unused object-conversion benchmark removes serde-wasm-bindgen/js-sys and reduces Wasm to 688,430 raw / 274,470 gzip level 6 (Vite 276.52 kB), with deployed glue/facade 10,948 bytes. All 13 browser migration tests still pass (24.2 s); Clippy/type-aware lint pass. New JSON sample: median 0.0075 ms / p95 0.01 ms per UTF-16 unit; initialization 48.7 ms (single sample); paint maximum 35.7 ms; memory 1,376,256 bytes. Evidence: selected-json-proof-performance.json. Full initialization/soak/website performance gates remain open. + +- Historical log bridge is SSR-safe and losslessly converts target words, input snapshots and composition data between JavaScript strings and UTF-16 arrays. Three tests cover every UTF-16 unit, original recordings/optional metadata and isolated text. Two new immutable historical Unicode references match native stats/frames/replay/seeks, then match real-browser Wasm through actual legacy string conversion. All 14 browser migration tests pass (26.1 s); native analysis, Clippy and type-aware lint pass. A parallel typecheck briefly ran while the prior builder had removed generated declarations; sequential rerun passed. Builder publication is being fixed separately. + +## Remaining Stage 1 exit work + +- Structured native/binding errors and original quote/favorite/polyglot recovery actions; explicit failed-content reply lifecycle. +- Bounded live-stat and word-burst reads without full-log cloning on commits/ticks. +- Complete content/config/observation/log validation, checked transport DTO compatibility, randomized lifecycle/stale-handle checks. +- Final engine initialization distributions, long-session/restart memory and per-key performance budgets; full website startup/input/restart comparison belongs to Stage 2 after integration. +- Current-head full regression/backend submission/hash checks, fresh-checkout bootstrap and complete CI evidence. +- Stage 2/3 implementation and their two stacked PRs still pending. No deployment or release publication performed. diff --git a/frontend/e2e/engine-proof.migration.ts b/frontend/e2e/engine-proof.migration.ts index 2b977bcd7..facfd98b2 100644 --- a/frontend/e2e/engine-proof.migration.ts +++ b/frontend/e2e/engine-proof.migration.ts @@ -1,6 +1,7 @@ import { readFile, readdir, writeFile } from "node:fs/promises"; import { resolve } from "node:path"; import { expect, test } from "playwright/test"; +import { toWebLog } from "@oxytype/typing-engine/text"; const root = resolve(import.meta.dirname, "../../packages/typing-fixtures"); test("native fixture matches browser Wasm through built Cloudflare assets", async ({ @@ -817,3 +818,106 @@ test("Wasm long custom progress keeps UTF-16, newline and bailout boundaries", a ]), ); }); + +test("historical surrogate halves cross browser Wasm without lossy strings", async ({ + page, +}) => { + const references = JSON.parse( + await readFile(resolve(root, "oracle/historical-unicode.json"), "utf8"), + ) as { + name: string; + log: Parameters[0]; + queries: unknown[]; + expected: unknown[]; + playback: { + zen: boolean; + words: unknown; + cursor: unknown; + sounds: unknown; + seeks: { + target: unknown; + words: unknown; + cursor: unknown; + nextIndex: number; + }[]; + }[]; + stats: { + history: unknown; + corrected: unknown; + wpm: unknown; + raw: unknown; + chars: { + correctWord: number; + incorrect: number; + extra: number; + missed: number; + }; + }; + }[]; + const inputs = references.map((reference) => ({ + webLog: toWebLog(reference.log), + queries: [...reference.queries, { op: "stats", now: 1000 }], + playback: reference.playback.map((replay) => ({ + zen: replay.zen, + targets: replay.seeks.map((seek) => seek.target), + })), + })); + await page.goto("/engine-proof"); + await page + .getByRole("textbox", { name: "Command fixture" }) + .fill(JSON.stringify(inputs)); + await page.getByRole("button", { name: "Run analysis", exact: true }).click(); + await expect(page.getByTestId("engine-proof-result")).not.toBeEmpty(); + await expect(page.getByTestId("engine-proof-error")).toBeEmpty(); + const actual = JSON.parse( + await page.getByTestId("engine-proof-result").innerText(), + ) as { + analysis: unknown[]; + roundTrip: unknown; + playback: unknown; + }[]; + expect(actual).toHaveLength(references.length); + for (const [index, reference] of references.entries()) { + const value = actual[index]; + if (value === undefined) throw new Error("Missing Unicode output"); + expect( + { ...value, analysis: value.analysis.slice(0, -1) }, + reference.name, + ).toEqual({ + analysis: reference.expected, + roundTrip: toWebLog(reference.log), + playback: reference.playback.map((replay) => ({ + words: replay.words, + cursor: replay.cursor, + sounds: replay.sounds, + stale: true, + seeks: replay.seeks.map(({ words, cursor, nextIndex }) => ({ + words, + cursor, + nextIndex, + })), + })), + }); + const stats = value.analysis.at(-1) as { + stats: { + inputHistory: unknown; + correctedWordsHistory: unknown; + rawHistory: unknown; + summary: { charStats: unknown; chartData: { wpm: unknown } }; + }; + }; + expect(stats.stats.inputHistory).toEqual(reference.stats.history); + expect(stats.stats.correctedWordsHistory).toEqual( + reference.stats.corrected, + ); + expect(stats.stats.rawHistory).toEqual(reference.stats.raw); + expect(stats.stats.summary.chartData.wpm).toEqual(reference.stats.wpm); + const counts = reference.stats.chars; + expect(stats.stats.summary.charStats).toEqual([ + counts.correctWord, + counts.incorrect, + counts.extra, + counts.missed, + ]); + } +}); diff --git a/frontend/src/routes/engine-proof.tsx b/frontend/src/routes/engine-proof.tsx index 0c7fb4124..556e77bcd 100644 --- a/frontend/src/routes/engine-proof.tsx +++ b/frontend/src/routes/engine-proof.tsx @@ -12,6 +12,7 @@ import type { Cursor, PaceCommand, } from "@oxytype/typing-engine/browser.client"; +import type { WebLog } from "@oxytype/typing-engine/text"; import { createFileRoute } from "@tanstack/solid-router"; import { createSignal, onCleanup, Show } from "solid-js"; @@ -244,7 +245,7 @@ function EngineProof() { freshWords: fresh.snapshot().words, remembered: next.pace({ op: "reset", wpm: null }).lastWpm, isolated: fresh.pace({ op: "reset", wpm: null }).lastWpm, - emptyLog: (next.log() as Log).events.length === 0, + emptyLog: next.log().events.length === 0, active: next.snapshot().state.active, }), ); @@ -487,21 +488,29 @@ function EngineProof() { setFailure(""); try { const inputs = JSON.parse(fixture()) as { - log: Log; + log?: Log; + webLog?: WebLog; queries: Query[]; playback?: { zen: boolean; targets: Cursor[] }[]; }[]; - const { loadEngine, analyzeLog } = + const { loadEngine, analyzeLog, toEngineLog, toWebLog } = await import("@oxytype/typing-engine/browser.client"); const engine = await loadEngine(); if (disposed) return; setResult( JSON.stringify( - inputs.map(({ log, queries, playback }) => { + inputs.map(({ log: neutral, webLog, queries, playback }) => { + const log = webLog === undefined ? neutral : toEngineLog(webLog); + if (log === undefined) throw new Error("Missing analysis log"); const analysis = queries.map((query) => analyzeLog(log, query)); - if (playback === undefined) return analysis; + if (playback === undefined) { + return webLog === undefined + ? analysis + : { analysis, roundTrip: toWebLog(log) }; + } return { analysis, + ...(webLog === undefined ? {} : { roundTrip: toWebLog(log) }), playback: playback.map(({ zen, targets }) => { const replay = engine.createReplay(log, zen); try { diff --git a/packages/typing-core/scripts/capture-historical-unicode-oracle.ts b/packages/typing-core/scripts/capture-historical-unicode-oracle.ts new file mode 100644 index 000000000..e1c220a4d --- /dev/null +++ b/packages/typing-core/scripts/capture-historical-unicode-oracle.ts @@ -0,0 +1,122 @@ +import { readFileSync, writeFileSync } from "node:fs"; +import { resolve } from "node:path"; +import { createTestSession } from "../src/session"; +import { getDefaultConfig } from "../src/config/default-config"; +import type { EventLog } from "../src/events/types"; +import * as stats from "../src/events/stats"; +import * as replay from "../src/replay"; +import { units, neutralLog } from "./rust-oracle"; +import { toWebLog } from "../../typing-engine/src/text"; +import type { Log } from "../../typing-engine/generated/types/Log"; + +const frozen = JSON.parse( + readFileSync( + resolve( + import.meta.dirname, + "../../typing-fixtures/oracle/unicode-halves.json", + ), + "utf8", + ), +) as { frames: { log: Log }[] }; +const final = frozen.frames.at(-1); +if (final === undefined) throw new Error("Missing Unicode reference"); +const session = createTestSession( + { ...getDefaultConfig(), mode: "words", words: 2 }, + { words: ["\ud800 ", "\udc00"], dateNow: () => 0 }, +); +await session.insert("\ud800", 0); +await session.insert(" ", 10); +session.record("composition", 20, { event: "start", wordIndex: 1 }); +session.record("composition", 21, { + event: "update", + data: "\udc00", + wordIndex: 1, +}); +await session.insert("\udc00", 30, { isCompositionEnding: true }); +session.record("composition", 40, { + event: "end", + data: "\udc00", + wordIndex: 1, +}); +const prompt = (words: replay.ReplayWord[]): unknown => + words.map((word) => ({ + ...word, + letters: word.letters.map((letter) => ({ + ...letter, + char: units(letter.char), + })), + })); +const references = [ + { name: "paired-target-split-events", log: toWebLog(final.log) as EventLog }, + { name: "isolated-targets-and-composition", log: session.buildEventLog() }, +].map(({ name, log }) => { + const queries: unknown[] = []; + const expected: unknown[] = []; + for (const time of [-1, 0, 10, 20, 50, 100]) { + const frame = replay.replayFrame(log, time); + queries.push({ op: "frame", time }); + expected.push({ + op: "frame", + frame: { + inputs: frame.inputs.map(units), + activeIndex: frame.activeIndex, + }, + }); + } + const playback = [false, true].map((zen) => { + const words = replay.getReplayWords(log, zen); + const steps = replay.getReplaySteps(log, zen); + queries.push({ op: "replay", zen }); + expected.push({ + op: "replay", + words: words.map(units), + steps: steps.map((step) => ({ + ...step, + ...(typeof step.value === "string" ? { value: units(step.value) } : {}), + })), + duration: replay.replayDuration(log), + }); + const finalWords = replay.createReplayPrompt(words, steps); + const cursor = { wordIndex: 0, letterIndex: 0 }; + const sounds = steps.map( + (step) => replay.applyReplayStep(finalWords, cursor, step) ?? null, + ); + const seeks = [ + { wordIndex: 0, letterIndex: 0 }, + { wordIndex: 0, letterIndex: 1 }, + { wordIndex: 1, letterIndex: 1 }, + ].map((target) => { + const seekWords = replay.createReplayPrompt( + replay.getReplayWords(log, zen), + steps, + ); + const result = replay.seekReplay(seekWords, steps, target); + return { target, ...result, words: prompt(seekWords) }; + }); + return { zen, words: prompt(finalWords), cursor, sounds, seeks }; + }); + return { + name, + log: neutralLog(log), + queries, + expected, + playback, + stats: { + chars: stats.getChars(log, true), + acc: stats.getAccuracy(log).percentage, + history: stats.getInputHistory(log).map(units), + corrected: stats.getCorrectedWordsHistory(log).map(units), + wpm: stats.getWpmHistory(log), + raw: stats.getRawHistory(log), + burst: stats.getWordBurstHistory(log), + }, + }; +}); +writeFileSync( + resolve( + import.meta.dirname, + "../../typing-fixtures/oracle/historical-unicode.json", + ), + `${JSON.stringify(references, null, 2)}\n`, + { flag: "wx" }, +); diff --git a/packages/typing-engine/__test__/text.spec.ts b/packages/typing-engine/__test__/text.spec.ts new file mode 100644 index 000000000..f9c553ac7 --- /dev/null +++ b/packages/typing-engine/__test__/text.spec.ts @@ -0,0 +1,81 @@ +import { readFileSync, readdirSync } from "node:fs"; +import { resolve } from "node:path"; +import { expect, it } from "vite-plus/test"; +import { + decodeText, + encodeText, + toEngineLog, + toWebLog, + type WebLog, +} from "../src/text"; + +it("preserves every UTF-16 unit, including isolated surrogates, across bounded chunks", () => { + const units = Array.from({ length: 65536 }, (_, index) => index); + expect(encodeText(decodeText(units))).toEqual(units); + for (const units of [[-1], [65536], [1.5], [NaN]]) { + expect(() => decodeText(units)).toThrow("Invalid UTF-16"); + } +}); +it("roundtrips all immutable website logs without changing optional fields or metadata", () => { + const directory = resolve( + import.meta.dirname, + "../../typing-fixtures/reference/keystrokes", + ); + for (const name of readdirSync(directory).filter((name) => + name.endsWith(".json"), + )) { + const fixture = JSON.parse( + readFileSync(resolve(directory, name), "utf8"), + ) as { eventLog: WebLog }; + const original = JSON.stringify(fixture.eventLog); + expect(toWebLog(toEngineLog(fixture.eventLog)), name).toEqual( + fixture.eventLog, + ); + expect(JSON.stringify(fixture.eventLog)).toBe(original); + } +}); +it("neutralizes lone-surrogate input and composition text before JSON serialization", () => { + const log: WebLog = { + version: 1, + context: { + mode: "words", + mode2: "1", + targetWords: ["\ud800\udc00\ud800"], + bailedOut: false, + koreanStatus: false, + }, + events: [ + { + type: "input", + testMs: 0, + data: { + data: "\ud800", + inputValue: "\ud800", + wordIndex: 0, + correct: true, + }, + }, + { + type: "composition", + testMs: 1, + data: { event: "update", data: "\udc00", wordIndex: 0 }, + }, + { type: "keyup", testMs: 2, data: { code: "NoCode0", estimated: true } }, + ], + }; + const neutral = toEngineLog(log); + expect(neutral.context.targetWords).toEqual([[0xd800, 0xdc00, 0xd800]]); + expect(neutral.events[0]?.data).toEqual({ + data: [0xd800], + inputValue: [0xd800], + wordIndex: 0, + correct: true, + }); + expect(neutral.events[1]?.data).toEqual({ + event: "update", + data: [0xdc00], + wordIndex: 0, + }); + expect(JSON.stringify(neutral)).not.toContain("\\ud800"); + expect(toWebLog(neutral)).toEqual(log); +}); diff --git a/packages/typing-engine/package.json b/packages/typing-engine/package.json index f65f47c42..ff92f6d7f 100644 --- a/packages/typing-engine/package.json +++ b/packages/typing-engine/package.json @@ -4,7 +4,8 @@ "license": "GPL-3.0", "type": "module", "exports": { - "./browser.client": "./src/browser.client.ts" + "./browser.client": "./src/browser.client.ts", + "./text": "./src/text.ts" }, "scripts": { "ts-check": "vp lint . --type-aware --type-check --format agent", diff --git a/packages/typing-engine/src/browser.client.ts b/packages/typing-engine/src/browser.client.ts index d2605d726..a7f6b8282 100644 --- a/packages/typing-engine/src/browser.client.ts +++ b/packages/typing-engine/src/browser.client.ts @@ -61,18 +61,8 @@ export type { }; const BINDING_VERSION = 1; -export function encodeText(value: string): Text { - return Array.from({ length: value.length }, (_, index) => - value.charCodeAt(index), - ); -} -export function decodeText(units: Text): string { - let value = ""; - for (let i = 0; i < units.length; i += 4096) { - value += String.fromCharCode(...units.slice(i, i + 4096)); - } - return value; -} +export { encodeText, decodeText, toEngineLog, toWebLog } from "./text"; +import { encodeText } from "./text"; /** Module preparation is async; all prepared session commands are synchronous. */ export async function loadEngine(signal?: AbortSignal): Promise<{ @@ -190,11 +180,11 @@ export class BrowserSession { } return JSON.parse(this.handle.snapshot()) as Snapshot; } - log(): unknown { + log(): Log { if (this.handle === undefined) { throw new Error("Typing engine session disposed"); } - return JSON.parse(this.handle.log()) as unknown; + return JSON.parse(this.handle.log()) as Log; } dispose(): void { const handle = this.handle; diff --git a/packages/typing-engine/src/text.ts b/packages/typing-engine/src/text.ts new file mode 100644 index 000000000..04d1b28cb --- /dev/null +++ b/packages/typing-engine/src/text.ts @@ -0,0 +1,86 @@ +import type { Text } from "../generated/types/Text"; +import type { Event } from "../generated/types/Event"; +import type { Log } from "../generated/types/Log"; +import type { JsonValue } from "../generated/types/serde_json/JsonValue"; + +export function encodeText(value: string): Text { + return Array.from({ length: value.length }, (_, index) => + value.charCodeAt(index), + ); +} +export function decodeText(units: Text): string { + if ( + !units.every( + (unit) => Number.isInteger(unit) && unit >= 0 && unit <= 0xffff, + ) + ) { + throw new RangeError("Invalid UTF-16 code unit"); + } + let value = ""; + for (let index = 0; index < units.length; index += 4096) { + value += String.fromCharCode(...units.slice(index, index + 4096)); + } + return value; +} +type WebContext = Omit< + Log["context"], + "targetWords" | "customTextLimitMode" | "customTextLimitValue" +> & { + targetWords: string[]; + customTextLimitMode?: string; + customTextLimitValue?: number; + isFunboxWithNospacePropertyActive?: boolean; +}; +export type WebLog = { version: number; context: WebContext; events: Event[] }; + +function fields( + data: JsonValue, + transform: (text: JsonValue) => JsonValue, +): JsonValue { + if (data === null || typeof data !== "object" || Array.isArray(data)) { + return data; + } + return Object.fromEntries( + Object.entries(data).map(([key, value]) => [ + key, + key === "inputValue" || key === "data" ? transform(value) : value, + ]), + ); +} +/** Mechanical v1 wire conversion. No string ever passes through Rust String. */ +export function toEngineEvent(event: Event): Event { + return { + ...event, + data: fields(event.data, (value) => + typeof value === "string" ? encodeText(value) : value, + ), + }; +} +export function toWebEvent(event: Event): Event { + return { + ...event, + data: fields(event.data, (value) => + Array.isArray(value) ? decodeText(value as Text) : value, + ), + }; +} +export function toEngineLog(log: WebLog): Log { + return { + ...log, + context: { + ...log.context, + targetWords: log.context.targetWords.map(encodeText), + }, + events: log.events.map(toEngineEvent), + }; +} +export function toWebLog(log: Log): WebLog { + return { + ...log, + context: { + ...log.context, + targetWords: log.context.targetWords.map(decodeText), + }, + events: log.events.map(toWebEvent), + }; +} diff --git a/packages/typing-engine/tsconfig.json b/packages/typing-engine/tsconfig.json index 5b2c1ab1b..93c90caba 100644 --- a/packages/typing-engine/tsconfig.json +++ b/packages/typing-engine/tsconfig.json @@ -5,7 +5,7 @@ "module": "ESNext", "target": "ES2022", "lib": ["es2022", "dom"], - "types": ["vite-plus/client"] + "types": ["vite-plus/client", "node"] }, - "include": ["src", "generated"] + "include": ["src", "generated", "__test__"] } diff --git a/packages/typing-engine/vitest.config.ts b/packages/typing-engine/vitest.config.ts new file mode 100644 index 000000000..600ed22de --- /dev/null +++ b/packages/typing-engine/vitest.config.ts @@ -0,0 +1,3 @@ +import { defineConfig } from "vite-plus"; + +export default defineConfig({ test: { environment: "node" } }); diff --git a/packages/typing-fixtures/oracle/historical-unicode.json b/packages/typing-fixtures/oracle/historical-unicode.json new file mode 100644 index 000000000..4ac997d61 --- /dev/null +++ b/packages/typing-fixtures/oracle/historical-unicode.json @@ -0,0 +1,1230 @@ +[ + { + "name": "paired-target-split-events", + "log": { + "version": 1, + "events": [ + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [55357], + "correct": true, + "wordIndex": 0, + "charIndex": 0, + "inputValue": [55357] + } + }, + { + "type": "timer", + "testMs": 0, + "data": { + "event": "start", + "timer": 0, + "date": 0 + } + }, + { + "type": "input", + "testMs": 20, + "data": { + "inputType": "insertText", + "data": [56832], + "correct": true, + "wordIndex": 0, + "charIndex": 1, + "inputValue": [55357, 56832] + } + }, + { + "type": "input", + "testMs": 40, + "data": { + "inputType": "insertText", + "data": [32], + "correct": true, + "wordIndex": 0, + "charIndex": 2, + "inputValue": [55357, 56832, 32], + "commitsWord": true + } + }, + { + "type": "input", + "testMs": 100, + "data": { + "inputType": "insertText", + "data": [101], + "correct": true, + "wordIndex": 1, + "charIndex": 0, + "inputValue": [101], + "lastWord": true + } + }, + { + "type": "input", + "testMs": 100, + "data": { + "inputType": "insertText", + "data": [769], + "correct": true, + "wordIndex": 1, + "charIndex": 1, + "inputValue": [101, 769], + "lastWord": true + } + }, + { + "type": "timer", + "testMs": 100, + "data": { + "event": "end", + "timer": 0, + "date": 0 + } + } + ], + "context": { + "targetWords": [ + [55357, 56832, 32], + [101, 769] + ], + "mode": "words", + "mode2": "2", + "koreanStatus": false, + "bailedOut": false, + "isFunboxWithNospacePropertyActive": false + } + }, + "queries": [ + { + "op": "frame", + "time": -1 + }, + { + "op": "frame", + "time": 0 + }, + { + "op": "frame", + "time": 10 + }, + { + "op": "frame", + "time": 20 + }, + { + "op": "frame", + "time": 50 + }, + { + "op": "frame", + "time": 100 + }, + { + "op": "replay", + "zen": false + }, + { + "op": "replay", + "zen": true + } + ], + "expected": [ + { + "op": "frame", + "frame": { + "inputs": [], + "activeIndex": 0 + } + }, + { + "op": "frame", + "frame": { + "inputs": [[55357]], + "activeIndex": 0 + } + }, + { + "op": "frame", + "frame": { + "inputs": [[55357]], + "activeIndex": 0 + } + }, + { + "op": "frame", + "frame": { + "inputs": [[55357, 56832]], + "activeIndex": 0 + } + }, + { + "op": "frame", + "frame": { + "inputs": [[55357, 56832, 32]], + "activeIndex": 1 + } + }, + { + "op": "frame", + "frame": { + "inputs": [ + [55357, 56832, 32], + [101, 769] + ], + "activeIndex": 1 + } + }, + { + "op": "replay", + "words": [ + [55357, 56832, 32], + [101, 769] + ], + "steps": [ + { + "action": "correctLetter", + "value": [55357], + "time": 0 + }, + { + "action": "correctLetter", + "value": [56832], + "time": 20 + }, + { + "action": "correctLetter", + "value": [32], + "time": 40 + }, + { + "action": "submitCorrectWord", + "time": 100 + }, + { + "action": "correctLetter", + "value": [101], + "time": 100 + }, + { + "action": "correctLetter", + "value": [769], + "time": 100 + } + ], + "duration": 100 + }, + { + "op": "replay", + "words": [ + [55357, 56832, 32], + [101, 769] + ], + "steps": [ + { + "action": "correctLetter", + "value": [55357], + "time": 0 + }, + { + "action": "correctLetter", + "value": [56832], + "time": 20 + }, + { + "action": "correctLetter", + "value": [32], + "time": 40 + }, + { + "action": "submitCorrectWord", + "time": 100 + }, + { + "action": "correctLetter", + "value": [101], + "time": 100 + }, + { + "action": "correctLetter", + "value": [769], + "time": 100 + } + ], + "duration": 100 + } + ], + "playback": [ + { + "zen": false, + "words": [ + { + "letters": [ + { + "char": [55357, 56832], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [769], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + } + ], + "cursor": { + "wordIndex": 1, + "letterIndex": 2 + }, + "sounds": ["click", "click", "click", "click", "click", "click"], + "seeks": [ + { + "target": { + "wordIndex": 0, + "letterIndex": 0 + }, + "cursor": { + "wordIndex": 0, + "letterIndex": 0 + }, + "nextIndex": 0, + "words": [ + { + "letters": [ + { + "char": [55357, 56832], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [769], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + } + ] + }, + { + "target": { + "wordIndex": 0, + "letterIndex": 1 + }, + "cursor": { + "wordIndex": 0, + "letterIndex": 1 + }, + "nextIndex": 1, + "words": [ + { + "letters": [ + { + "char": [55357, 56832], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [769], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + } + ] + }, + { + "target": { + "wordIndex": 1, + "letterIndex": 1 + }, + "cursor": { + "wordIndex": 1, + "letterIndex": 1 + }, + "nextIndex": 5, + "words": [ + { + "letters": [ + { + "char": [55357, 56832], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [769], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + } + ] + } + ] + }, + { + "zen": true, + "words": [ + { + "letters": [ + { + "char": [55357, 56832], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [769], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + } + ], + "cursor": { + "wordIndex": 1, + "letterIndex": 2 + }, + "sounds": ["click", "click", "click", "click", "click", "click"], + "seeks": [ + { + "target": { + "wordIndex": 0, + "letterIndex": 0 + }, + "cursor": { + "wordIndex": 0, + "letterIndex": 0 + }, + "nextIndex": 0, + "words": [ + { + "letters": [ + { + "char": [55357, 56832], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [769], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + } + ] + }, + { + "target": { + "wordIndex": 0, + "letterIndex": 1 + }, + "cursor": { + "wordIndex": 0, + "letterIndex": 1 + }, + "nextIndex": 1, + "words": [ + { + "letters": [ + { + "char": [55357, 56832], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [101], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [769], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + } + ] + }, + { + "target": { + "wordIndex": 1, + "letterIndex": 1 + }, + "cursor": { + "wordIndex": 1, + "letterIndex": 1 + }, + "nextIndex": 5, + "words": [ + { + "letters": [ + { + "char": [55357, 56832], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [101], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [769], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + } + ] + } + ] + } + ], + "stats": { + "chars": { + "allCorrect": 5, + "correctWord": 5, + "incorrect": 0, + "extra": 0, + "missed": 0 + }, + "acc": 100, + "history": [ + [55357, 56832, 32], + [101, 769] + ], + "corrected": [ + [55357, 56832, 32], + [101, 769] + ], + "wpm": [], + "raw": [], + "burst": [900, null] + } + }, + { + "name": "isolated-targets-and-composition", + "log": { + "version": 1, + "events": [ + { + "type": "input", + "testMs": 0, + "data": { + "inputType": "insertText", + "data": [55296], + "correct": true, + "wordIndex": 0, + "charIndex": 0, + "inputValue": [55296] + } + }, + { + "type": "timer", + "testMs": 0, + "data": { + "event": "start", + "timer": 0, + "date": 0 + } + }, + { + "type": "input", + "testMs": 10, + "data": { + "inputType": "insertText", + "data": [32], + "correct": true, + "wordIndex": 0, + "charIndex": 1, + "inputValue": [55296, 32], + "commitsWord": true + } + }, + { + "type": "composition", + "testMs": 20, + "data": { + "event": "start", + "wordIndex": 1 + } + }, + { + "type": "composition", + "testMs": 21, + "data": { + "event": "update", + "data": [56320], + "wordIndex": 1 + } + }, + { + "type": "input", + "testMs": 30, + "data": { + "inputType": "insertText", + "data": [56320], + "correct": true, + "wordIndex": 1, + "charIndex": 0, + "inputValue": [56320], + "lastWord": true, + "isCompositionEnding": true + } + }, + { + "type": "timer", + "testMs": 30, + "data": { + "event": "end", + "timer": 0, + "date": 0 + } + }, + { + "type": "composition", + "testMs": 40, + "data": { + "event": "end", + "data": [56320], + "wordIndex": 1 + } + } + ], + "context": { + "targetWords": [[55296, 32], [56320]], + "mode": "words", + "mode2": "2", + "koreanStatus": false, + "bailedOut": false, + "isFunboxWithNospacePropertyActive": false + } + }, + "queries": [ + { + "op": "frame", + "time": -1 + }, + { + "op": "frame", + "time": 0 + }, + { + "op": "frame", + "time": 10 + }, + { + "op": "frame", + "time": 20 + }, + { + "op": "frame", + "time": 50 + }, + { + "op": "frame", + "time": 100 + }, + { + "op": "replay", + "zen": false + }, + { + "op": "replay", + "zen": true + } + ], + "expected": [ + { + "op": "frame", + "frame": { + "inputs": [], + "activeIndex": 0 + } + }, + { + "op": "frame", + "frame": { + "inputs": [[55296]], + "activeIndex": 0 + } + }, + { + "op": "frame", + "frame": { + "inputs": [[55296, 32]], + "activeIndex": 1 + } + }, + { + "op": "frame", + "frame": { + "inputs": [[55296, 32]], + "activeIndex": 1 + } + }, + { + "op": "frame", + "frame": { + "inputs": [[55296, 32], [56320]], + "activeIndex": 1 + } + }, + { + "op": "frame", + "frame": { + "inputs": [[55296, 32], [56320]], + "activeIndex": 1 + } + }, + { + "op": "replay", + "words": [[55296, 32], [56320]], + "steps": [ + { + "action": "correctLetter", + "value": [55296], + "time": 0 + }, + { + "action": "correctLetter", + "value": [32], + "time": 10 + }, + { + "action": "submitCorrectWord", + "time": 30 + }, + { + "action": "correctLetter", + "value": [56320], + "time": 30 + } + ], + "duration": 40 + }, + { + "op": "replay", + "words": [[55296, 32], [56320]], + "steps": [ + { + "action": "correctLetter", + "value": [55296], + "time": 0 + }, + { + "action": "correctLetter", + "value": [32], + "time": 10 + }, + { + "action": "submitCorrectWord", + "time": 30 + }, + { + "action": "correctLetter", + "value": [56320], + "time": 30 + } + ], + "duration": 40 + } + ], + "playback": [ + { + "zen": false, + "words": [ + { + "letters": [ + { + "char": [55296], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [56320], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + } + ], + "cursor": { + "wordIndex": 1, + "letterIndex": 1 + }, + "sounds": ["click", "click", "click", "click"], + "seeks": [ + { + "target": { + "wordIndex": 0, + "letterIndex": 0 + }, + "cursor": { + "wordIndex": 0, + "letterIndex": 0 + }, + "nextIndex": 0, + "words": [ + { + "letters": [ + { + "char": [55296], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [56320], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + } + ] + }, + { + "target": { + "wordIndex": 0, + "letterIndex": 1 + }, + "cursor": { + "wordIndex": 0, + "letterIndex": 1 + }, + "nextIndex": 1, + "words": [ + { + "letters": [ + { + "char": [55296], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [56320], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + } + ] + }, + { + "target": { + "wordIndex": 1, + "letterIndex": 1 + }, + "cursor": { + "wordIndex": 1, + "letterIndex": 1 + }, + "nextIndex": 4, + "words": [ + { + "letters": [ + { + "char": [55296], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [56320], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + } + ] + } + ] + }, + { + "zen": true, + "words": [ + { + "letters": [ + { + "char": [55296], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [56320], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + } + ], + "cursor": { + "wordIndex": 1, + "letterIndex": 1 + }, + "sounds": ["click", "click", "click", "click"], + "seeks": [ + { + "target": { + "wordIndex": 0, + "letterIndex": 0 + }, + "cursor": { + "wordIndex": 0, + "letterIndex": 0 + }, + "nextIndex": 0, + "words": [ + { + "letters": [ + { + "char": [55296], + "correct": false, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [56320], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + } + ] + }, + { + "target": { + "wordIndex": 0, + "letterIndex": 1 + }, + "cursor": { + "wordIndex": 0, + "letterIndex": 1 + }, + "nextIndex": 1, + "words": [ + { + "letters": [ + { + "char": [55296], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [56320], + "correct": false, + "incorrect": false, + "extra": false + } + ], + "error": false + } + ] + }, + { + "target": { + "wordIndex": 1, + "letterIndex": 1 + }, + "cursor": { + "wordIndex": 1, + "letterIndex": 1 + }, + "nextIndex": 4, + "words": [ + { + "letters": [ + { + "char": [55296], + "correct": true, + "incorrect": false, + "extra": false + }, + { + "char": [32], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + }, + { + "letters": [ + { + "char": [56320], + "correct": true, + "incorrect": false, + "extra": false + } + ], + "error": false + } + ] + } + ] + } + ], + "stats": { + "chars": { + "allCorrect": 3, + "correctWord": 3, + "incorrect": 0, + "extra": 0, + "missed": 0 + }, + "acc": 100, + "history": [[55296, 32], [56320]], + "corrected": [[55296, 32], [56320]], + "wpm": [], + "raw": [], + "burst": [2400, 2400] + } + } +] From f3f94b6b9094b024c75e20c1cfe089cee53abda8 Mon Sep 17 00:00:00 2001 From: Lakshmi Tanmay Date: Fri, 23 Oct 2026 09:42:49 +0530 Subject: [PATCH 66/93] Stage generated bindings before replacing live build outputs --- .../typing-engine/src/bin/export-bindings.rs | 9 ++++-- docs/rust-engine/progress.md | 2 ++ scripts/engine.ts | 30 ++++++++++++++++--- 3 files changed, 35 insertions(+), 6 deletions(-) diff --git a/crates/typing-engine/src/bin/export-bindings.rs b/crates/typing-engine/src/bin/export-bindings.rs index 3bd7b4ec3..3ba1631d2 100644 --- a/crates/typing-engine/src/bin/export-bindings.rs +++ b/crates/typing-engine/src/bin/export-bindings.rs @@ -5,8 +5,13 @@ use typing_engine::{ }; fn main() -> Result<(), Box> { - let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR")) - .join("../../packages/typing-engine/generated/types"); + let root = std::env::args_os() + .nth(1) + .map(std::path::PathBuf::from) + .unwrap_or_else(|| { + std::path::Path::new(env!("CARGO_MANIFEST_DIR")) + .join("../../packages/typing-engine/generated/types") + }); let config = ts_rs::Config::new().with_out_dir(root.clone()); std::fs::create_dir_all(root.parent().unwrap())?; std::fs::write( diff --git a/docs/rust-engine/progress.md b/docs/rust-engine/progress.md index 196d4c14c..ed0e08919 100644 --- a/docs/rust-engine/progress.md +++ b/docs/rust-engine/progress.md @@ -175,3 +175,5 @@ do not rerun completed checks without a new reason. - Final engine initialization distributions, long-session/restart memory and per-key performance budgets; full website startup/input/restart comparison belongs to Stage 2 after integration. - Current-head full regression/backend submission/hash checks, fresh-checkout bootstrap and complete CI evidence. - Stage 2/3 implementation and their two stacked PRs still pending. No deployment or release publication performed. + +- Fixed generated-output publication: build/optimization occurs in a staging directory, then complete files replace prior output; obsolete declarations are removed afterwards. Forced rebuild probe observed 2,000 polls with no missing prior Wasm/glue/types and verified obsolete-file removal. The following build is a verified cache hit. Build target directory is explicit. This avoids the prior long typechecking gap; normal build success alone is not a concurrent-build/process-lock claim. diff --git a/scripts/engine.ts b/scripts/engine.ts index 26f7a5257..19a46cb95 100644 --- a/scripts/engine.ts +++ b/scripts/engine.ts @@ -2,6 +2,7 @@ import { spawnSync } from "node:child_process"; import { existsSync, mkdirSync, + mkdtempSync, readFileSync, readdirSync, rmSync, @@ -108,18 +109,21 @@ switch (process.argv[2]) { "--locked", "--profile", "wasm", + "--target-dir", + resolve(root, "target"), "--target", "wasm32-unknown-unknown", "-p", "typing-engine-wasm", ]); - rmSync(output, { recursive: true, force: true }); - mkdirSync(output, { recursive: true }); + const staging = mkdtempSync( + resolve(root, "target/typing-engine-bindings-"), + ); run("wasm-bindgen", [ "--target", "web", "--out-dir", - output, + staging, "--out-name", "engine", resolve( @@ -130,7 +134,7 @@ switch (process.argv[2]) { const binaryen = dirname( createRequire(import.meta.url).resolve("binaryen/package.json"), ); - const wasm = resolve(output, "engine_bg.wasm"); + const wasm = resolve(staging, "engine_bg.wasm"); run(process.execPath, [ resolve(binaryen, "bin/wasm-opt"), wasm, @@ -151,7 +155,25 @@ switch (process.argv[2]) { "typing-engine", "--bin", "export-bindings", + "--", + resolve(staging, "types"), ]); + // Keep the prior declarations available throughout compilation/optimization. + // Publish only complete files, then remove obsolete generated output. + const produced = new Set( + files(staging).map((path) => relative(staging, path)), + ); + for (const path of produced) { + const destination = resolve(output, path); + mkdirSync(dirname(destination), { recursive: true }); + renameSync(resolve(staging, path), destination); + } + for (const path of files(output)) { + if (path !== cacheFile && !produced.has(relative(output, path))) { + rmSync(path); + } + } + rmSync(staging, { recursive: true, force: true }); const cache: BuildCache = { fingerprint: key, outputs: files(output) From 11b68864b46335f13b3e1cf2308489c8910f9788 Mon Sep 17 00:00:00 2001 From: Lakshmi Tanmay Date: Fri, 23 Oct 2026 09:46:04 +0530 Subject: [PATCH 67/93] Freeze quote and polyglot recovery messages, settings actions and notice timing --- docs/rust-engine/progress.md | 2 + .../scripts/capture-recovery-oracle.ts | 154 +++ packages/typing-fixtures/oracle/recovery.json | 1172 +++++++++++++++++ 3 files changed, 1328 insertions(+) create mode 100644 packages/typing-core/scripts/capture-recovery-oracle.ts create mode 100644 packages/typing-fixtures/oracle/recovery.json diff --git a/docs/rust-engine/progress.md b/docs/rust-engine/progress.md index ed0e08919..b791766d4 100644 --- a/docs/rust-engine/progress.md +++ b/docs/rust-engine/progress.md @@ -177,3 +177,5 @@ do not rerun completed checks without a new reason. - Stage 2/3 implementation and their two stacked PRs still pending. No deployment or release publication performed. - Fixed generated-output publication: build/optimization occurs in a staging directory, then complete files replace prior output; obsolete declarations are removed afterwards. Forced rebuild probe observed 2,000 polls with no missing prior Wasm/glue/types and verified obsolete-file removal. The following build is a verified cache hit. Build target directory is explicit. This avoids the prior long typechecking gap; normal build success alone is not a concurrent-build/process-lock claim. + +- Captured eight original TypeScript recovery paths once: absent quotes, selected/favorite IDs, empty length, zero/one polyglot languages and direction conflict. Fixture includes exact messages, ordered setting/funbox changes, no-save/no-restart flags and notice durations. Capture typechecks and refuses overwrite. Rust typed recovery port is next; no recovery gate claimed yet. diff --git a/packages/typing-core/scripts/capture-recovery-oracle.ts b/packages/typing-core/scripts/capture-recovery-oracle.ts new file mode 100644 index 000000000..dfb54ccba --- /dev/null +++ b/packages/typing-core/scripts/capture-recovery-oracle.ts @@ -0,0 +1,154 @@ +import { writeFileSync } from "node:fs"; +import { resolve } from "node:path"; +import type { Config } from "@oxytype/schemas/configs"; +import type { LanguageObject } from "@oxytype/schemas/languages"; +import { getDefaultConfig } from "../src/config/default-config"; +import { QuotesController } from "../src/quote-source"; +import { getQuoteWordList } from "../src/words-generator/quotes"; +import type { WordsGeneratorDeps } from "../src/words-generator/types"; +import { createFunboxWordFunctions } from "../src/funbox-word-functions"; +import { units } from "./rust-oracle"; + +const language: LanguageObject = { name: "english", words: ["one", "two"] }; +const references = []; +async function failure(action: () => Promise): Promise { + try { + await action(); + } catch (error) { + if (!(error instanceof Error)) throw error; + return error; + } + throw new Error("Expected source failure"); +} +for (const [caseName, lengths, empty, favorites] of [ + ["no-quotes", [0], true, []], + ["missing-selected", [-2], false, []], + ["no-favorites", [-3], false, []], + ["missing-favorite", [-3], false, [99]], + ["empty-length", [3], false, []], +] as const) { + const config = { + ...getDefaultConfig(), + mode: "quote" as const, + quoteLength: [...lengths], + }; + const asset = { + language: "english", + groups: [[0, 100]], + quotes: empty + ? [] + : [{ id: 1, text: "one two", length: 7, source: "fixture" }], + }; + const actions: unknown[] = []; + const deps: WordsGeneratorDeps = { + getConfig: () => config, + getActiveFunboxes: () => [], + customText: { + getText: () => [], + getMode: () => "repeat", + getLimitMode: () => "word", + getLimitValue: () => 0, + getPipeDelimiter: () => false, + }, + quotes: new QuotesController({ + fetchJson: async () => asset, + getSnapshot: () => ({ + favoriteQuotes: { english: favorites.map(String) }, + }), + }), + transforms: { + replaceAccents: (word) => word, + britishEnglish: async (word) => word, + englishPunctuation: { + check: async () => false, + replace: async (word) => word, + }, + }, + isRepeated: () => false, + isPractiseWordsActive: () => false, + getCurrentQuote: () => null, + setCurrentQuote: () => undefined, + getSelectedQuoteId: () => 99, + getWordsLength: () => 0, + setMode: (mode) => actions.push({ op: "setMode", mode }), + setQuoteLengthAll: () => actions.push({ op: "setQuoteLengthAll" }), + disableFunbox: () => undefined, + }; + { + const error = await failure(async () => + getQuoteWordList(deps, language, null, null), + ); + references.push({ + name: caseName, + input: { + config, + language: { ...language, words: language.words.map(units) }, + quotes: { + ...asset, + quotes: asset.quotes.map((quote) => ({ + ...quote, + text: units(quote.text), + })), + }, + favoriteQuoteIds: favorites, + selectedQuoteId: 99, + random: { kind: "seeded", state: 1 }, + }, + expected: { message: error.message, recovery: actions }, + }); + } +} +for (const [caseName, main, selected] of [ + ["no-polyglot", "english", []], + ["one-polyglot", "english", ["english"]], + ["polyglot-direction", "english", ["arabic", "hebrew"]], +] as const) { + const config: Config = { + ...getDefaultConfig(), + funbox: ["polyglot"], + language: main, + customPolyglot: [...selected], + }; + const languages: LanguageObject[] = selected.map((name) => ({ + name, + words: ["one", "two"], + rightToLeft: name !== "english", + })); + const actions: unknown[] = []; + const functions = createFunboxWordFunctions({ + getConfig: () => config, + getLanguage: async (name) => + languages.find((lang) => lang.name === name) ?? language, + getPoem: async () => false, + getSection: async () => false, + disableFunbox: (name, noRestart = false) => + actions.push({ op: "disableFunbox", name, noRestart }), + setLanguage: (nextLanguage, noSave = false) => + actions.push({ op: "setLanguage", language: nextLanguage, noSave }), + notify: (message, options) => + actions.push({ op: "notice", message, ...options }), + }); + { + const error = await failure(async () => + functions.polyglot?.withWords?.([]), + ); + references.push({ + name: caseName, + input: { + config, + language: { ...language, words: language.words.map(units) }, + polyglot: languages.map((lang) => ({ + ...lang, + words: lang.words.map(units), + })), + random: { kind: "seeded", state: 1 }, + }, + expected: { message: error.message, recovery: actions }, + }); + } +} +writeFileSync( + resolve(import.meta.dirname, "../../typing-fixtures/oracle/recovery.json"), + `${JSON.stringify(references, null, 2)}\n`, + { flag: "wx" }, +); diff --git a/packages/typing-fixtures/oracle/recovery.json b/packages/typing-fixtures/oracle/recovery.json new file mode 100644 index 000000000..b66e7880a --- /dev/null +++ b/packages/typing-fixtures/oracle/recovery.json @@ -0,0 +1,1172 @@ +[ + { + "name": "no-quotes", + "input": { + "config": { + "theme": "rust", + "themeLight": "serika", + "themeDark": "rust", + "autoSwitchTheme": false, + "customTheme": false, + "customThemeColors": [ + "#180d0d", + "#e8692e", + "#e8692e", + "#9c867a", + "#271616", + "#f3e7dc", + "#ff4d6d", + "#b8344f", + "#ff4d6d", + "#b8344f" + ], + "favThemes": [], + "showKeyTips": false, + "smoothCaret": "medium", + "codeUnindentOnBackspace": false, + "quickRestart": "off", + "punctuation": false, + "numbers": false, + "words": 50, + "time": 30, + "mode": "quote", + "quoteLength": [0], + "language": "english", + "fontSize": 2, + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "quickEnd": false, + "caretStyle": "default", + "paceCaretStyle": "default", + "flipTestColors": false, + "layout": "default", + "funbox": [], + "confidenceMode": "off", + "indicateTypos": "off", + "compositionDisplay": "replace", + "timerStyle": "mini", + "liveSpeedStyle": "off", + "liveAccStyle": "off", + "liveBurstStyle": "off", + "colorfulMode": false, + "randomTheme": "off", + "timerColor": "main", + "timerOpacity": "1", + "stopOnError": "off", + "deleteOnError": "off", + "showAllLines": false, + "keymapMode": "off", + "keymapStyle": "staggered", + "keymapLegendStyle": "lowercase", + "keymapLayout": "overrideSync", + "keymapKeys": "minimal", + "keymapSize": 1, + "fontFamily": "Roboto_Mono", + "smoothLineScroll": false, + "alwaysShowDecimalPlaces": false, + "alwaysShowWordsHistory": false, + "singleListCommandLine": "on", + "commandPaletteHotkey": "Mod+K", + "capsLockWarning": true, + "playSoundOnError": "off", + "playSoundOnClick": "off", + "soundVolume": 0.5, + "startGraphsAtZero": true, + "showOutOfFocusWarning": true, + "showTestModesNotice": true, + "paceCaret": "off", + "paceCaretCustomSpeed": 100, + "repeatedPace": true, + "accountChart": ["on", "on", "on", "on"], + "minWpm": "off", + "minWpmCustomSpeed": 100, + "highlightMode": "letter", + "typedEffect": "keep", + "typingSpeedUnit": "wpm", + "hideExtraLetters": false, + "strictSpace": false, + "minAcc": "off", + "minAccCustom": 90, + "monkey": false, + "repeatQuotes": "off", + "resultSaving": true, + "oppositeShiftMode": "off", + "customBackground": "", + "customBackgroundSize": "cover", + "customBackgroundFilter": [0, 1, 1, 1], + "customLayoutfluid": ["qwerty", "dvorak", "colemak"], + "customPolyglot": ["english", "spanish", "french", "german"], + "monkeyPowerLevel": "off", + "minBurst": "off", + "minBurstCustomSpeed": 100, + "burstHeatmap": false, + "britishEnglish": false, + "lazyMode": false, + "showAverage": "off", + "showPb": false, + "tapeMode": "off", + "tapeMargin": 50, + "maxLineWidth": 0, + "playTimeWarning": "off" + }, + "language": { + "name": "english", + "words": [ + [111, 110, 101], + [116, 119, 111] + ] + }, + "quotes": { + "language": "english", + "groups": [[0, 100]], + "quotes": [] + }, + "favoriteQuoteIds": [], + "selectedQuoteId": 99, + "random": { + "kind": "seeded", + "state": 1 + } + }, + "expected": { + "message": "No english quotes found", + "recovery": [ + { + "op": "setMode", + "mode": "words" + } + ] + } + }, + { + "name": "missing-selected", + "input": { + "config": { + "theme": "rust", + "themeLight": "serika", + "themeDark": "rust", + "autoSwitchTheme": false, + "customTheme": false, + "customThemeColors": [ + "#180d0d", + "#e8692e", + "#e8692e", + "#9c867a", + "#271616", + "#f3e7dc", + "#ff4d6d", + "#b8344f", + "#ff4d6d", + "#b8344f" + ], + "favThemes": [], + "showKeyTips": false, + "smoothCaret": "medium", + "codeUnindentOnBackspace": false, + "quickRestart": "off", + "punctuation": false, + "numbers": false, + "words": 50, + "time": 30, + "mode": "quote", + "quoteLength": [-2], + "language": "english", + "fontSize": 2, + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "quickEnd": false, + "caretStyle": "default", + "paceCaretStyle": "default", + "flipTestColors": false, + "layout": "default", + "funbox": [], + "confidenceMode": "off", + "indicateTypos": "off", + "compositionDisplay": "replace", + "timerStyle": "mini", + "liveSpeedStyle": "off", + "liveAccStyle": "off", + "liveBurstStyle": "off", + "colorfulMode": false, + "randomTheme": "off", + "timerColor": "main", + "timerOpacity": "1", + "stopOnError": "off", + "deleteOnError": "off", + "showAllLines": false, + "keymapMode": "off", + "keymapStyle": "staggered", + "keymapLegendStyle": "lowercase", + "keymapLayout": "overrideSync", + "keymapKeys": "minimal", + "keymapSize": 1, + "fontFamily": "Roboto_Mono", + "smoothLineScroll": false, + "alwaysShowDecimalPlaces": false, + "alwaysShowWordsHistory": false, + "singleListCommandLine": "on", + "commandPaletteHotkey": "Mod+K", + "capsLockWarning": true, + "playSoundOnError": "off", + "playSoundOnClick": "off", + "soundVolume": 0.5, + "startGraphsAtZero": true, + "showOutOfFocusWarning": true, + "showTestModesNotice": true, + "paceCaret": "off", + "paceCaretCustomSpeed": 100, + "repeatedPace": true, + "accountChart": ["on", "on", "on", "on"], + "minWpm": "off", + "minWpmCustomSpeed": 100, + "highlightMode": "letter", + "typedEffect": "keep", + "typingSpeedUnit": "wpm", + "hideExtraLetters": false, + "strictSpace": false, + "minAcc": "off", + "minAccCustom": 90, + "monkey": false, + "repeatQuotes": "off", + "resultSaving": true, + "oppositeShiftMode": "off", + "customBackground": "", + "customBackgroundSize": "cover", + "customBackgroundFilter": [0, 1, 1, 1], + "customLayoutfluid": ["qwerty", "dvorak", "colemak"], + "customPolyglot": ["english", "spanish", "french", "german"], + "monkeyPowerLevel": "off", + "minBurst": "off", + "minBurstCustomSpeed": 100, + "burstHeatmap": false, + "britishEnglish": false, + "lazyMode": false, + "showAverage": "off", + "showPb": false, + "tapeMode": "off", + "tapeMargin": 50, + "maxLineWidth": 0, + "playTimeWarning": "off" + }, + "language": { + "name": "english", + "words": [ + [111, 110, 101], + [116, 119, 111] + ] + }, + "quotes": { + "language": "english", + "groups": [[0, 100]], + "quotes": [ + { + "id": 1, + "text": [111, 110, 101, 32, 116, 119, 111], + "length": 7, + "source": "fixture" + } + ] + }, + "favoriteQuoteIds": [], + "selectedQuoteId": 99, + "random": { + "kind": "seeded", + "state": 1 + } + }, + "expected": { + "message": "Quote 99 does not exist", + "recovery": [ + { + "op": "setQuoteLengthAll" + } + ] + } + }, + { + "name": "no-favorites", + "input": { + "config": { + "theme": "rust", + "themeLight": "serika", + "themeDark": "rust", + "autoSwitchTheme": false, + "customTheme": false, + "customThemeColors": [ + "#180d0d", + "#e8692e", + "#e8692e", + "#9c867a", + "#271616", + "#f3e7dc", + "#ff4d6d", + "#b8344f", + "#ff4d6d", + "#b8344f" + ], + "favThemes": [], + "showKeyTips": false, + "smoothCaret": "medium", + "codeUnindentOnBackspace": false, + "quickRestart": "off", + "punctuation": false, + "numbers": false, + "words": 50, + "time": 30, + "mode": "quote", + "quoteLength": [-3], + "language": "english", + "fontSize": 2, + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "quickEnd": false, + "caretStyle": "default", + "paceCaretStyle": "default", + "flipTestColors": false, + "layout": "default", + "funbox": [], + "confidenceMode": "off", + "indicateTypos": "off", + "compositionDisplay": "replace", + "timerStyle": "mini", + "liveSpeedStyle": "off", + "liveAccStyle": "off", + "liveBurstStyle": "off", + "colorfulMode": false, + "randomTheme": "off", + "timerColor": "main", + "timerOpacity": "1", + "stopOnError": "off", + "deleteOnError": "off", + "showAllLines": false, + "keymapMode": "off", + "keymapStyle": "staggered", + "keymapLegendStyle": "lowercase", + "keymapLayout": "overrideSync", + "keymapKeys": "minimal", + "keymapSize": 1, + "fontFamily": "Roboto_Mono", + "smoothLineScroll": false, + "alwaysShowDecimalPlaces": false, + "alwaysShowWordsHistory": false, + "singleListCommandLine": "on", + "commandPaletteHotkey": "Mod+K", + "capsLockWarning": true, + "playSoundOnError": "off", + "playSoundOnClick": "off", + "soundVolume": 0.5, + "startGraphsAtZero": true, + "showOutOfFocusWarning": true, + "showTestModesNotice": true, + "paceCaret": "off", + "paceCaretCustomSpeed": 100, + "repeatedPace": true, + "accountChart": ["on", "on", "on", "on"], + "minWpm": "off", + "minWpmCustomSpeed": 100, + "highlightMode": "letter", + "typedEffect": "keep", + "typingSpeedUnit": "wpm", + "hideExtraLetters": false, + "strictSpace": false, + "minAcc": "off", + "minAccCustom": 90, + "monkey": false, + "repeatQuotes": "off", + "resultSaving": true, + "oppositeShiftMode": "off", + "customBackground": "", + "customBackgroundSize": "cover", + "customBackgroundFilter": [0, 1, 1, 1], + "customLayoutfluid": ["qwerty", "dvorak", "colemak"], + "customPolyglot": ["english", "spanish", "french", "german"], + "monkeyPowerLevel": "off", + "minBurst": "off", + "minBurstCustomSpeed": 100, + "burstHeatmap": false, + "britishEnglish": false, + "lazyMode": false, + "showAverage": "off", + "showPb": false, + "tapeMode": "off", + "tapeMargin": 50, + "maxLineWidth": 0, + "playTimeWarning": "off" + }, + "language": { + "name": "english", + "words": [ + [111, 110, 101], + [116, 119, 111] + ] + }, + "quotes": { + "language": "english", + "groups": [[0, 100]], + "quotes": [ + { + "id": 1, + "text": [111, 110, 101, 32, 116, 119, 111], + "length": 7, + "source": "fixture" + } + ] + }, + "favoriteQuoteIds": [], + "selectedQuoteId": 99, + "random": { + "kind": "seeded", + "state": 1 + } + }, + "expected": { + "message": "No favorite quotes found", + "recovery": [ + { + "op": "setQuoteLengthAll" + } + ] + } + }, + { + "name": "missing-favorite", + "input": { + "config": { + "theme": "rust", + "themeLight": "serika", + "themeDark": "rust", + "autoSwitchTheme": false, + "customTheme": false, + "customThemeColors": [ + "#180d0d", + "#e8692e", + "#e8692e", + "#9c867a", + "#271616", + "#f3e7dc", + "#ff4d6d", + "#b8344f", + "#ff4d6d", + "#b8344f" + ], + "favThemes": [], + "showKeyTips": false, + "smoothCaret": "medium", + "codeUnindentOnBackspace": false, + "quickRestart": "off", + "punctuation": false, + "numbers": false, + "words": 50, + "time": 30, + "mode": "quote", + "quoteLength": [-3], + "language": "english", + "fontSize": 2, + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "quickEnd": false, + "caretStyle": "default", + "paceCaretStyle": "default", + "flipTestColors": false, + "layout": "default", + "funbox": [], + "confidenceMode": "off", + "indicateTypos": "off", + "compositionDisplay": "replace", + "timerStyle": "mini", + "liveSpeedStyle": "off", + "liveAccStyle": "off", + "liveBurstStyle": "off", + "colorfulMode": false, + "randomTheme": "off", + "timerColor": "main", + "timerOpacity": "1", + "stopOnError": "off", + "deleteOnError": "off", + "showAllLines": false, + "keymapMode": "off", + "keymapStyle": "staggered", + "keymapLegendStyle": "lowercase", + "keymapLayout": "overrideSync", + "keymapKeys": "minimal", + "keymapSize": 1, + "fontFamily": "Roboto_Mono", + "smoothLineScroll": false, + "alwaysShowDecimalPlaces": false, + "alwaysShowWordsHistory": false, + "singleListCommandLine": "on", + "commandPaletteHotkey": "Mod+K", + "capsLockWarning": true, + "playSoundOnError": "off", + "playSoundOnClick": "off", + "soundVolume": 0.5, + "startGraphsAtZero": true, + "showOutOfFocusWarning": true, + "showTestModesNotice": true, + "paceCaret": "off", + "paceCaretCustomSpeed": 100, + "repeatedPace": true, + "accountChart": ["on", "on", "on", "on"], + "minWpm": "off", + "minWpmCustomSpeed": 100, + "highlightMode": "letter", + "typedEffect": "keep", + "typingSpeedUnit": "wpm", + "hideExtraLetters": false, + "strictSpace": false, + "minAcc": "off", + "minAccCustom": 90, + "monkey": false, + "repeatQuotes": "off", + "resultSaving": true, + "oppositeShiftMode": "off", + "customBackground": "", + "customBackgroundSize": "cover", + "customBackgroundFilter": [0, 1, 1, 1], + "customLayoutfluid": ["qwerty", "dvorak", "colemak"], + "customPolyglot": ["english", "spanish", "french", "german"], + "monkeyPowerLevel": "off", + "minBurst": "off", + "minBurstCustomSpeed": 100, + "burstHeatmap": false, + "britishEnglish": false, + "lazyMode": false, + "showAverage": "off", + "showPb": false, + "tapeMode": "off", + "tapeMargin": 50, + "maxLineWidth": 0, + "playTimeWarning": "off" + }, + "language": { + "name": "english", + "words": [ + [111, 110, 101], + [116, 119, 111] + ] + }, + "quotes": { + "language": "english", + "groups": [[0, 100]], + "quotes": [ + { + "id": 1, + "text": [111, 110, 101, 32, 116, 119, 111], + "length": 7, + "source": "fixture" + } + ] + }, + "favoriteQuoteIds": [99], + "selectedQuoteId": 99, + "random": { + "kind": "seeded", + "state": 1 + } + }, + "expected": { + "message": "No favorite quotes found", + "recovery": [ + { + "op": "setQuoteLengthAll" + } + ] + } + }, + { + "name": "empty-length", + "input": { + "config": { + "theme": "rust", + "themeLight": "serika", + "themeDark": "rust", + "autoSwitchTheme": false, + "customTheme": false, + "customThemeColors": [ + "#180d0d", + "#e8692e", + "#e8692e", + "#9c867a", + "#271616", + "#f3e7dc", + "#ff4d6d", + "#b8344f", + "#ff4d6d", + "#b8344f" + ], + "favThemes": [], + "showKeyTips": false, + "smoothCaret": "medium", + "codeUnindentOnBackspace": false, + "quickRestart": "off", + "punctuation": false, + "numbers": false, + "words": 50, + "time": 30, + "mode": "quote", + "quoteLength": [3], + "language": "english", + "fontSize": 2, + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "quickEnd": false, + "caretStyle": "default", + "paceCaretStyle": "default", + "flipTestColors": false, + "layout": "default", + "funbox": [], + "confidenceMode": "off", + "indicateTypos": "off", + "compositionDisplay": "replace", + "timerStyle": "mini", + "liveSpeedStyle": "off", + "liveAccStyle": "off", + "liveBurstStyle": "off", + "colorfulMode": false, + "randomTheme": "off", + "timerColor": "main", + "timerOpacity": "1", + "stopOnError": "off", + "deleteOnError": "off", + "showAllLines": false, + "keymapMode": "off", + "keymapStyle": "staggered", + "keymapLegendStyle": "lowercase", + "keymapLayout": "overrideSync", + "keymapKeys": "minimal", + "keymapSize": 1, + "fontFamily": "Roboto_Mono", + "smoothLineScroll": false, + "alwaysShowDecimalPlaces": false, + "alwaysShowWordsHistory": false, + "singleListCommandLine": "on", + "commandPaletteHotkey": "Mod+K", + "capsLockWarning": true, + "playSoundOnError": "off", + "playSoundOnClick": "off", + "soundVolume": 0.5, + "startGraphsAtZero": true, + "showOutOfFocusWarning": true, + "showTestModesNotice": true, + "paceCaret": "off", + "paceCaretCustomSpeed": 100, + "repeatedPace": true, + "accountChart": ["on", "on", "on", "on"], + "minWpm": "off", + "minWpmCustomSpeed": 100, + "highlightMode": "letter", + "typedEffect": "keep", + "typingSpeedUnit": "wpm", + "hideExtraLetters": false, + "strictSpace": false, + "minAcc": "off", + "minAccCustom": 90, + "monkey": false, + "repeatQuotes": "off", + "resultSaving": true, + "oppositeShiftMode": "off", + "customBackground": "", + "customBackgroundSize": "cover", + "customBackgroundFilter": [0, 1, 1, 1], + "customLayoutfluid": ["qwerty", "dvorak", "colemak"], + "customPolyglot": ["english", "spanish", "french", "german"], + "monkeyPowerLevel": "off", + "minBurst": "off", + "minBurstCustomSpeed": 100, + "burstHeatmap": false, + "britishEnglish": false, + "lazyMode": false, + "showAverage": "off", + "showPb": false, + "tapeMode": "off", + "tapeMargin": 50, + "maxLineWidth": 0, + "playTimeWarning": "off" + }, + "language": { + "name": "english", + "words": [ + [111, 110, 101], + [116, 119, 111] + ] + }, + "quotes": { + "language": "english", + "groups": [[0, 100]], + "quotes": [ + { + "id": 1, + "text": [111, 110, 101, 32, 116, 119, 111], + "length": 7, + "source": "fixture" + } + ] + }, + "favoriteQuoteIds": [], + "selectedQuoteId": 99, + "random": { + "kind": "seeded", + "state": 1 + } + }, + "expected": { + "message": "No quotes found for selected quote length", + "recovery": [ + { + "op": "setQuoteLengthAll" + } + ] + } + }, + { + "name": "no-polyglot", + "input": { + "config": { + "theme": "rust", + "themeLight": "serika", + "themeDark": "rust", + "autoSwitchTheme": false, + "customTheme": false, + "customThemeColors": [ + "#180d0d", + "#e8692e", + "#e8692e", + "#9c867a", + "#271616", + "#f3e7dc", + "#ff4d6d", + "#b8344f", + "#ff4d6d", + "#b8344f" + ], + "favThemes": [], + "showKeyTips": false, + "smoothCaret": "medium", + "codeUnindentOnBackspace": false, + "quickRestart": "off", + "punctuation": false, + "numbers": false, + "words": 50, + "time": 30, + "mode": "time", + "quoteLength": [1], + "language": "english", + "fontSize": 2, + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "quickEnd": false, + "caretStyle": "default", + "paceCaretStyle": "default", + "flipTestColors": false, + "layout": "default", + "funbox": ["polyglot"], + "confidenceMode": "off", + "indicateTypos": "off", + "compositionDisplay": "replace", + "timerStyle": "mini", + "liveSpeedStyle": "off", + "liveAccStyle": "off", + "liveBurstStyle": "off", + "colorfulMode": false, + "randomTheme": "off", + "timerColor": "main", + "timerOpacity": "1", + "stopOnError": "off", + "deleteOnError": "off", + "showAllLines": false, + "keymapMode": "off", + "keymapStyle": "staggered", + "keymapLegendStyle": "lowercase", + "keymapLayout": "overrideSync", + "keymapKeys": "minimal", + "keymapSize": 1, + "fontFamily": "Roboto_Mono", + "smoothLineScroll": false, + "alwaysShowDecimalPlaces": false, + "alwaysShowWordsHistory": false, + "singleListCommandLine": "on", + "commandPaletteHotkey": "Mod+K", + "capsLockWarning": true, + "playSoundOnError": "off", + "playSoundOnClick": "off", + "soundVolume": 0.5, + "startGraphsAtZero": true, + "showOutOfFocusWarning": true, + "showTestModesNotice": true, + "paceCaret": "off", + "paceCaretCustomSpeed": 100, + "repeatedPace": true, + "accountChart": ["on", "on", "on", "on"], + "minWpm": "off", + "minWpmCustomSpeed": 100, + "highlightMode": "letter", + "typedEffect": "keep", + "typingSpeedUnit": "wpm", + "hideExtraLetters": false, + "strictSpace": false, + "minAcc": "off", + "minAccCustom": 90, + "monkey": false, + "repeatQuotes": "off", + "resultSaving": true, + "oppositeShiftMode": "off", + "customBackground": "", + "customBackgroundSize": "cover", + "customBackgroundFilter": [0, 1, 1, 1], + "customLayoutfluid": ["qwerty", "dvorak", "colemak"], + "customPolyglot": [], + "monkeyPowerLevel": "off", + "minBurst": "off", + "minBurstCustomSpeed": 100, + "burstHeatmap": false, + "britishEnglish": false, + "lazyMode": false, + "showAverage": "off", + "showPb": false, + "tapeMode": "off", + "tapeMargin": 50, + "maxLineWidth": 0, + "playTimeWarning": "off" + }, + "language": { + "name": "english", + "words": [ + [111, 110, 101], + [116, 119, 111] + ] + }, + "polyglot": [], + "random": { + "kind": "seeded", + "state": 1 + } + }, + "expected": { + "message": "No valid languages found. Please check your polyglot languages config ().", + "recovery": [ + { + "op": "disableFunbox", + "name": "polyglot", + "noRestart": false + } + ] + } + }, + { + "name": "one-polyglot", + "input": { + "config": { + "theme": "rust", + "themeLight": "serika", + "themeDark": "rust", + "autoSwitchTheme": false, + "customTheme": false, + "customThemeColors": [ + "#180d0d", + "#e8692e", + "#e8692e", + "#9c867a", + "#271616", + "#f3e7dc", + "#ff4d6d", + "#b8344f", + "#ff4d6d", + "#b8344f" + ], + "favThemes": [], + "showKeyTips": false, + "smoothCaret": "medium", + "codeUnindentOnBackspace": false, + "quickRestart": "off", + "punctuation": false, + "numbers": false, + "words": 50, + "time": 30, + "mode": "time", + "quoteLength": [1], + "language": "english", + "fontSize": 2, + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "quickEnd": false, + "caretStyle": "default", + "paceCaretStyle": "default", + "flipTestColors": false, + "layout": "default", + "funbox": ["polyglot"], + "confidenceMode": "off", + "indicateTypos": "off", + "compositionDisplay": "replace", + "timerStyle": "mini", + "liveSpeedStyle": "off", + "liveAccStyle": "off", + "liveBurstStyle": "off", + "colorfulMode": false, + "randomTheme": "off", + "timerColor": "main", + "timerOpacity": "1", + "stopOnError": "off", + "deleteOnError": "off", + "showAllLines": false, + "keymapMode": "off", + "keymapStyle": "staggered", + "keymapLegendStyle": "lowercase", + "keymapLayout": "overrideSync", + "keymapKeys": "minimal", + "keymapSize": 1, + "fontFamily": "Roboto_Mono", + "smoothLineScroll": false, + "alwaysShowDecimalPlaces": false, + "alwaysShowWordsHistory": false, + "singleListCommandLine": "on", + "commandPaletteHotkey": "Mod+K", + "capsLockWarning": true, + "playSoundOnError": "off", + "playSoundOnClick": "off", + "soundVolume": 0.5, + "startGraphsAtZero": true, + "showOutOfFocusWarning": true, + "showTestModesNotice": true, + "paceCaret": "off", + "paceCaretCustomSpeed": 100, + "repeatedPace": true, + "accountChart": ["on", "on", "on", "on"], + "minWpm": "off", + "minWpmCustomSpeed": 100, + "highlightMode": "letter", + "typedEffect": "keep", + "typingSpeedUnit": "wpm", + "hideExtraLetters": false, + "strictSpace": false, + "minAcc": "off", + "minAccCustom": 90, + "monkey": false, + "repeatQuotes": "off", + "resultSaving": true, + "oppositeShiftMode": "off", + "customBackground": "", + "customBackgroundSize": "cover", + "customBackgroundFilter": [0, 1, 1, 1], + "customLayoutfluid": ["qwerty", "dvorak", "colemak"], + "customPolyglot": ["english"], + "monkeyPowerLevel": "off", + "minBurst": "off", + "minBurstCustomSpeed": 100, + "burstHeatmap": false, + "britishEnglish": false, + "lazyMode": false, + "showAverage": "off", + "showPb": false, + "tapeMode": "off", + "tapeMargin": 50, + "maxLineWidth": 0, + "playTimeWarning": "off" + }, + "language": { + "name": "english", + "words": [ + [111, 110, 101], + [116, 119, 111] + ] + }, + "polyglot": [ + { + "name": "english", + "words": [ + [111, 110, 101], + [116, 119, 111] + ], + "rightToLeft": false + } + ], + "random": { + "kind": "seeded", + "state": 1 + } + }, + "expected": { + "message": "", + "recovery": [ + { + "op": "setLanguage", + "language": "english", + "noSave": true + }, + { + "op": "disableFunbox", + "name": "polyglot", + "noRestart": true + }, + { + "op": "notice", + "message": "Disabled polyglot funbox because only one valid language was found. Check your polyglot languages config (english).", + "durationMs": 7000 + } + ] + } + }, + { + "name": "polyglot-direction", + "input": { + "config": { + "theme": "rust", + "themeLight": "serika", + "themeDark": "rust", + "autoSwitchTheme": false, + "customTheme": false, + "customThemeColors": [ + "#180d0d", + "#e8692e", + "#e8692e", + "#9c867a", + "#271616", + "#f3e7dc", + "#ff4d6d", + "#b8344f", + "#ff4d6d", + "#b8344f" + ], + "favThemes": [], + "showKeyTips": false, + "smoothCaret": "medium", + "codeUnindentOnBackspace": false, + "quickRestart": "off", + "punctuation": false, + "numbers": false, + "words": 50, + "time": 30, + "mode": "time", + "quoteLength": [1], + "language": "english", + "fontSize": 2, + "freedomMode": false, + "difficulty": "normal", + "blindMode": false, + "quickEnd": false, + "caretStyle": "default", + "paceCaretStyle": "default", + "flipTestColors": false, + "layout": "default", + "funbox": ["polyglot"], + "confidenceMode": "off", + "indicateTypos": "off", + "compositionDisplay": "replace", + "timerStyle": "mini", + "liveSpeedStyle": "off", + "liveAccStyle": "off", + "liveBurstStyle": "off", + "colorfulMode": false, + "randomTheme": "off", + "timerColor": "main", + "timerOpacity": "1", + "stopOnError": "off", + "deleteOnError": "off", + "showAllLines": false, + "keymapMode": "off", + "keymapStyle": "staggered", + "keymapLegendStyle": "lowercase", + "keymapLayout": "overrideSync", + "keymapKeys": "minimal", + "keymapSize": 1, + "fontFamily": "Roboto_Mono", + "smoothLineScroll": false, + "alwaysShowDecimalPlaces": false, + "alwaysShowWordsHistory": false, + "singleListCommandLine": "on", + "commandPaletteHotkey": "Mod+K", + "capsLockWarning": true, + "playSoundOnError": "off", + "playSoundOnClick": "off", + "soundVolume": 0.5, + "startGraphsAtZero": true, + "showOutOfFocusWarning": true, + "showTestModesNotice": true, + "paceCaret": "off", + "paceCaretCustomSpeed": 100, + "repeatedPace": true, + "accountChart": ["on", "on", "on", "on"], + "minWpm": "off", + "minWpmCustomSpeed": 100, + "highlightMode": "letter", + "typedEffect": "keep", + "typingSpeedUnit": "wpm", + "hideExtraLetters": false, + "strictSpace": false, + "minAcc": "off", + "minAccCustom": 90, + "monkey": false, + "repeatQuotes": "off", + "resultSaving": true, + "oppositeShiftMode": "off", + "customBackground": "", + "customBackgroundSize": "cover", + "customBackgroundFilter": [0, 1, 1, 1], + "customLayoutfluid": ["qwerty", "dvorak", "colemak"], + "customPolyglot": ["arabic", "hebrew"], + "monkeyPowerLevel": "off", + "minBurst": "off", + "minBurstCustomSpeed": 100, + "burstHeatmap": false, + "britishEnglish": false, + "lazyMode": false, + "showAverage": "off", + "showPb": false, + "tapeMode": "off", + "tapeMargin": 50, + "maxLineWidth": 0, + "playTimeWarning": "off" + }, + "language": { + "name": "english", + "words": [ + [111, 110, 101], + [116, 119, 111] + ] + }, + "polyglot": [ + { + "name": "arabic", + "words": [ + [111, 110, 101], + [116, 119, 111] + ], + "rightToLeft": true + }, + { + "name": "hebrew", + "words": [ + [111, 110, 101], + [116, 119, 111] + ], + "rightToLeft": true + } + ], + "random": { + "kind": "seeded", + "state": 1 + } + }, + "expected": { + "message": "", + "recovery": [ + { + "op": "setLanguage", + "language": "arabic", + "noSave": false + }, + { + "op": "notice", + "message": "Language direction conflict: switched to arabic for consistency.", + "durationMs": 5000 + } + ] + } + } +] From 3207490e10258e5732a717741a15d20822193e10 Mon Sep 17 00:00:00 2001 From: Lakshmi Tanmay Date: Fri, 23 Oct 2026 09:52:09 +0530 Subject: [PATCH 68/93] Return structured engine errors with frozen quote and polyglot recovery actions --- .../typing-engine/src/bin/export-bindings.rs | 1 + crates/typing-engine/src/content.rs | 15 ++- crates/typing-engine/src/error.rs | 96 ++++++++++++++ crates/typing-engine/src/generation.rs | 121 +++++++++++++++--- crates/typing-engine/src/lib.rs | 1 + crates/typing-engine/src/quotes.rs | 51 +++++++- crates/typing-engine/src/session.rs | 24 ++-- crates/typing-engine/tests/completion.rs | 2 +- crates/typing-engine/tests/recovery.rs | 63 +++++++++ docs/rust-engine/progress.md | 2 + 10 files changed, 337 insertions(+), 39 deletions(-) create mode 100644 crates/typing-engine/src/error.rs create mode 100644 crates/typing-engine/tests/recovery.rs diff --git a/crates/typing-engine/src/bin/export-bindings.rs b/crates/typing-engine/src/bin/export-bindings.rs index 3ba1631d2..4893e00dc 100644 --- a/crates/typing-engine/src/bin/export-bindings.rs +++ b/crates/typing-engine/src/bin/export-bindings.rs @@ -24,6 +24,7 @@ fn main() -> Result<(), Box> { )?; Command::export_all(&config)?; Config::export_all(&config)?; + typing_engine::error::EngineError::export_all(&config)?; State::export_all(&config)?; typing_engine::content::Request::export_all(&config)?; typing_engine::session::Preflight::export_all(&config)?; diff --git a/crates/typing-engine/src/content.rs b/crates/typing-engine/src/content.rs index 50338011f..ee8d131a3 100644 --- a/crates/typing-engine/src/content.rs +++ b/crates/typing-engine/src/content.rs @@ -31,7 +31,7 @@ impl Source { Self::Article { .. } => "wikipedia", } } - pub fn normalize(self, english: bool) -> Result { + pub fn normalize(self, english: bool) -> Result { match self { Self::Poem { title, @@ -174,7 +174,7 @@ impl Queue { ready: VecDeque::new(), } } - pub fn prefetch(&mut self, source: Source) -> Result<(), String> { + pub fn prefetch(&mut self, source: Source) -> Result<(), crate::error::EngineError> { if source.kind() != self.kind { return Err("Unexpected section source kind".into()); } @@ -202,9 +202,16 @@ impl Queue { self.pending = Some(request.clone()); request } - pub fn supply(&mut self, request: &Request, source: Source) -> Result<(), String> { + pub fn supply( + &mut self, + request: &Request, + source: Source, + ) -> Result<(), crate::error::EngineError> { if self.pending.as_ref() != Some(request) { - return Err("Stale or mismatched content request".into()); + return Err(crate::error::EngineError::new( + crate::error::ErrorCode::StaleRequest, + "Stale or mismatched content request", + )); } self.prefetch(source)?; self.pending = None; diff --git a/crates/typing-engine/src/error.rs b/crates/typing-engine/src/error.rs new file mode 100644 index 000000000..1f17feed3 --- /dev/null +++ b/crates/typing-engine/src/error.rs @@ -0,0 +1,96 @@ +use serde::{Deserialize, Serialize}; +use ts_rs::TS; + +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize, TS)] +#[serde(rename_all = "camelCase")] +pub enum ErrorCode { + InvalidInput, + InvalidConfig, + InvalidContent, + InvalidLog, + Disposed, + BindingMismatch, + ContentRequired, + StaleRequest, + NoQuotes, + SelectedQuoteMissing, + NoFavorites, + NoQuoteLength, + NoPolyglotLanguages, + OnePolyglotLanguage, + PolyglotDirection, + SourceUnavailable, +} +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize, TS)] +#[serde(tag = "op", rename_all = "camelCase")] +pub enum Recovery { + SetMode { + mode: String, + }, + SetQuoteLengthAll, + #[serde(rename_all = "camelCase")] + DisableFunbox { + name: String, + no_restart: bool, + }, + #[serde(rename_all = "camelCase")] + SetLanguage { + language: String, + no_save: bool, + }, + #[serde(rename_all = "camelCase")] + Notice { + message: String, + duration_ms: u32, + }, + Restart, +} +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize, TS)] +pub struct EngineError { + pub code: ErrorCode, + pub message: String, + pub recovery: Vec, +} +impl EngineError { + pub fn new(code: ErrorCode, message: impl Into) -> Self { + Self { + code, + message: message.into(), + recovery: vec![], + } + } + pub fn recover(code: ErrorCode, message: impl Into, recovery: Vec) -> Self { + Self { + code, + message: message.into(), + recovery, + } + } +} +impl From for EngineError { + fn from(message: String) -> Self { + Self::new(ErrorCode::InvalidInput, message) + } +} +impl From<&str> for EngineError { + fn from(message: &str) -> Self { + Self::from(message.to_owned()) + } +} +impl std::fmt::Display for EngineError { + fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.message.fmt(formatter) + } +} +impl std::error::Error for EngineError {} +impl std::ops::Deref for EngineError { + type Target = str; + fn deref(&self) -> &str { + &self.message + } +} +impl PartialEq<&str> for EngineError { + fn eq(&self, other: &&str) -> bool { + self.message == *other + } +} diff --git a/crates/typing-engine/src/generation.rs b/crates/typing-engine/src/generation.rs index 68d9440aa..411854c38 100644 --- a/crates/typing-engine/src/generation.rs +++ b/crates/typing-engine/src/generation.rs @@ -1,3 +1,4 @@ +use crate::error::{EngineError, ErrorCode, Recovery}; use crate::{Text, random::Random}; use serde::{Deserialize, Serialize}; use std::collections::VecDeque; @@ -163,7 +164,7 @@ impl Generator { language: Language, custom: Option, random: Random, - ) -> Result { + ) -> Result { Self::with_assets(config, language, custom, random, &mut Assets::default()) } pub fn with_assets( @@ -172,7 +173,7 @@ impl Generator { custom: Option, random: Random, assets: &mut Assets, - ) -> Result { + ) -> Result { Self::with_repeated_quote(config, language, custom, random, assets, None) } fn with_repeated_quote( @@ -182,8 +183,10 @@ impl Generator { mut random: Random, assets: &mut Assets, repeated_quote: Option, - ) -> Result { - config.validate()?; + ) -> Result { + config + .validate() + .map_err(|message| EngineError::new(ErrorCode::InvalidConfig, message))?; let section_queue = if let Some(name) = config.funbox.iter().find(|name| { crate::funboxes::get(name).is_some_and(|metadata| { metadata @@ -217,7 +220,28 @@ impl Generator { if let Some(asset) = assets.quote_asset.take() { assets .quotes - .load(asset, &config.quote_length, &mut random)?; + .load(asset, &config.quote_length, &mut random) + .map_err(|mut error| { + if error.code == ErrorCode::NoQuotes { + error.message = format!( + "No {} quotes found", + crate::text::base_language(&config.language).replace('_', " ") + ); + } + error + })?; + } + if assets.quotes.is_empty() { + return Err(EngineError::recover( + ErrorCode::NoQuotes, + format!( + "No {} quotes found", + crate::text::base_language(&config.language).replace('_', " ") + ), + vec![Recovery::SetMode { + mode: "words".into(), + }], + )); } Some(assets.quotes.select( &config.language, @@ -239,8 +263,41 @@ impl Generator { }; let mut word_languages = std::collections::BTreeMap::new(); let polyglot = if config.funbox.iter().any(|name| name == "polyglot") { - if assets.polyglot.len() < 2 { - return Err("Polyglot requires at least two valid languages".into()); + if assets.polyglot.is_empty() { + return Err(EngineError::recover( + ErrorCode::NoPolyglotLanguages, + format!( + "No valid languages found. Please check your polyglot languages config ({}).", + config.custom_polyglot.join(", ") + ), + vec![Recovery::DisableFunbox { + name: "polyglot".into(), + no_restart: false, + }], + )); + } + if assets.polyglot.len() == 1 { + return Err(EngineError::recover( + ErrorCode::OnePolyglotLanguage, + "", + vec![ + Recovery::SetLanguage { + language: assets.polyglot[0].name.clone(), + no_save: true, + }, + Recovery::DisableFunbox { + name: "polyglot".into(), + no_restart: true, + }, + Recovery::Notice { + message: format!( + "Disabled polyglot funbox because only one valid language was found. Check your polyglot languages config ({}).", + config.custom_polyglot.join(", ") + ), + duration_ms: 7000, + }, + ], + )); } let all_rtl = assets .polyglot @@ -253,7 +310,23 @@ impl Generator { if (language.right_to_left == Some(true) && all_ltr) || (language.right_to_left != Some(true) && all_rtl) { - return Err("Polyglot language direction conflict; update main language".into()); + let fallback = assets.polyglot[0].name.clone(); + return Err(EngineError::recover( + ErrorCode::PolyglotDirection, + "", + vec![ + Recovery::SetLanguage { + language: fallback.clone(), + no_save: false, + }, + Recovery::Notice { + message: format!( + "Language direction conflict: switched to {fallback} for consistency." + ), + duration_ms: 5000, + }, + ], + )); } wordset.clear(); for (index, lang) in assets.polyglot.iter().enumerate() { @@ -374,12 +447,12 @@ impl Generator { } false } - fn random_word(&mut self) -> Result { + fn random_word(&mut self) -> Result { if self.config.funbox.iter().any(|name| name == "pseudolang") { let generator = self .pseudolang .get_or_insert_with(|| crate::generated_funboxes::Pseudolang::new(&self.wordset)); - return generator.word(&mut self.random); + return generator.word(&mut self.random).map_err(Into::into); } let index = if self.config.funbox.iter().any(|name| name == "zipf") { self.random.zipf_index(self.wordset.len())? @@ -413,7 +486,7 @@ impl Generator { self.ordered += 1; word } - fn next_shuffled(&mut self) -> Result { + fn next_shuffled(&mut self) -> Result { if self.shuffled.is_empty() { self.shuffled = (0..self.wordset.len()).collect(); self.random.shuffle(&mut self.shuffled)?; @@ -429,7 +502,7 @@ impl Generator { &mut self, request: &crate::content::Request, source: crate::content::Source, - ) -> Result<(), String> { + ) -> Result<(), crate::error::EngineError> { self.section_queue .as_mut() .ok_or("Generator has no section source")? @@ -441,7 +514,7 @@ impl Generator { } self.batch = None; } - pub fn generate(&mut self, repeating: bool) -> Result { + pub fn generate(&mut self, repeating: bool) -> Result { let mut batch = if let Some(batch) = self.batch.take() { if batch.repeating != repeating { self.batch = Some(batch); @@ -531,7 +604,7 @@ impl Generator { bound: usize, previous: Option<&Text>, previous2: Option<&Text>, - ) -> Result { + ) -> Result { if self.repeating && self.config.mode != "quote" && let Some(next) = self.previous.get(index) @@ -559,7 +632,10 @@ impl Generator { && queue.is_empty() { queue.request(); - return Err("Section content required".into()); + return Err(EngineError::new( + ErrorCode::ContentRequired, + "Section content required", + )); } let mut word = self.random_word()?; let previous_original = previous.cloned(); @@ -844,10 +920,14 @@ impl Content { self.has_prepared = true; self.generator = None; } - pub fn prepare(&mut self, input: Prepare) -> Result<(), String> { + pub fn prepare(&mut self, input: Prepare) -> Result<(), crate::error::EngineError> { self.prepare_repeating(input, false) } - pub fn prepare_repeating(&mut self, input: Prepare, repeating: bool) -> Result<(), String> { + pub fn prepare_repeating( + &mut self, + input: Prepare, + repeating: bool, + ) -> Result<(), crate::error::EngineError> { if repeating && input.config.mode == "quote" && self.previous_quote.is_none() { return Err("Missing previous quote".into()); } @@ -885,14 +965,17 @@ impl Content { } Ok(()) } - pub fn start_session(&mut self, repeating: bool) -> Result { + pub fn start_session( + &mut self, + repeating: bool, + ) -> Result { let generated = self.generator()?.generate(repeating)?; let generator = self.generator.take().ok_or("Content not prepared")?; let mut session = crate::Session::from_generated(generator, generated)?; session.pace.remember(self.last_pace_wpm, false); Ok(session) } - pub fn generator(&mut self) -> Result<&mut Generator, String> { + pub fn generator(&mut self) -> Result<&mut Generator, crate::error::EngineError> { self.generator.as_mut().ok_or("Content not prepared".into()) } } diff --git a/crates/typing-engine/src/lib.rs b/crates/typing-engine/src/lib.rs index 52aabbd34..305350531 100644 --- a/crates/typing-engine/src/lib.rs +++ b/crates/typing-engine/src/lib.rs @@ -2,6 +2,7 @@ pub mod analysis; pub mod content; pub mod custom_progress; +pub mod error; pub mod funboxes; mod generated_funboxes; pub mod generation; diff --git a/crates/typing-engine/src/quotes.rs b/crates/typing-engine/src/quotes.rs index 774055533..c604dbe38 100644 --- a/crates/typing-engine/src/quotes.rs +++ b/crates/typing-engine/src/quotes.rs @@ -1,3 +1,4 @@ +use crate::error::{EngineError, ErrorCode, Recovery}; use crate::{Text, random::Random}; use serde::{Deserialize, Serialize}; use ts_rs::TS; @@ -34,17 +35,26 @@ pub struct Quotes { cursor: usize, } impl Quotes { + pub fn is_empty(&self) -> bool { + self.quotes.is_empty() + } pub fn load( &mut self, mut asset: QuoteAsset, lengths: &[i32], random: &mut Random, - ) -> Result<(), String> { + ) -> Result<(), crate::error::EngineError> { if self.language.as_ref() == Some(&asset.language) { return Ok(()); } if asset.quotes.is_empty() { - return Err("No quotes found".into()); + return Err(EngineError::recover( + ErrorCode::NoQuotes, + "No quotes found", + vec![Recovery::SetMode { + mode: "words".into(), + }], + )); } if asset.quotes.len() > 1_000_000 || asset @@ -79,7 +89,11 @@ impl Quotes { self.quotes = asset.quotes; self.update_queue(lengths, random) } - pub fn update_queue(&mut self, lengths: &[i32], random: &mut Random) -> Result<(), String> { + pub fn update_queue( + &mut self, + lengths: &[i32], + random: &mut Random, + ) -> Result<(), crate::error::EngineError> { self.queue.clear(); for group in lengths { if *group >= 0 @@ -100,23 +114,46 @@ impl Quotes { favorites: &[u32], british: bool, random: &mut Random, - ) -> Result { + ) -> Result { let mut quote = if lengths == [-2] { self.quotes .iter() .find(|q| q.id == selected) .cloned() - .ok_or("Selected quote does not exist")? + .ok_or_else(|| { + EngineError::recover( + ErrorCode::SelectedQuoteMissing, + format!("Quote {selected} does not exist"), + vec![Recovery::SetQuoteLengthAll], + ) + })? } else if lengths.contains(&-3) { + if favorites.is_empty() { + return Err(EngineError::recover( + ErrorCode::NoFavorites, + "No favorite quotes found", + vec![Recovery::SetQuoteLengthAll], + )); + } let id = favorites[random.index(favorites.len())?]; self.quotes .iter() .find(|q| q.id == id) .cloned() - .ok_or("Favorite quote does not exist")? + .ok_or_else(|| { + EngineError::recover( + ErrorCode::NoFavorites, + "No favorite quotes found", + vec![Recovery::SetQuoteLengthAll], + ) + })? } else { if self.queue.is_empty() { - return Err("No quotes found for selected length".into()); + return Err(EngineError::recover( + ErrorCode::NoQuoteLength, + "No quotes found for selected quote length", + vec![Recovery::SetQuoteLengthAll], + )); } if self.cursor >= self.queue.len() { self.cursor = 0; diff --git a/crates/typing-engine/src/session.rs b/crates/typing-engine/src/session.rs index 73d566aff..d735b255e 100644 --- a/crates/typing-engine/src/session.rs +++ b/crates/typing-engine/src/session.rs @@ -481,9 +481,10 @@ impl Session { pub fn pace_command( &mut self, command: crate::pace::PaceCommand, - ) -> Result { + ) -> Result { self.pace .command(command, &self.words, self.config.blind_mode) + .map_err(Into::into) } pub(crate) fn into_learning(self) -> Retained { let (learner, previous, quote) = @@ -503,8 +504,10 @@ impl Session { generation: self.generation, } } - pub fn new(config: Config, words: Vec) -> Result { - config.validate()?; + pub fn new(config: Config, words: Vec) -> Result { + config.validate().map_err(|message| { + crate::error::EngineError::new(crate::error::ErrorCode::InvalidConfig, message) + })?; if words.len() > 1_000_000 || words.iter().any(|word| word.len() > 1_000_000) { return Err("Content exceeds session bound".into()); } @@ -561,14 +564,14 @@ impl Session { pub fn from_generator( mut generator: crate::generation::Generator, repeating: bool, - ) -> Result { + ) -> Result { let generated = generator.generate(repeating)?; Self::from_generated(generator, generated) } pub fn from_generated( generator: crate::generation::Generator, generated: crate::generation::Generated, - ) -> Result { + ) -> Result { let mut session = Self::new(generator.config().clone(), generated.words)?; session.generation = generator.prepared_generation; session.sections = generated.section_indexes; @@ -651,7 +654,7 @@ impl Session { }); } } - fn refill(&mut self) -> Result<(), String> { + fn refill(&mut self) -> Result<(), crate::error::EngineError> { let bound = crate::funboxes::to_push(&self.config.funbox) .map_or(100, |count| count.saturating_sub(1)); if self.all_generated || self.words.len().saturating_sub(self.index + 1) > bound { @@ -753,7 +756,7 @@ impl Session { self.record("timer", now, json!({"event":"end", "timer":((now-self.start.unwrap_or(now))/1000.0).floor(), "date":date})); } - pub fn apply(&mut self, command: Command) -> Result { + pub fn apply(&mut self, command: Command) -> Result { let now = match &command { Command::Insert { now, .. } | Command::Delete { now, .. } @@ -1541,7 +1544,12 @@ impl Session { } } - fn observe(&mut self, kind: &str, now: f64, mut data: Value) -> Result<(), String> { + fn observe( + &mut self, + kind: &str, + now: f64, + mut data: Value, + ) -> Result<(), crate::error::EngineError> { if !data.is_object() { return Err("Observation data must be an object".into()); } diff --git a/crates/typing-engine/tests/completion.rs b/crates/typing-engine/tests/completion.rs index b6c4bd97e..4989b8d86 100644 --- a/crates/typing-engine/tests/completion.rs +++ b/crates/typing-engine/tests/completion.rs @@ -3,7 +3,7 @@ use typing_engine::{Config, Session, Text}; fn command( session: &mut Session, value: serde_json::Value, -) -> Result { +) -> Result { session.apply(serde_json::from_value(value).unwrap()) } fn session() -> Session { diff --git a/crates/typing-engine/tests/recovery.rs b/crates/typing-engine/tests/recovery.rs new file mode 100644 index 000000000..430ffd528 --- /dev/null +++ b/crates/typing-engine/tests/recovery.rs @@ -0,0 +1,63 @@ +use serde_json::{Value, json}; +use typing_engine::{ + error::ErrorCode, + generation::{Content, Prepare}, +}; +#[test] +fn recovery_actions_and_messages_match_every_frozen_typescript_path() { + let path = std::path::Path::new(env!("CARGO_MANIFEST_DIR")) + .join("../../packages/typing-fixtures/oracle/recovery.json"); + let references: Vec = serde_json::from_slice(&std::fs::read(path).unwrap()).unwrap(); + for reference in references { + let mut owner = Content::default(); + let input: Prepare = serde_json::from_value(reference["input"].clone()).unwrap(); + let error = owner.prepare(input).unwrap_err(); + assert_eq!( + json!({"message":error.message,"recovery":error.recovery}), + reference["expected"], + "{}", + reference["name"] + ); + assert_ne!(error.code, ErrorCode::InvalidInput); + // A corrected preparation must remain usable after every error. + let input = serde_json::from_value(json!({ + "config":{"mode":"words","words":2},"language":{"name":"english","words":[[97],[98]]}, + "random":{"kind":"seeded","state":1} + })) + .unwrap(); + owner.prepare(input).unwrap(); + assert_eq!( + owner.start_session(false).unwrap().snapshot().words.len(), + 2 + ); + } +} +#[test] +fn configuration_and_stale_reply_failures_have_codes_without_recovery_side_effects() { + let error = typing_engine::Session::new( + typing_engine::Config { + mode: "invalid".into(), + ..Default::default() + }, + vec![], + ) + .err() + .unwrap(); + assert_eq!(error.code, ErrorCode::InvalidConfig); + assert!(error.recovery.is_empty()); + let mut queue = typing_engine::content::Queue::new("poetry".into(), "english".into(), true); + let request = queue.request(); + queue.reset(1); + let error = queue + .supply( + &request, + typing_engine::content::Source::Poem { + title: Default::default(), + author: Default::default(), + lines: vec![], + }, + ) + .unwrap_err(); + assert_eq!(error.code, ErrorCode::StaleRequest); + assert!(error.recovery.is_empty()); +} diff --git a/docs/rust-engine/progress.md b/docs/rust-engine/progress.md index b791766d4..9673896cc 100644 --- a/docs/rust-engine/progress.md +++ b/docs/rust-engine/progress.md @@ -179,3 +179,5 @@ do not rerun completed checks without a new reason. - Fixed generated-output publication: build/optimization occurs in a staging directory, then complete files replace prior output; obsolete declarations are removed afterwards. Forced rebuild probe observed 2,000 polls with no missing prior Wasm/glue/types and verified obsolete-file removal. The following build is a verified cache hit. Build target directory is explicit. This avoids the prior long typechecking gap; normal build success alone is not a concurrent-build/process-lock claim. - Captured eight original TypeScript recovery paths once: absent quotes, selected/favorite IDs, empty length, zero/one polyglot languages and direction conflict. Fixture includes exact messages, ordered setting/funbox changes, no-save/no-restart flags and notice durations. Capture typechecks and refuses overwrite. Rust typed recovery port is next; no recovery gate claimed yet. + +- Shared public session/content/generation operations now return structured EngineError values with codes and ordered typed recovery actions. All eight frozen quote/polyglot recovery messages/actions match natively, including empty-message notices and no-save/no-restart flags. Invalid config and stale replies have explicit codes; corrected preparation succeeds after every captured error. Full native suite and Clippy pass. Wasm currently preserves old error messages only; structured facade and failed-source lifecycle remain pending. From 8928bba3d4ac041f1ebb706346e93689a938906f Mon Sep 17 00:00:00 2001 From: Lakshmi Tanmay Date: Fri, 23 Oct 2026 10:01:52 +0530 Subject: [PATCH 69/93] feat(engine): expose structured Wasm recovery failures --- Cargo.lock | 1 + crates/typing-engine-wasm/Cargo.toml | 1 + crates/typing-engine-wasm/src/lib.rs | 163 ++++++++------- docs/rust-engine/contract.md | 4 + docs/rust-engine/progress.md | 2 + frontend/e2e/engine-proof.migration.ts | 68 +++++++ frontend/src/routes/engine-proof.tsx | 88 ++++++++ .../typing-engine/__test__/failure.spec.ts | 44 ++++ packages/typing-engine/package.json | 3 +- packages/typing-engine/src/browser.client.ts | 190 +++++++++++------- packages/typing-engine/src/failure.ts | 54 +++++ 11 files changed, 466 insertions(+), 152 deletions(-) create mode 100644 packages/typing-engine/__test__/failure.spec.ts create mode 100644 packages/typing-engine/src/failure.ts diff --git a/Cargo.lock b/Cargo.lock index 4ec184744..2e6b37e02 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -216,6 +216,7 @@ dependencies = [ name = "typing-engine-wasm" version = "0.1.0" dependencies = [ + "serde", "serde_json", "typing-engine", "wasm-bindgen", diff --git a/crates/typing-engine-wasm/Cargo.toml b/crates/typing-engine-wasm/Cargo.toml index f2de450d1..a1b816600 100644 --- a/crates/typing-engine-wasm/Cargo.toml +++ b/crates/typing-engine-wasm/Cargo.toml @@ -12,6 +12,7 @@ crate-type = ["cdylib", "rlib"] [dependencies] typing-engine = { path = "../typing-engine" } serde_json.workspace = true +serde.workspace = true wasm-bindgen.workspace = true [lints] diff --git a/crates/typing-engine-wasm/src/lib.rs b/crates/typing-engine-wasm/src/lib.rs index 7d3677d5e..65998679d 100644 --- a/crates/typing-engine-wasm/src/lib.rs +++ b/crates/typing-engine-wasm/src/lib.rs @@ -1,6 +1,22 @@ +use typing_engine::error::{EngineError, ErrorCode}; use typing_engine::{BINDING_VERSION, Config, Session, Text}; use wasm_bindgen::prelude::*; +fn bind_error(error: EngineError) -> JsError { + let payload = serde_json::to_string(&error) + .expect("Engine errors contain serializable strings and actions"); + JsError::new(&payload) +} +fn failure(code: ErrorCode, message: &str) -> JsError { + bind_error(EngineError::new(code, message)) +} +fn parse<'a, T: serde::Deserialize<'a>>(source: &'a str) -> Result { + serde_json::from_str(source).map_err(|error| bind_error(EngineError::from(error.to_string()))) +} +fn serialize(value: &T) -> Result { + serde_json::to_string(value).map_err(|error| bind_error(EngineError::from(error.to_string()))) +} + #[wasm_bindgen] pub fn binding_version() -> u32 { BINDING_VERSION @@ -17,67 +33,64 @@ impl WasmSession { #[wasm_bindgen(constructor)] pub fn new(config: &str, words: &str, version: u32) -> Result { if version != BINDING_VERSION { - return Err(JsError::new("Binding version mismatch")); + return Err(failure( + ErrorCode::BindingMismatch, + "Binding version mismatch", + )); } - let config: Config = serde_json::from_str(config)?; - let words: Vec = serde_json::from_str(words)?; + let config: Config = parse(config)?; + let words: Vec = parse(words)?; Ok(Self { - session: Some(Session::new(config, words).map_err(|message| JsError::new(&message))?), + session: Some(Session::new(config, words).map_err(bind_error)?), }) } pub fn command(&mut self, command: &str) -> Result { - let command = serde_json::from_str(command)?; + let command = parse(command)?; let session = self .session .as_mut() - .ok_or_else(|| JsError::new("Session disposed"))?; - let update = session - .apply(command) - .map_err(|message| JsError::new(&message))?; - Ok(serde_json::to_string(&update)?) + .ok_or_else(|| failure(ErrorCode::Disposed, "Session disposed"))?; + let update = session.apply(command).map_err(bind_error)?; + serialize(&update) } pub fn preflight(&self, data: &str, layout: &str) -> Result { - let data = serde_json::from_str(data)?; - let layout = serde_json::from_str(layout)?; + let data = parse(data)?; + let layout = parse(layout)?; let session = self .session .as_ref() - .ok_or_else(|| JsError::new("Session disposed"))?; - Ok(serde_json::to_string(&session.preflight(data, &layout))?) + .ok_or_else(|| failure(ErrorCode::Disposed, "Session disposed"))?; + serialize(&session.preflight(data, &layout)) } pub fn pace(&mut self, command: &str) -> Result { - let command = serde_json::from_str(command)?; + let command = parse(command)?; let session = self .session .as_mut() - .ok_or_else(|| JsError::new("Session disposed"))?; - Ok(serde_json::to_string( - &session - .pace_command(command) - .map_err(|message| JsError::new(&message))?, - )?) + .ok_or_else(|| failure(ErrorCode::Disposed, "Session disposed"))?; + serialize(&session.pace_command(command).map_err(bind_error)?) } pub fn restart_policy(&self, options: &str) -> Result { - let options = serde_json::from_str(options)?; + let options = parse(options)?; let session = self .session .as_ref() - .ok_or_else(|| JsError::new("Session disposed"))?; - Ok(serde_json::to_string(&session.restart_policy(options))?) + .ok_or_else(|| failure(ErrorCode::Disposed, "Session disposed"))?; + serialize(&session.restart_policy(options)) } pub fn snapshot(&self) -> Result { let session = self .session .as_ref() - .ok_or_else(|| JsError::new("Session disposed"))?; - Ok(serde_json::to_string(&session.snapshot())?) + .ok_or_else(|| failure(ErrorCode::Disposed, "Session disposed"))?; + serialize(&session.snapshot()) } pub fn log(&self) -> Result { let session = self .session .as_ref() - .ok_or_else(|| JsError::new("Session disposed"))?; - Ok(serde_json::to_string(&session.log())?) + .ok_or_else(|| failure(ErrorCode::Disposed, "Session disposed"))?; + serialize(&session.log()) } pub fn dispose(&mut self) { self.session = None; @@ -94,42 +107,43 @@ impl WasmContent { let content = self .content .as_mut() - .ok_or_else(|| JsError::new("Content disposed"))?; + .ok_or_else(|| failure(ErrorCode::Disposed, "Content disposed"))?; let session = session .session .take() - .ok_or_else(|| JsError::new("Session disposed"))?; + .ok_or_else(|| failure(ErrorCode::Disposed, "Session disposed"))?; content.recycle_session(session); Ok(()) } #[wasm_bindgen(constructor)] pub fn new(version: u32) -> Result { if version != BINDING_VERSION { - return Err(JsError::new("Binding version mismatch")); + return Err(failure( + ErrorCode::BindingMismatch, + "Binding version mismatch", + )); } Ok(Self { content: Some(typing_engine::generation::Content::default()), }) } pub fn prepare(&mut self, input: &str, repeating: bool) -> Result<(), JsError> { - let input = serde_json::from_str(input)?; + let input = parse(input)?; self.content .as_mut() - .ok_or_else(|| JsError::new("Content disposed"))? + .ok_or_else(|| failure(ErrorCode::Disposed, "Content disposed"))? .prepare_repeating(input, repeating) - .map_err(|message| JsError::new(&message)) + .map_err(bind_error) } pub fn generate(&mut self, repeating: bool) -> Result { let generator = self .content .as_mut() - .ok_or_else(|| JsError::new("Content disposed"))? + .ok_or_else(|| failure(ErrorCode::Disposed, "Content disposed"))? .generator() - .map_err(|message| JsError::new(&message))?; - let generated = generator - .generate(repeating) - .map_err(|message| JsError::new(&message))?; - Ok(serde_json::to_string(&generated)?) + .map_err(bind_error)?; + let generated = generator.generate(repeating).map_err(bind_error)?; + serialize(&generated) } pub fn next( &mut self, @@ -138,57 +152,57 @@ impl WasmContent { previous: &str, previous2: &str, ) -> Result { - let previous: Option = serde_json::from_str(previous)?; - let previous2: Option = serde_json::from_str(previous2)?; + let previous: Option = parse(previous)?; + let previous2: Option = parse(previous2)?; let generator = self .content .as_mut() - .ok_or_else(|| JsError::new("Content disposed"))? + .ok_or_else(|| failure(ErrorCode::Disposed, "Content disposed"))? .generator() - .map_err(|message| JsError::new(&message))?; + .map_err(bind_error)?; let next = generator .next(index, bound, previous.as_ref(), previous2.as_ref()) - .map_err(|message| JsError::new(&message))?; - Ok(serde_json::to_string(&next)?) + .map_err(bind_error)?; + serialize(&next) } pub fn metadata(&mut self) -> Result { let generator = self .content .as_mut() - .ok_or_else(|| JsError::new("Content disposed"))? + .ok_or_else(|| failure(ErrorCode::Disposed, "Content disposed"))? .generator() - .map_err(|message| JsError::new(&message))?; - Ok(serde_json::to_string( + .map_err(bind_error)?; + serialize( &serde_json::json!({ "allWordsGenerated": generator.all_generated(), "currentQuote": generator.current_quote }), - )?) + ) } pub fn request(&mut self) -> Result { let generator = self .content .as_mut() - .ok_or_else(|| JsError::new("Content disposed"))? + .ok_or_else(|| failure(ErrorCode::Disposed, "Content disposed"))? .generator() - .map_err(|message| JsError::new(&message))?; - Ok(serde_json::to_string(&generator.pending_request())?) + .map_err(bind_error)?; + serialize(&generator.pending_request()) } pub fn supply(&mut self, request: &str, source: &str) -> Result<(), JsError> { - let request = serde_json::from_str(request)?; - let source = serde_json::from_str(source)?; + let request = parse(request)?; + let source = parse(source)?; self.content .as_mut() - .ok_or_else(|| JsError::new("Content disposed"))? + .ok_or_else(|| failure(ErrorCode::Disposed, "Content disposed"))? .generator() - .map_err(|message| JsError::new(&message))? + .map_err(bind_error)? .supply(&request, source) - .map_err(|message| JsError::new(&message)) + .map_err(bind_error) } pub fn session(&mut self, repeating: bool) -> Result { let session = self .content .as_mut() - .ok_or_else(|| JsError::new("Content disposed"))? + .ok_or_else(|| failure(ErrorCode::Disposed, "Content disposed"))? .start_session(repeating) - .map_err(|message| JsError::new(&message))?; + .map_err(bind_error)?; Ok(WasmSession { session: Some(session), }) @@ -200,11 +214,9 @@ impl WasmContent { #[wasm_bindgen] pub fn analyze(log: &str, query: &str) -> Result { - let log = serde_json::from_str(log)?; - let query = serde_json::from_str(query)?; - Ok(serde_json::to_string(&typing_engine::analysis::analyze( - &log, query, - ))?) + let log = parse(log)?; + let query = parse(query)?; + serialize(&typing_engine::analysis::analyze(&log, query)) } #[wasm_bindgen] @@ -216,9 +228,12 @@ impl WasmReplay { #[wasm_bindgen(constructor)] pub fn new(log: &str, zen: bool, version: u32) -> Result { if version != BINDING_VERSION { - return Err(JsError::new("Binding version mismatch")); + return Err(failure( + ErrorCode::BindingMismatch, + "Binding version mismatch", + )); } - let log = serde_json::from_str(log)?; + let log = parse(log)?; Ok(Self { playback: Some(typing_engine::replay::Playback::new(&log, zen)), }) @@ -227,23 +242,23 @@ impl WasmReplay { let playback = self .playback .as_ref() - .ok_or_else(|| JsError::new("Replay disposed"))?; - Ok(serde_json::to_string(&playback.snapshot())?) + .ok_or_else(|| failure(ErrorCode::Disposed, "Replay disposed"))?; + serialize(&playback.snapshot()) } pub fn step(&mut self) -> Result { let playback = self .playback .as_mut() - .ok_or_else(|| JsError::new("Replay disposed"))?; - Ok(serde_json::to_string(&playback.step())?) + .ok_or_else(|| failure(ErrorCode::Disposed, "Replay disposed"))?; + serialize(&playback.step()) } pub fn seek(&mut self, cursor: &str) -> Result { - let cursor = serde_json::from_str(cursor)?; + let cursor = parse(cursor)?; let playback = self .playback .as_mut() - .ok_or_else(|| JsError::new("Replay disposed"))?; - Ok(serde_json::to_string(&playback.seek(cursor))?) + .ok_or_else(|| failure(ErrorCode::Disposed, "Replay disposed"))?; + serialize(&playback.seek(cursor)) } pub fn dispose(&mut self) { self.playback = None; diff --git a/docs/rust-engine/contract.md b/docs/rust-engine/contract.md index 2b9737657..91fda8aac 100644 --- a/docs/rust-engine/contract.md +++ b/docs/rust-engine/contract.md @@ -131,3 +131,7 @@ overwrite files. Existing snapshots are never updated by the harness. An app owns `Content` across test replacements. Call `recycle_session` before preparing a replacement to consume the old session and retain only weak-spot learning and remembered pace WPM. Its log, input, pending content and completion state are dropped; the consumed handle rejects subsequent calls. Independent content owners retain independent learning. Explicit disposal drops an owner entirely. Pace commands are synchronous; Rust uses bare UTF-16 word lengths for caret stepping and full targets for mistake correction. Hosts supply speed selection from account/PB data and schedule/render steps. Content preparation advances an owner-local generation. Recycling takes the maximum of the owner's generation and the consumed session's reset generation; the next preparation advances it. Old content/automatic tokens cannot match a replacement session. Owner instances route their own replies. Explicit repeated preparation retains generated words and quote identity; quote repetition does not advance the selection queue. + +### Structured failures + +Native operations return EngineError with a stable code, display message and ordered Recovery actions. Wasm carries this DTO as JSON in its private generated exception message; the public browser facade decodes EngineFailure. Consumers inspect code/actions, never parse human wording. Empty messages are intentional for recovery paths that supply a notice. Corrected content preparation may retry after validation/recovery failures. Disposal and ABI mismatch are explicit failures; no session becomes usable after failed initialization. diff --git a/docs/rust-engine/progress.md b/docs/rust-engine/progress.md index 9673896cc..bf07d6105 100644 --- a/docs/rust-engine/progress.md +++ b/docs/rust-engine/progress.md @@ -181,3 +181,5 @@ do not rerun completed checks without a new reason. - Captured eight original TypeScript recovery paths once: absent quotes, selected/favorite IDs, empty length, zero/one polyglot languages and direction conflict. Fixture includes exact messages, ordered setting/funbox changes, no-save/no-restart flags and notice durations. Capture typechecks and refuses overwrite. Rust typed recovery port is next; no recovery gate claimed yet. - Shared public session/content/generation operations now return structured EngineError values with codes and ordered typed recovery actions. All eight frozen quote/polyglot recovery messages/actions match natively, including empty-message notices and no-save/no-restart flags. Invalid config and stale replies have explicit codes; corrected preparation succeeds after every captured error. Full native suite and Clippy pass. Wasm currently preserves old error messages only; structured facade and failed-source lifecycle remain pending. + +- Wasm now transports the native structured error DTO; browser EngineFailure exposes code/actions and preserves cause. Hosts never infer recovery from message text. The built Cloudflare proof passes all 13 engine-proof tests (26.9 s), including all eight frozen recovery cases, invalid UTF-16 command rejection without mutation, disposal and an injected ABI mismatch that prevents usable sessions. Two facade unit tests and type-aware lint pass. Clippy initially caught redundant result wrappers; removed them and reran successfully. Wasm: 696,520 raw / 278,194 gzip level 6 (Vite 280.38 kB). Other migration files were not part of this targeted run. diff --git a/frontend/e2e/engine-proof.migration.ts b/frontend/e2e/engine-proof.migration.ts index facfd98b2..e470167fe 100644 --- a/frontend/e2e/engine-proof.migration.ts +++ b/frontend/e2e/engine-proof.migration.ts @@ -921,3 +921,71 @@ test("historical surrogate halves cross browser Wasm without lossy strings", asy ]); } }); + +test("Wasm exposes frozen recovery actions and structured validation/disposal errors", async ({ + page, +}) => { + const references = JSON.parse( + await readFile(resolve(root, "oracle/recovery.json"), "utf8"), + ) as { + expected: { message: string; recovery: unknown[] }; + }[]; + const codes = [ + "noQuotes", + "selectedQuoteMissing", + "noFavorites", + "noFavorites", + "noQuoteLength", + "noPolyglotLanguages", + "onePolyglotLanguage", + "polyglotDirection", + ]; + await page.goto("/engine-proof"); + await page + .getByRole("textbox", { name: "Command fixture" }) + .fill(JSON.stringify(references)); + await page.getByRole("button", { name: "Run recovery", exact: true }).click(); + await expect(page.getByTestId("engine-proof-result")).not.toBeEmpty(); + await expect(page.getByTestId("engine-proof-error")).toBeEmpty(); + expect( + JSON.parse(await page.getByTestId("engine-proof-result").innerText()), + ).toEqual( + references.map((reference, index) => ({ + ...reference.expected, + code: codes[index], + recovered: true, + invalidCode: "invalidInput", + unchanged: true, + disposedCode: "disposed", + })), + ); +}); + +test("binding version mismatch prevents creating a usable browser session", async ({ + page, +}) => { + await page.addInitScript({ + content: ` + const NativeInstance = WebAssembly.Instance; + WebAssembly.Instance = new Proxy(NativeInstance, { + construct(target, args) { + const instance = Reflect.construct(target, args); + return { exports: { ...instance.exports, binding_version: () => 999 } }; + } + }); + `, + }); + await page.goto("/engine-proof"); + await page + .getByRole("textbox", { name: "Command fixture" }) + .fill(await readFile(resolve(root, "commands/finite-edit.json"), "utf8")); + await page.getByRole("button", { name: "Run fixture", exact: true }).click(); + await expect(page.getByTestId("engine-proof-error")).toHaveText( + "Typing engine binding version mismatch", + ); + await expect(page.getByTestId("engine-proof-result")).toBeEmpty(); + await page + .getByRole("textbox", { name: "Type finite slice" }) + .pressSequentially("one two"); + await expect(page.getByLabel("Finite slice state")).toBeEmpty(); +}); diff --git a/frontend/src/routes/engine-proof.tsx b/frontend/src/routes/engine-proof.tsx index 556e77bcd..d05adb742 100644 --- a/frontend/src/routes/engine-proof.tsx +++ b/frontend/src/routes/engine-proof.tsx @@ -424,6 +424,86 @@ function EngineProof() { setFailure(error instanceof Error ? error.message : String(error)); } }; + const runRecovery = async (): Promise => { + setResult(""); + setFailure(""); + try { + const inputs = JSON.parse(fixture()) as { input: Prepare }[]; + const { loadEngine, EngineFailure } = + await import("@oxytype/typing-engine/browser.client"); + const engine = await loadEngine(); + if (disposed) return; + const results = inputs.map(({ input }) => { + const content = engine.createContent(); + let session: BrowserSession | undefined; + try { + let failure: InstanceType | undefined; + try { + content.prepare(input); + } catch (error) { + if (!(error instanceof EngineFailure)) throw error; + failure = error; + } + if (failure === undefined) { + throw new Error("Expected typed preparation failure"); + } + content.prepare({ + ...input, + config: { ...input.config, mode: "words", words: 2, funbox: [] }, + language: { + ...input.language, + name: "english", + words: [[97], [98]], + }, + quotes: null, + polyglot: [], + }); + session = content.createSession(); + const recovered = session.snapshot().words.length === 2; + const log = JSON.stringify(session.log()); + let invalidCode: string | undefined; + try { + session.command({ + op: "insert", + data: [-1], + now: 0, + date: 0, + automatic: null, + isCompositionEnding: null, + correctShiftUsed: null, + }); + } catch (error) { + if (!(error instanceof EngineFailure)) throw error; + invalidCode = error.code; + } + const unchanged = log === JSON.stringify(session.log()); + content.dispose(); + let disposedCode: string | undefined; + try { + content.request(); + } catch (error) { + if (!(error instanceof EngineFailure)) throw error; + disposedCode = error.code; + } + return { + message: failure.message, + recovery: failure.recovery, + code: failure.code, + recovered, + invalidCode, + unchanged, + disposedCode, + }; + } finally { + session?.dispose(); + content.dispose(); + } + }); + setResult(JSON.stringify(results)); + } catch (error) { + setFailure(error instanceof Error ? error.message : String(error)); + } + }; const runGeneration = async (): Promise => { setResult(""); setFailure(""); @@ -708,6 +788,14 @@ function EngineProof() { > Run policies +
{result()}
{failure()}
{benchmark()}
diff --git a/packages/typing-engine/__test__/failure.spec.ts b/packages/typing-engine/__test__/failure.spec.ts new file mode 100644 index 000000000..1c064877e --- /dev/null +++ b/packages/typing-engine/__test__/failure.spec.ts @@ -0,0 +1,44 @@ +import { expect, it } from "vite-plus/test"; +import { EngineFailure, disposed, invoke } from "../src/failure"; +it("decodes the native structured error envelope without interpreting messages", () => { + const detail = { + code: "onePolyglotLanguage" as const, + message: "", + recovery: [ + { op: "setLanguage" as const, language: "english", noSave: true }, + { op: "disableFunbox" as const, name: "polyglot", noRestart: true }, + ], + }; + const cause = new Error(JSON.stringify(detail)); + try { + invoke(() => { + throw cause; + }); + throw new Error("Expected failure"); + } catch (error) { + expect(error).toBeInstanceOf(EngineFailure); + expect(error).toMatchObject({ + code: detail.code, + message: "", + recovery: detail.recovery, + cause, + }); + } +}); +it("preserves typed disposal and wraps host serialization errors as invalid input", () => { + const error = disposed("Session disposed"); + expect(() => + invoke(() => { + throw error; + }), + ).toThrow(error); + let caught: unknown; + try { + invoke(() => JSON.stringify(1n)); + } catch (error) { + caught = error; + } + expect(caught).toBeInstanceOf(EngineFailure); + expect(caught).toMatchObject({ code: "invalidInput", recovery: [] }); + expect(invoke(() => 7)).toBe(7); +}); diff --git a/packages/typing-engine/package.json b/packages/typing-engine/package.json index ff92f6d7f..7c5b19193 100644 --- a/packages/typing-engine/package.json +++ b/packages/typing-engine/package.json @@ -5,7 +5,8 @@ "type": "module", "exports": { "./browser.client": "./src/browser.client.ts", - "./text": "./src/text.ts" + "./text": "./src/text.ts", + "./failure": "./src/failure.ts" }, "scripts": { "ts-check": "vp lint . --type-aware --type-check --format agent", diff --git a/packages/typing-engine/src/browser.client.ts b/packages/typing-engine/src/browser.client.ts index a7f6b8282..f5e35f08c 100644 --- a/packages/typing-engine/src/browser.client.ts +++ b/packages/typing-engine/src/browser.client.ts @@ -59,6 +59,9 @@ export type { NextWord, Quote, }; +import { EngineFailure, disposed, invoke } from "./failure"; +export { EngineFailure } from "./failure"; +export type { EngineError, ErrorCode, Recovery } from "./failure"; const BINDING_VERSION = 1; export { encodeText, decodeText, toEngineLog, toWebLog } from "./text"; @@ -85,7 +88,11 @@ export async function loadEngine(signal?: AbortSignal): Promise<{ signal?.throwIfAborted(); const module = initSync({ module: compiled }); if (binding_version() !== BINDING_VERSION) { - throw new Error("Typing engine binding version mismatch"); + throw new EngineFailure({ + code: "bindingMismatch", + message: "Typing engine binding version mismatch", + recovery: [], + }); } return { createSession: (config, words) => new BrowserSession(config, words), @@ -104,12 +111,12 @@ export function createEngineLoader(): { } { const controller = new AbortController(); let pending: Promise | undefined; - let disposed = false; + let released = false; return { // oxlint-disable-next-line typescript/promise-function-async -- Owners share the exact pending promise. load: () => { - if (disposed) { - return Promise.reject(new Error("Typing engine loader disposed")); + if (released) { + return Promise.reject(disposed("Typing engine loader disposed")); } pending ??= loadEngine(controller.signal).catch((error: unknown) => { pending = undefined; @@ -118,8 +125,8 @@ export function createEngineLoader(): { return pending; }, dispose: () => { - if (disposed) return; - disposed = true; + if (released) return; + released = true; controller.abort(); pending = undefined; }, @@ -128,70 +135,69 @@ export function createEngineLoader(): { export class BrowserSession { private handle: WasmSession | undefined; - constructor(config: Partial, words: Text[], handle?: WasmSession) { this.handle = handle ?? - new WasmSession( - JSON.stringify(config), - JSON.stringify(words), - BINDING_VERSION, + invoke( + () => + new WasmSession( + JSON.stringify(config), + JSON.stringify(words), + BINDING_VERSION, + ), ); } - command(command: Command): Update { + private live(): WasmSession { if (this.handle === undefined) { - throw new Error("Typing engine session disposed"); + throw disposed("Typing engine session disposed"); } - return JSON.parse(this.handle.command(JSON.stringify(command))) as Update; + return this.handle; + } + command(command: Command): Update { + return invoke( + () => JSON.parse(this.live().command(JSON.stringify(command))) as Update, + ); } restartPolicy(options: Partial = {}): RestartDecision { - if (this.handle === undefined) { - throw new Error("Typing engine session disposed"); - } - return JSON.parse( - this.handle.restart_policy(JSON.stringify(options)), - ) as RestartDecision; + return invoke( + () => + JSON.parse( + this.live().restart_policy(JSON.stringify(options)), + ) as RestartDecision, + ); } pace(command: PaceCommand): PaceUpdate { - if (this.handle === undefined) { - throw new Error("Typing engine session disposed"); - } - return JSON.parse(this.handle.pace(JSON.stringify(command))) as PaceUpdate; + return invoke( + () => JSON.parse(this.live().pace(JSON.stringify(command))) as PaceUpdate, + ); } /** @internal Ownership transfer used by the app's content resource. */ recycleTo(content: WasmContent): void { - if (this.handle === undefined) { - throw new Error("Typing engine session disposed"); - } - content.recycle_session(this.handle); + invoke(() => content.recycle_session(this.live())); this.dispose(); } preflight(data: Text, layout: Partial = {}): Preflight { - if (this.handle === undefined) { - throw new Error("Typing engine session disposed"); - } - return JSON.parse( - this.handle.preflight(JSON.stringify(data), JSON.stringify(layout)), - ) as Preflight; + return invoke( + () => + JSON.parse( + this.live().preflight(JSON.stringify(data), JSON.stringify(layout)), + ) as Preflight, + ); } snapshot(): Snapshot { - if (this.handle === undefined) { - throw new Error("Typing engine session disposed"); - } - return JSON.parse(this.handle.snapshot()) as Snapshot; + return invoke(() => JSON.parse(this.live().snapshot()) as Snapshot); } log(): Log { - if (this.handle === undefined) { - throw new Error("Typing engine session disposed"); - } - return JSON.parse(this.handle.log()) as Log; + return invoke(() => JSON.parse(this.live().log()) as Log); } dispose(): void { const handle = this.handle; if (handle === undefined) return; this.handle = undefined; - handle.dispose(); - handle.free(); + invoke(() => { + handle.dispose(); + handle.free(); + }); } } @@ -227,10 +233,12 @@ export function benchmarkBoundary(): { jsonMs: number[] } { } export class BrowserContent { - private handle: WasmContent | undefined = new WasmContent(BINDING_VERSION); + private handle: WasmContent | undefined = invoke( + () => new WasmContent(BINDING_VERSION), + ); private live(): WasmContent { if (this.handle === undefined) { - throw new Error("Typing engine content disposed"); + throw disposed("Typing engine content disposed"); } return this.handle; } @@ -238,10 +246,12 @@ export class BrowserContent { session.recycleTo(this.live()); } prepare(input: Prepare, repeating = false): void { - this.live().prepare(JSON.stringify(input), repeating); + invoke(() => this.live().prepare(JSON.stringify(input), repeating)); } generate(repeating = false): Generated { - return JSON.parse(this.live().generate(repeating)) as Generated; + return invoke( + () => JSON.parse(this.live().generate(repeating)) as Generated, + ); } next( index: number, @@ -249,36 +259,52 @@ export class BrowserContent { previous: Text | null, previous2: Text | null, ): NextWord { - return JSON.parse( - this.live().next( - index, - bound, - JSON.stringify(previous), - JSON.stringify(previous2), - ), - ) as NextWord; + return invoke( + () => + JSON.parse( + this.live().next( + index, + bound, + JSON.stringify(previous), + JSON.stringify(previous2), + ), + ) as NextWord, + ); } request(): ContentRequest | null { - return JSON.parse(this.live().request()) as ContentRequest | null; + return invoke( + () => JSON.parse(this.live().request()) as ContentRequest | null, + ); } supply(request: ContentRequest, source: Source): void { - this.live().supply(JSON.stringify(request), JSON.stringify(source)); + invoke(() => + this.live().supply(JSON.stringify(request), JSON.stringify(source)), + ); } createSession(repeating = false): BrowserSession { - return new BrowserSession({}, [], this.live().session(repeating)); + return new BrowserSession( + {}, + [], + invoke(() => this.live().session(repeating)), + ); } metadata(): { allWordsGenerated: boolean; currentQuote: Quote | null } { - return JSON.parse(this.live().metadata()) as { - allWordsGenerated: boolean; - currentQuote: Quote | null; - }; + return invoke( + () => + JSON.parse(this.live().metadata()) as { + allWordsGenerated: boolean; + currentQuote: Quote | null; + }, + ); } dispose(): void { const handle = this.handle; if (handle === undefined) return; this.handle = undefined; - handle.dispose(); - handle.free(); + invoke(() => { + handle.dispose(); + handle.free(); + }); } } @@ -287,39 +313,49 @@ export function analyzeLog( log: Log, query: Q, ): Extract { - return JSON.parse( - analyze(JSON.stringify(log), JSON.stringify(query)), - ) as Extract; + return invoke( + () => + JSON.parse( + analyze(JSON.stringify(log), JSON.stringify(query)), + ) as Extract, + ); } /** Persistent playback: scheduling/audio/DOM belong to the host. */ export class BrowserReplay { private handle: WasmReplay | undefined; constructor(log: Log, zen: boolean) { - this.handle = new WasmReplay(JSON.stringify(log), zen, BINDING_VERSION); + this.handle = invoke( + () => new WasmReplay(JSON.stringify(log), zen, BINDING_VERSION), + ); } private live(): WasmReplay { if (this.handle === undefined) { - throw new Error("Typing engine replay disposed"); + throw disposed("Typing engine replay disposed"); } return this.handle; } snapshot(): PlaybackSnapshot { - return JSON.parse(this.live().snapshot()) as PlaybackSnapshot; + return invoke(() => JSON.parse(this.live().snapshot()) as PlaybackSnapshot); } step(): PlaybackUpdate { - return JSON.parse(this.live().step()) as PlaybackUpdate; + return invoke(() => JSON.parse(this.live().step()) as PlaybackUpdate); } seek(cursor: Cursor): PlaybackSnapshot { - return JSON.parse( - this.live().seek(JSON.stringify(cursor)), - ) as PlaybackSnapshot; + return invoke( + () => + JSON.parse( + this.live().seek(JSON.stringify(cursor)), + ) as PlaybackSnapshot, + ); } dispose(): void { const handle = this.handle; if (handle === undefined) return; this.handle = undefined; - handle.dispose(); - handle.free(); + invoke(() => { + handle.dispose(); + handle.free(); + }); } } diff --git a/packages/typing-engine/src/failure.ts b/packages/typing-engine/src/failure.ts new file mode 100644 index 000000000..4f8e0938f --- /dev/null +++ b/packages/typing-engine/src/failure.ts @@ -0,0 +1,54 @@ +import type { EngineError } from "../generated/types/EngineError"; +import type { ErrorCode } from "../generated/types/ErrorCode"; +import type { Recovery } from "../generated/types/Recovery"; +export type { EngineError, ErrorCode, Recovery }; + +export class EngineFailure extends Error { + readonly code: ErrorCode; + readonly recovery: Recovery[]; + constructor(detail: EngineError, options?: ErrorOptions) { + super(detail.message, options); + this.name = "EngineFailure"; + this.code = detail.code; + this.recovery = detail.recovery; + } +} +/** All binding errors use the same JSON DTO as native callers. */ +export function invoke(operation: () => T): T { + try { + return operation(); + } catch (cause) { + if (cause instanceof EngineFailure) throw cause; + if (cause instanceof Error) { + let detail: unknown; + try { + detail = JSON.parse(cause.message) as unknown; + } catch { + /* Host serialization errors are ordinary invalid input. */ + } + if ( + detail !== null && + typeof detail === "object" && + "code" in detail && + typeof detail.code === "string" && + "message" in detail && + typeof detail.message === "string" && + "recovery" in detail && + Array.isArray(detail.recovery) + ) { + throw new EngineFailure(detail as EngineError, { cause }); + } + } + throw new EngineFailure( + { + code: "invalidInput", + message: cause instanceof Error ? cause.message : String(cause), + recovery: [], + }, + { cause }, + ); + } +} +export function disposed(message: string): EngineFailure { + return new EngineFailure({ code: "disposed", message, recovery: [] }); +} From ffdd61b1da02be6c19e6c8f0e7011e836ff7d724 Mon Sep 17 00:00:00 2001 From: Lakshmi Tanmay Date: Fri, 23 Oct 2026 10:05:58 +0530 Subject: [PATCH 70/93] test(engine): freeze failed section recovery behavior --- docs/rust-engine/progress.md | 2 + frontend/__tests__/test/test-logic.spec.ts | 56 ++++++++++++- .../scripts/capture-source-recovery-oracle.ts | 83 +++++++++++++++++++ .../oracle/source-recovery.json | 30 +++++++ 4 files changed, 168 insertions(+), 3 deletions(-) create mode 100644 packages/typing-core/scripts/capture-source-recovery-oracle.ts create mode 100644 packages/typing-fixtures/oracle/source-recovery.json diff --git a/docs/rust-engine/progress.md b/docs/rust-engine/progress.md index bf07d6105..61697192b 100644 --- a/docs/rust-engine/progress.md +++ b/docs/rust-engine/progress.md @@ -183,3 +183,5 @@ do not rerun completed checks without a new reason. - Shared public session/content/generation operations now return structured EngineError values with codes and ordered typed recovery actions. All eight frozen quote/polyglot recovery messages/actions match natively, including empty-message notices and no-save/no-restart flags. Invalid config and stale replies have explicit codes; corrected preparation succeeds after every captured error. Full native suite and Clippy pass. Wasm currently preserves old error messages only; structured facade and failed-source lifecycle remain pending. - Wasm now transports the native structured error DTO; browser EngineFailure exposes code/actions and preserves cause. Hosts never infer recovery from message text. The built Cloudflare proof passes all 13 engine-proof tests (26.9 s), including all eight frozen recovery cases, invalid UTF-16 command rejection without mutation, disposal and an injected ABI mismatch that prevents usable sessions. Two facade unit tests and type-aware lint pass. Clippy initially caught redundant result wrappers; removed them and reran successfully. Wasm: 696,520 raw / 278,194 gzip level 6 (Vite 280.38 kB). Other migration files were not part of this targeted run. + +- Frozen source-error behavior before host cutover: initial poetry/Wikipedia false replies disable their funbox and throw the original message (new immutable source-recovery.json); active false replies show the exact error notice, disable the source and restart without completion. Actual website lifecycle characterization now passes 24 tests, including active undefined replies producing no error/restart. Capture typechecks and refuses overwrite. Native source-rejection implementation/tests are in progress separately. diff --git a/frontend/__tests__/test/test-logic.spec.ts b/frontend/__tests__/test/test-logic.spec.ts index 3280c286e..801e8042e 100644 --- a/frontend/__tests__/test/test-logic.spec.ts +++ b/frontend/__tests__/test/test-logic.spec.ts @@ -27,6 +27,9 @@ const mocks = vi.hoisted(() => ({ noQuit: false, inputAccuracy: 96.789, generatedWords: ["hello ", "world"], + allGenerated: true, + pullSection: undefined as undefined | (() => Promise), + sourceActions: [] as unknown[], generate: vi.fn(), complete: vi.fn(), sessionStart: vi.fn(), @@ -145,13 +148,16 @@ vi.mock("../../src/features/typing/runtime/events/data", () => ({ }), })); vi.mock("../../src/features/typing/runtime/words-generator", () => ({ - areAllWordsGenerated: () => true, + areAllWordsGenerated: () => mocks.allGenerated, })); vi.mock("../../src/features/typing/runtime/funbox/list", () => ({ getActiveFunboxes: () => [], getActiveFunboxesWithFunction: () => [], getActiveFunboxNames: () => [], - findSingleActiveFunboxWithFunction: () => undefined, + findSingleActiveFunboxWithFunction: () => + mocks.pullSection === undefined + ? undefined + : { name: "poetry", functions: { pullSection: mocks.pullSection } }, isFunboxActive: (name: string) => name === "no_quit" && mocks.noQuit, isFunboxActiveWithProperty: () => false, })); @@ -213,7 +219,8 @@ vi.mock("../../src/ts/config/setters", async () => { Object.assign(getMutableConfig(), { [key]: value }); }, setQuoteLengthAll: vi.fn(), - toggleFunbox: vi.fn(), + toggleFunbox: (name: string) => + mocks.sourceActions.push({ op: "disableFunbox", name, noRestart: false }), }; }); vi.mock("../../src/ts/db", () => ({ @@ -256,6 +263,7 @@ vi.mock("@oxytype/typing-core/result-validity", () => ({ import { getMutableConfig } from "../../src/ts/config/store"; import { getDefaultConfig } from "../../src/ts/constants/default-config"; import { + addWord, fail, finish, restart, @@ -274,6 +282,9 @@ beforeEach(() => { return dispose; }); Object.assign(mocks, { + allGenerated: true, + pullSection: undefined, + sourceActions: [], active: false, restarting: false, calculating: false, @@ -519,3 +530,42 @@ it("never applies a pending save acknowledgement to another actor", async () => }); mockHttpFixture("results.add", async (input) => await mocks.addResult(input)); + +// Frozen behavior before the Rust host cutover. Source I/O failure is recovery, not a score failure. +describe("section source recovery", () => { + it("reports, disables the source and restarts on an unavailable active section", async () => { + mocks.allGenerated = false; + mocks.pullSection = async () => false; + mocks.error.mockImplementation((message: string) => + mocks.sourceActions.push({ op: "errorNotice", message }), + ); + mocks.generate.mockImplementation(async () => { + mocks.sourceActions.push({ op: "restart" }); + return { + words: mocks.generatedWords, + sectionIndexes: [0, 0], + hasTab: false, + hasNewline: false, + }; + }); + await addWord(); + await vi.waitFor(() => expect(mocks.restarting).toBe(false)); + expect(mocks.sourceActions).toEqual([ + { + op: "errorNotice", + message: "Error while getting section. Please try again later", + }, + { op: "disableFunbox", name: "poetry", noRestart: false }, + { op: "restart" }, + ]); + expect(mocks.complete).not.toHaveBeenCalled(); + }); + it("ignores an undefined active section without reporting a failure", async () => { + mocks.allGenerated = false; + mocks.pullSection = async () => undefined; + await addWord(); + expect(mocks.sourceActions).toEqual([]); + expect(mocks.generate).not.toHaveBeenCalled(); + expect(mocks.error).not.toHaveBeenCalled(); + }); +}); diff --git a/packages/typing-core/scripts/capture-source-recovery-oracle.ts b/packages/typing-core/scripts/capture-source-recovery-oracle.ts new file mode 100644 index 000000000..cf7f02647 --- /dev/null +++ b/packages/typing-core/scripts/capture-source-recovery-oracle.ts @@ -0,0 +1,83 @@ +import { writeFileSync } from "node:fs"; +import { resolve } from "node:path"; +import { getDefaultConfig } from "../src/config/default-config"; +import { createFunboxWords } from "../src/words-generator/funboxes"; +import { + createFunboxWordFunctions, + getWordFunboxes, +} from "../src/funbox-word-functions"; +import type { WordsGeneratorDeps } from "../src/words-generator/types"; + +const references = []; +for (const kind of ["poetry", "wikipedia"] as const) { + for (const absent of [false] as const) { + const config = { ...getDefaultConfig(), funbox: [kind] }; + const recovery: unknown[] = []; + const functions = createFunboxWordFunctions({ + getConfig: () => config, + getLanguage: async () => ({ name: "english", words: ["one"] }), + getPoem: async () => absent, + getSection: async () => absent, + disableFunbox: () => undefined, + setLanguage: () => undefined, + }); + const deps: WordsGeneratorDeps = { + getConfig: () => config, + getActiveFunboxes: () => getWordFunboxes(config.funbox, functions), + customText: { + getText: () => [], + getMode: () => "repeat", + getLimitMode: () => "word", + getLimitValue: () => 0, + getPipeDelimiter: () => false, + }, + quotes: { + getQuotes: async () => ({ length: 0 }), + getQuoteById: () => undefined, + getRandomFavoriteQuote: () => null, + getRandomQuote: () => null, + }, + transforms: { + replaceAccents: (value) => value, + britishEnglish: async (value) => value, + englishPunctuation: { + check: async () => false, + replace: async (value) => value, + }, + }, + isRepeated: () => false, + isPractiseWordsActive: () => false, + getCurrentQuote: () => null, + setCurrentQuote: () => undefined, + getSelectedQuoteId: () => 0, + getWordsLength: () => 0, + setMode: () => undefined, + setQuoteLengthAll: () => undefined, + disableFunbox: (name) => + recovery.push({ op: "disableFunbox", name, noRestart: false }), + }; + let message: string | undefined; + try { + await createFunboxWords(deps).getFunboxSection(); + } catch (error) { + if (!(error instanceof Error)) throw error; + message = error.message; + } + if (message === undefined) { + throw new Error("Expected initial section failure"); + } + references.push({ + kind, + absent: "false", + expected: { message, recovery }, + }); + } +} +writeFileSync( + resolve( + import.meta.dirname, + "../../typing-fixtures/oracle/source-recovery.json", + ), + `${JSON.stringify(references, null, 2)}\n`, + { flag: "wx" }, +); diff --git a/packages/typing-fixtures/oracle/source-recovery.json b/packages/typing-fixtures/oracle/source-recovery.json new file mode 100644 index 000000000..bae545e79 --- /dev/null +++ b/packages/typing-fixtures/oracle/source-recovery.json @@ -0,0 +1,30 @@ +[ + { + "kind": "poetry", + "absent": "false", + "expected": { + "message": "Failed to pull section", + "recovery": [ + { + "op": "disableFunbox", + "name": "poetry", + "noRestart": false + } + ] + } + }, + { + "kind": "wikipedia", + "absent": "false", + "expected": { + "message": "Failed to pull section", + "recovery": [ + { + "op": "disableFunbox", + "name": "wikipedia", + "noRestart": false + } + ] + } + } +] From 3e5ca22ff492686065559d89ce08f2ea85342328 Mon Sep 17 00:00:00 2001 From: Lakshmi Tanmay Date: Fri, 23 Oct 2026 10:08:09 +0530 Subject: [PATCH 71/93] feat(engine): handle unavailable content without scoring failure --- crates/typing-engine/src/content.rs | 10 ++ crates/typing-engine/src/error.rs | 3 + crates/typing-engine/src/generation.rs | 41 ++++++++ crates/typing-engine/src/session.rs | 44 +++++++- crates/typing-engine/tests/content.rs | 133 +++++++++++++++++++++++++ docs/rust-engine/contract.md | 4 + docs/rust-engine/progress.md | 2 + 7 files changed, 236 insertions(+), 1 deletion(-) diff --git a/crates/typing-engine/src/content.rs b/crates/typing-engine/src/content.rs index ee8d131a3..d251f9331 100644 --- a/crates/typing-engine/src/content.rs +++ b/crates/typing-engine/src/content.rs @@ -217,6 +217,16 @@ impl Queue { self.pending = None; Ok(()) } + pub fn reject(&mut self, request: &Request) -> Result<(), crate::error::EngineError> { + if self.pending.as_ref() != Some(request) { + return Err(crate::error::EngineError::new( + crate::error::ErrorCode::StaleRequest, + "Stale or mismatched content request", + )); + } + self.pending = None; + Ok(()) + } pub fn pop(&mut self) -> Option
{ self.ready.pop_front() } diff --git a/crates/typing-engine/src/error.rs b/crates/typing-engine/src/error.rs index 1f17feed3..31b5ca607 100644 --- a/crates/typing-engine/src/error.rs +++ b/crates/typing-engine/src/error.rs @@ -43,6 +43,9 @@ pub enum Recovery { message: String, duration_ms: u32, }, + ErrorNotice { + message: String, + }, Restart, } #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize, TS)] diff --git a/crates/typing-engine/src/generation.rs b/crates/typing-engine/src/generation.rs index 411854c38..255c5903d 100644 --- a/crates/typing-engine/src/generation.rs +++ b/crates/typing-engine/src/generation.rs @@ -88,6 +88,7 @@ pub struct Generator { word_languages: std::collections::BTreeMap, usize>, section_queue: Option, batch: Option, + source_failure: Option, pub(crate) prepared_generation: u32, } pub(crate) fn case(text: &Text, upper: bool) -> Text { @@ -376,6 +377,7 @@ impl Generator { word_languages, section_queue, batch: None, + source_failure: None, prepared_generation: 0, }) } @@ -508,13 +510,49 @@ impl Generator { .ok_or("Generator has no section source")? .supply(request, source) } + /// A failed fetch consumes its exact token. Reprepare/reset is required before more generation. + pub fn fail_source( + &mut self, + request: &crate::content::Request, + active: bool, + ) -> Result { + self.section_queue + .as_mut() + .ok_or("Generator has no section source")? + .reject(request)?; + let mut recovery = vec![]; + if active { + recovery.push(Recovery::ErrorNotice { + message: "Error while getting section. Please try again later".into(), + }); + } + recovery.push(Recovery::DisableFunbox { + name: request.kind.clone(), + no_restart: false, + }); + if active { + recovery.push(Recovery::Restart); + } + let error = EngineError::recover( + ErrorCode::SourceUnavailable, + if active { "" } else { "Failed to pull section" }, + recovery, + ); + self.source_failure = Some(error.clone()); + self.batch = None; + Ok(error) + } pub fn reset_requests(&mut self, generation: u32) { + self.source_failure = None; if let Some(queue) = &mut self.section_queue { queue.reset(generation); } self.batch = None; } pub fn generate(&mut self, repeating: bool) -> Result { + if let Some(error) = &self.source_failure { + return Err(error.clone()); + } let mut batch = if let Some(batch) = self.batch.take() { if batch.repeating != repeating { self.batch = Some(batch); @@ -605,6 +643,9 @@ impl Generator { previous: Option<&Text>, previous2: Option<&Text>, ) -> Result { + if let Some(error) = &self.source_failure { + return Err(error.clone()); + } if self.repeating && self.config.mode != "quote" && let Some(next) = self.previous.get(index) diff --git a/crates/typing-engine/src/session.rs b/crates/typing-engine/src/session.rs index d735b255e..81b3d5aff 100644 --- a/crates/typing-engine/src/session.rs +++ b/crates/typing-engine/src/session.rs @@ -266,6 +266,11 @@ pub enum Command { request: crate::content::Request, source: Box, }, + #[serde(rename_all = "camelCase")] + FailContent { + now: f64, + request: crate::content::Request, + }, Configure { now: f64, config: Box, @@ -314,6 +319,9 @@ pub struct State { pub input_value: Text, pub active: bool, pub ended: bool, + #[serde(default, skip_serializing_if = "Option::is_none")] + #[ts(optional)] + pub awaiting_recovery: Option, #[serde(skip_serializing_if = "Option::is_none")] #[ts(optional)] pub reason: Option, @@ -448,6 +456,7 @@ pub struct Session { has_tab: bool, has_newline: bool, pending_commit: Option, + awaiting_recovery: bool, deferred_input: Option, defer_finish: bool, pending_finish: Option, @@ -550,6 +559,7 @@ impl Session { has_tab, has_newline, pending_commit: None, + awaiting_recovery: false, deferred_input: None, defer_finish: false, pending_finish: None, @@ -607,6 +617,7 @@ impl Session { ..observation }; let blocked = self.ended + || self.awaiting_recovery || self.pending_commit.is_some() || self.pending_finish.is_some() || crate::input::blocked(&self.config, &observation, self.has_newline); @@ -693,6 +704,7 @@ impl Session { input_value: self.current(), active: self.active, ended: self.ended, + awaiting_recovery: self.awaiting_recovery.then_some(true), reason: self.reason.clone(), } } @@ -773,6 +785,7 @@ impl Session { | Command::Configure { now, .. } | Command::CustomLimit { now, .. } | Command::Bailout { now, .. } + | Command::FailContent { now, .. } | Command::SupplyContent { now, .. } | Command::AcknowledgeFinish { now, .. } => *now, }; @@ -810,6 +823,21 @@ impl Session { { return Err("Timer catchup exceeds bounded work".into()); } + if self.awaiting_recovery + && matches!( + command, + Command::Start { .. } + | Command::Observe { .. } + | Command::FlushAutomatic { .. } + | Command::Bailout { .. } + | Command::Finish { .. } + ) + { + return Err(crate::error::EngineError::new( + crate::error::ErrorCode::SourceUnavailable, + "Session awaits content recovery", + )); + } let before = self.events.len(); let previous_origin = self.origin(); let invalidated = matches!(command, Command::Finalize { .. } | Command::Reset { .. }); @@ -860,6 +888,18 @@ impl Session { final_in_batch: layout.final_in_batch != Some(false), }); } + Command::FailContent { request, .. } => { + let error = self + .generator + .as_mut() + .ok_or("Session has no generated content")? + .fail_source(&request, true)?; + self.awaiting_recovery = true; + self.pending_commit = None; + self.deferred_input = None; + self.automatic.clear(); + return Err(error); + } Command::SupplyContent { now, date, @@ -947,6 +987,7 @@ impl Session { Command::Reset { .. } => { self.index = 0; self.bailed_out = false; + self.awaiting_recovery = false; self.pending_commit = None; self.deferred_input = None; self.pending_finish = None; @@ -1366,6 +1407,7 @@ impl Session { fn delete(&mut self, kind: &str, now: f64, observed: Option, sentinel_removed: bool) { if !self.active || self.ended + || self.awaiting_recovery || self.pending_commit.is_some() || self.pending_finish.is_some() { @@ -1446,7 +1488,7 @@ impl Session { } fn advance(&mut self, now: f64, date: f64) { - if !self.active || self.pending_finish.is_some() { + if !self.active || self.awaiting_recovery || self.pending_finish.is_some() { return; } let start = self.start.unwrap_or(now); diff --git a/crates/typing-engine/tests/content.rs b/crates/typing-engine/tests/content.rs index 62af20089..b90a11282 100644 --- a/crates/typing-engine/tests/content.rs +++ b/crates/typing-engine/tests/content.rs @@ -491,3 +491,136 @@ fn section_generation_and_random_draw_order_match_frozen_typescript() { ); } } + +#[test] +fn initial_source_failure_matches_frozen_recovery_and_requires_reprepare() { + use typing_engine::error::ErrorCode; + let references: Vec = serde_json::from_slice( + &std::fs::read( + std::path::Path::new(env!("CARGO_MANIFEST_DIR")) + .join("../../packages/typing-fixtures/oracle/source-recovery.json"), + ) + .unwrap(), + ) + .unwrap(); + for reference in references { + let mut content = typing_engine::generation::Content::default(); + let mut preparation = source_preparation(vec![]); + preparation.config.funbox = vec![reference["kind"].as_str().unwrap().into()]; + content.prepare(preparation).unwrap(); + assert!(content.start_session(false).is_err()); + let generator = content.generator().unwrap(); + let request = generator.pending_request().unwrap().clone(); + let error = generator.fail_source(&request, false).unwrap(); + assert_eq!(error.code, ErrorCode::SourceUnavailable); + assert_eq!( + serde_json::json!({"message":error.message,"recovery":error.recovery}), + reference["expected"] + ); + assert!(generator.pending_request().is_none()); + assert_eq!( + generator.generate(false).unwrap_err().code, + ErrorCode::SourceUnavailable + ); + assert_eq!( + generator.supply(&request, poem("late")).unwrap_err().code, + ErrorCode::StaleRequest + ); + content + .prepare(source_preparation(vec![poem(&vec!["one"; 100].join(" "))])) + .unwrap(); + assert!(content.start_session(false).is_ok()); + } +} +#[test] +fn active_source_failure_preserves_captured_input_and_never_scores_an_io_failure() { + use typing_engine::{Command, error::ErrorCode}; + for exhausted in [false, true] { + let mut session = running_section_session("normal"); + let batch = if exhausted { + "one ".repeat(101) + } else { + "one ".into() + }; + let waiting = commit(&mut session, &batch, 0.0); + let request = waiting.content_request.unwrap(); + let mut stale = request.clone(); + stale.generation += 1; + let before = session.log(); + assert_eq!( + session + .apply(Command::FailContent { + now: 10.0, + request: stale + }) + .err() + .unwrap() + .code, + ErrorCode::StaleRequest + ); + assert_eq!(session.log(), before); + let error = session + .apply(Command::FailContent { + now: 10.0, + request: request.clone(), + }) + .err() + .unwrap(); + assert_eq!(error.code, ErrorCode::SourceUnavailable); + assert_eq!( + serde_json::to_value(error.recovery).unwrap(), + serde_json::json!([ + {"op":"errorNotice","message":"Error while getting section. Please try again later"}, + {"op":"disableFunbox","name":"poetry","noRestart":false}, {"op":"restart"} + ]) + ); + assert_eq!(session.state().awaiting_recovery, Some(true)); + assert!(session.state().reason.is_none()); + assert_eq!(session.log(), before); + assert!(commit(&mut session, "discarded", 20.0).events.is_empty()); + assert!( + session + .apply(Command::Advance { + now: 5000.0, + date: 0.0 + }) + .unwrap() + .events + .is_empty() + ); + assert_eq!(session.log(), before); + assert_eq!( + session + .apply(Command::SupplyContent { + now: 5000.0, + date: 0.0, + request, + source: Box::new(poem("late")) + }) + .err() + .unwrap() + .code, + ErrorCode::StaleRequest + ); + session + .apply(Command::Abandon { + now: 5010.0, + date: 9000.0, + }) + .unwrap(); + assert!(session.state().ended); + assert!(session.state().reason.is_none()); + let after = session.log(); + assert_eq!( + after["events"].as_array().unwrap().len(), + before["events"].as_array().unwrap().len() + 1 + ); + assert_eq!( + after["events"].as_array().unwrap().last().unwrap()["testMs"], + 5010.0 + ); + session.apply(Command::Reset { now: 5020.0 }).unwrap(); + assert!(session.state().awaiting_recovery.is_none()); + assert!(session.log()["events"].as_array().unwrap().is_empty()); + } +} diff --git a/docs/rust-engine/contract.md b/docs/rust-engine/contract.md index 91fda8aac..14551856a 100644 --- a/docs/rust-engine/contract.md +++ b/docs/rust-engine/contract.md @@ -135,3 +135,7 @@ Content preparation advances an owner-local generation. Recycling takes the maxi ### Structured failures Native operations return EngineError with a stable code, display message and ordered Recovery actions. Wasm carries this DTO as JSON in its private generated exception message; the public browser facade decodes EngineFailure. Consumers inspect code/actions, never parse human wording. Empty messages are intentional for recovery paths that supply a notice. Corrected content preparation may retry after validation/recovery failures. Disposal and ABI mismatch are explicit failures; no session becomes usable after failed initialization. + +### Unavailable section replies + +Initial Content.fail_source returns sourceUnavailable with the frozen disable-funbox action. Active FailContent consumes the exact pending token and returns ordered errorNotice, disableFunbox, restart actions. No I/O error is recorded as a typing failure. State.awaitingRecovery blocks further input/ticks until host abandon/end/reset/replacement. Already logged input survives; unprocessed units awaiting the failed reply are discarded with the abandoned lifecycle. Abandon uses the host's actual captured clock. Late replies and stale failures cannot affect replacement state. Browser undefined section replies retain the original no-op behavior while buffered words remain; route cancellation disposes the owner, rather than reporting a source failure. Only an explicitly unavailable source (legacy false) invokes recovery. diff --git a/docs/rust-engine/progress.md b/docs/rust-engine/progress.md index 61697192b..287a32186 100644 --- a/docs/rust-engine/progress.md +++ b/docs/rust-engine/progress.md @@ -185,3 +185,5 @@ do not rerun completed checks without a new reason. - Wasm now transports the native structured error DTO; browser EngineFailure exposes code/actions and preserves cause. Hosts never infer recovery from message text. The built Cloudflare proof passes all 13 engine-proof tests (26.9 s), including all eight frozen recovery cases, invalid UTF-16 command rejection without mutation, disposal and an injected ABI mismatch that prevents usable sessions. Two facade unit tests and type-aware lint pass. Clippy initially caught redundant result wrappers; removed them and reran successfully. Wasm: 696,520 raw / 278,194 gzip level 6 (Vite 280.38 kB). Other migration files were not part of this targeted run. - Frozen source-error behavior before host cutover: initial poetry/Wikipedia false replies disable their funbox and throw the original message (new immutable source-recovery.json); active false replies show the exact error notice, disable the source and restart without completion. Actual website lifecycle characterization now passes 24 tests, including active undefined replies producing no error/restart. Capture typechecks and refuses overwrite. Native source-rejection implementation/tests are in progress separately. + +- Native failed-source lifecycle now consumes only the matching token, returns the frozen initial/active recovery actions and rejects late/duplicate replies. An active failure blocks input/ticks pending host recovery, preserves already captured events, drops unlogged deferred units and leaves score failure unset. Host abandon records its actual clock; reset permits a new lifecycle. Two new regressions cover prefetched and exhausted states. Full native suite/Clippy pass; browser checks running. From bf9e732aff840021d35e32d27991f7903d29023d Mon Sep 17 00:00:00 2001 From: Lakshmi Tanmay Date: Fri, 23 Oct 2026 10:08:33 +0530 Subject: [PATCH 72/93] test(engine): prove source failure recovery through Cloudflare Wasm --- crates/typing-engine-wasm/src/lib.rs | 12 ++ docs/rust-engine/progress.md | 4 +- frontend/e2e/engine-proof.migration.ts | 37 +++++ frontend/src/routes/engine-proof.tsx | 145 +++++++++++++++++++ packages/typing-engine/src/browser.client.ts | 4 + 5 files changed, 201 insertions(+), 1 deletion(-) diff --git a/crates/typing-engine-wasm/src/lib.rs b/crates/typing-engine-wasm/src/lib.rs index 65998679d..c0ecff714 100644 --- a/crates/typing-engine-wasm/src/lib.rs +++ b/crates/typing-engine-wasm/src/lib.rs @@ -196,6 +196,18 @@ impl WasmContent { .supply(&request, source) .map_err(bind_error) } + pub fn fail_source(&mut self, request: &str) -> Result<(), JsError> { + let request = parse(request)?; + let error = self + .content + .as_mut() + .ok_or_else(|| failure(ErrorCode::Disposed, "Content disposed"))? + .generator() + .map_err(bind_error)? + .fail_source(&request, false) + .map_err(bind_error)?; + Err(bind_error(error)) + } pub fn session(&mut self, repeating: bool) -> Result { let session = self .content diff --git a/docs/rust-engine/progress.md b/docs/rust-engine/progress.md index 287a32186..5eea02bd2 100644 --- a/docs/rust-engine/progress.md +++ b/docs/rust-engine/progress.md @@ -169,7 +169,7 @@ do not rerun completed checks without a new reason. ## Remaining Stage 1 exit work -- Structured native/binding errors and original quote/favorite/polyglot recovery actions; explicit failed-content reply lifecycle. +- Structured native/binding errors, quote/favorite/polyglot recovery and failed-content lifecycle implemented; final malformed-input and lifecycle stress gates remain. - Bounded live-stat and word-burst reads without full-log cloning on commits/ticks. - Complete content/config/observation/log validation, checked transport DTO compatibility, randomized lifecycle/stale-handle checks. - Final engine initialization distributions, long-session/restart memory and per-key performance budgets; full website startup/input/restart comparison belongs to Stage 2 after integration. @@ -187,3 +187,5 @@ do not rerun completed checks without a new reason. - Frozen source-error behavior before host cutover: initial poetry/Wikipedia false replies disable their funbox and throw the original message (new immutable source-recovery.json); active false replies show the exact error notice, disable the source and restart without completion. Actual website lifecycle characterization now passes 24 tests, including active undefined replies producing no error/restart. Capture typechecks and refuses overwrite. Native source-rejection implementation/tests are in progress separately. - Native failed-source lifecycle now consumes only the matching token, returns the frozen initial/active recovery actions and rejects late/duplicate replies. An active failure blocks input/ticks pending host recovery, preserves already captured events, drops unlogged deferred units and leaves score failure unset. Host abandon records its actual clock; reset permits a new lifecycle. Two new regressions cover prefetched and exhausted states. Full native suite/Clippy pass; browser checks running. + +- Built Cloudflare proof plus actual website input-contract suite: 17 tests pass (26.7 s). New browser checks match initial source errors and both buffered/exhausted active failures, preserve the log, block subsequent typing/ticks, reject late replies and abandon without a score failure. Type-aware lint and Clippy pass. Wasm remains within frozen transfer limits: 701.91 kB raw / 282.63 kB Vite gzip. diff --git a/frontend/e2e/engine-proof.migration.ts b/frontend/e2e/engine-proof.migration.ts index e470167fe..60456168f 100644 --- a/frontend/e2e/engine-proof.migration.ts +++ b/frontend/e2e/engine-proof.migration.ts @@ -989,3 +989,40 @@ test("binding version mismatch prevents creating a usable browser session", asyn .pressSequentially("one two"); await expect(page.getByLabel("Finite slice state")).toBeEmpty(); }); + +test("failed source replies preserve captured input and return exact recovery actions", async ({ + page, +}) => { + const refs = JSON.parse( + await readFile(resolve(root, "oracle/source-recovery.json"), "utf8"), + ) as { expected: unknown }[]; + await page.goto("/engine-proof"); + await page + .getByRole("textbox", { name: "Command fixture" }) + .fill(JSON.stringify(refs)); + await page + .getByRole("button", { name: "Run source failures", exact: true }) + .click(); + await expect(page.getByTestId("engine-proof-result")).not.toBeEmpty(); + await expect(page.getByTestId("engine-proof-error")).toBeEmpty(); + expect( + JSON.parse(await page.getByTestId("engine-proof-result").innerText()), + ).toEqual({ + initial: refs.map((ref) => ref.expected), + active: [false, true].map(() => ({ + recovery: [ + { + op: "errorNotice", + message: "Error while getting section. Please try again later", + }, + { op: "disableFunbox", name: "poetry", noRestart: false }, + { op: "restart" }, + ], + preserved: true, + awaitingRecovery: true, + failed: false, + stale: true, + ended: true, + })), + }); +}); diff --git a/frontend/src/routes/engine-proof.tsx b/frontend/src/routes/engine-proof.tsx index d05adb742..8d9a1b8ac 100644 --- a/frontend/src/routes/engine-proof.tsx +++ b/frontend/src/routes/engine-proof.tsx @@ -424,6 +424,143 @@ function EngineProof() { setFailure(error instanceof Error ? error.message : String(error)); } }; + const runSourceFailures = async (): Promise => { + setResult(""); + setFailure(""); + try { + const { loadEngine, EngineFailure, encodeText } = + await import("@oxytype/typing-engine/browser.client"); + const engine = await loadEngine(); + if (disposed) return; + const refs = JSON.parse(fixture()) as { kind: string }[]; + const output = []; + const preparation = ( + kind: string, + sections: Prepare["sections"], + ): Prepare => + ({ + config: { mode: "time", time: 0, funbox: [kind] }, + language: { name: "english", words: [encodeText("base")] }, + random: { kind: "seeded", state: 1 }, + sections, + }) as Prepare; + for (const ref of refs) { + const content = engine.createContent(); + try { + content.prepare(preparation(ref.kind, [])); + try { + content.createSession(); + } catch (error) { + if ( + !(error instanceof EngineFailure) || + error.code !== "contentRequired" + ) { + throw error; + } + } + const request = content.request(); + if (request === null) throw new Error("Missing initial request"); + try { + content.failSource(request); + } catch (error) { + if ( + !(error instanceof EngineFailure) || + error.code !== "sourceUnavailable" + ) { + throw error; + } + output.push({ message: error.message, recovery: error.recovery }); + } + } finally { + content.dispose(); + } + } + const active = []; + for (const exhausted of [false, true]) { + const content = engine.createContent(); + let session: BrowserSession | undefined; + try { + const source = { + kind: "poem" as const, + title: [], + author: [], + lines: [encodeText("one ".repeat(100))], + }; + content.prepare(preparation("poetry", [source])); + session = content.createSession(); + const waiting = session.command({ + op: "insert", + data: encodeText("one ".repeat(exhausted ? 101 : 1)), + correctShiftUsed: null, + automatic: null, + isCompositionEnding: null, + now: 0, + date: 0, + }); + if (waiting.contentRequest === undefined) { + throw new Error("Missing active request"); + } + const before = JSON.stringify(session.log()); + let recovery; + try { + session.command({ + op: "failContent", + now: 10, + request: waiting.contentRequest, + }); + } catch (error) { + if ( + !(error instanceof EngineFailure) || + error.code !== "sourceUnavailable" + ) { + throw error; + } + recovery = error.recovery; + } + session.command({ + op: "insert", + now: 20, + date: 0, + data: encodeText("discarded"), + correctShiftUsed: null, + automatic: null, + isCompositionEnding: null, + }); + session.command({ op: "advance", now: 5000, date: 0 }); + const preserved = before === JSON.stringify(session.log()); + const state = session.snapshot().state; + let stale = false; + try { + session.command({ + op: "supplyContent", + now: 5001, + date: 0, + request: waiting.contentRequest, + source, + }); + } catch (error) { + stale = + error instanceof EngineFailure && error.code === "staleRequest"; + } + session.command({ op: "abandon", now: 5010, date: 9000 }); + active.push({ + recovery, + preserved, + awaitingRecovery: state.awaitingRecovery, + failed: state.reason !== undefined, + stale, + ended: session.snapshot().state.ended, + }); + } finally { + session?.dispose(); + content.dispose(); + } + } + setResult(JSON.stringify({ initial: output, active })); + } catch (error) { + setFailure(error instanceof Error ? error.message : String(error)); + } + }; const runRecovery = async (): Promise => { setResult(""); setFailure(""); @@ -788,6 +925,14 @@ function EngineProof() { > Run policies + + + + + +