diff --git a/.github/workflows/layout-benchmark.yml b/.github/workflows/layout-benchmark.yml new file mode 100644 index 0000000..dddc868 --- /dev/null +++ b/.github/workflows/layout-benchmark.yml @@ -0,0 +1,67 @@ +name: Layout Reliability Benchmark + +on: + push: + branches: + - master + - develop + - "feat/**" + pull_request: + branches: + - master + - develop + +permissions: + contents: read + +jobs: + benchmark: + name: Layout Reliability Check (macos-15) + runs-on: macos-15 + timeout-minutes: 20 + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Show toolchain environment + run: | + swift --version + sw_vers + uname -a + + - name: Run TestRunner + run: swift run -c release TestRunner + + - name: Run InputPipelineTestRunner + # Hosted runners pass the Accessibility-dependent TextCorrector test (release CI runs + # the same runner); every failure here is a real failure. + run: swift run -c release InputPipelineTestRunner + + - name: Run LayoutBenchmark self-check + run: swift run -c release LayoutBenchmark self-check + + - name: Validate corpora and frozen layout tables + run: | + swift run -c release LayoutBenchmark validate --corpus Benchmarks/layout-corpus/v2 --tables Benchmarks/layout-tables/v1 --strict + # v1 is frozen with known label conflicts; it must still validate, warnings allowed. + swift run -c release LayoutBenchmark validate --corpus Benchmarks/layout-corpus/v1 --tables Benchmarks/layout-tables/v1 + + - name: Development smoke run gated against baseline v2 + run: | + mkdir -p Benchmarks/results/ci + swift run -c release LayoutBenchmark run \ + --adapter switchfix \ + --split development \ + --profile boundary-layout-only \ + --corpus Benchmarks/layout-corpus/v2 \ + --baseline Benchmarks/baselines/v2/corpus-v2/baseline.json \ + --output Benchmarks/results/ci + + - name: Upload benchmark artifacts + if: always() + uses: actions/upload-artifact@v4 + with: + name: benchmark-results-ci + path: Benchmarks/results/ci/ + if-no-files-found: ignore diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 716b188..bd00114 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -96,6 +96,9 @@ jobs: contents: write steps: + - name: Checkout release notes + uses: actions/checkout@v4 + - name: Download all release artifacts uses: actions/download-artifact@v4 with: @@ -112,4 +115,4 @@ jobs: with: files: | release-assets/*.zip - generate_release_notes: true + body_path: docs/releases/${{ github.ref_name }}.md diff --git a/.gitignore b/.gitignore index 299dc52..b8e8905 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,4 @@ temp.txt *.key brag-output/ brag-output-*/ +Benchmarks/results/ diff --git a/Benchmarks/baselines/v1/SUPERSEDED.md b/Benchmarks/baselines/v1/SUPERSEDED.md new file mode 100644 index 0000000..788d05a --- /dev/null +++ b/Benchmarks/baselines/v1/SUPERSEDED.md @@ -0,0 +1,11 @@ +# Baseline v1 is superseded + +Do not compare against this baseline. Use `Benchmarks/baselines/v2/corpus-v2/baseline.json`. + +Verification on 2026-10-19 found that the harness that recorded v1 did not measure the app's behavior: + +- It configured the detector with the `.standard` Ukrainian variant for every trial. All trials use `com.apple.keylayout.Ukrainian`, whose frozen table (key 1 = и, key 11 = і) the app classifies `.legacy`. The four EN → UK misses (for example `добрий день` → `добрій день`) come from this setting alone. Rerunning the same harness with `.legacy` gave 40/40. They were not caused by static mapping, as `baseline.md` states. +- It fed every key to the state machine as a letter, while the app treats `/`, `!`, `%`, `(` and other hard punctuation as word boundaries. This hid a real false positive: `/usr/local/bin` on ABC becomes `/гик/local/bin`. +- Its scorer dropped missing results, ignored error status and checkpoints, and scored layout-only switches as text damage. Its runner counts and analysis text were hardcoded. + +The files here are kept unchanged as the historical record. `Benchmarks/baselines/v2/corpus-v1` is the corrected measurement on the same corpus (40/40 recall, 1/160 text-damage false positives). diff --git a/Benchmarks/baselines/v1/baseline.json b/Benchmarks/baselines/v1/baseline.json new file mode 100644 index 0000000..e3e8585 --- /dev/null +++ b/Benchmarks/baselines/v1/baseline.json @@ -0,0 +1,828 @@ +{ + "baselineVersion" : "v1", + "combined" : { + "categoryBreakdown" : { + "acronym" : { + "category" : "acronym", + "fn" : 0, + "fp" : 0, + "tn" : 14, + "total" : 14, + "tp" : 0 + }, + "ambiguous-short" : { + "category" : "ambiguous-short", + "fn" : 0, + "fp" : 0, + "tn" : 20, + "total" : 20, + "tp" : 0 + }, + "apostrophe" : { + "category" : "apostrophe", + "fn" : 0, + "fp" : 0, + "tn" : 8, + "total" : 8, + "tp" : 0 + }, + "back-to-back" : { + "category" : "back-to-back", + "fn" : 2, + "fp" : 0, + "tn" : 0, + "total" : 4, + "tp" : 2 + }, + "capitalized" : { + "category" : "capitalized", + "fn" : 0, + "fp" : 0, + "tn" : 0, + "total" : 4, + "tp" : 4 + }, + "code-token" : { + "category" : "code-token", + "fn" : 0, + "fp" : 0, + "tn" : 8, + "total" : 8, + "tp" : 0 + }, + "contraction" : { + "category" : "contraction", + "fn" : 0, + "fp" : 0, + "tn" : 4, + "total" : 4, + "tp" : 0 + }, + "email" : { + "category" : "email", + "fn" : 0, + "fp" : 0, + "tn" : 2, + "total" : 2, + "tp" : 0 + }, + "filename" : { + "category" : "filename", + "fn" : 0, + "fp" : 0, + "tn" : 2, + "total" : 2, + "tp" : 0 + }, + "gibberish" : { + "category" : "gibberish", + "fn" : 0, + "fp" : 0, + "tn" : 8, + "total" : 8, + "tp" : 0 + }, + "isolated-char" : { + "category" : "isolated-char", + "fn" : 0, + "fp" : 0, + "tn" : 6, + "total" : 6, + "tp" : 0 + }, + "isolated-symbol" : { + "category" : "isolated-symbol", + "fn" : 0, + "fp" : 0, + "tn" : 4, + "total" : 4, + "tp" : 0 + }, + "natural-word" : { + "category" : "natural-word", + "fn" : 0, + "fp" : 0, + "tn" : 24, + "total" : 24, + "tp" : 0 + }, + "path" : { + "category" : "path", + "fn" : 0, + "fp" : 0, + "tn" : 2, + "total" : 2, + "tp" : 0 + }, + "phrase" : { + "category" : "phrase", + "fn" : 0, + "fp" : 0, + "tn" : 4, + "total" : 4, + "tp" : 0 + }, + "proper-noun" : { + "category" : "proper-noun", + "fn" : 0, + "fp" : 0, + "tn" : 16, + "total" : 16, + "tp" : 0 + }, + "punctuation" : { + "category" : "punctuation", + "fn" : 0, + "fp" : 0, + "tn" : 8, + "total" : 8, + "tp" : 0 + }, + "question" : { + "category" : "question", + "fn" : 2, + "fp" : 0, + "tn" : 0, + "total" : 4, + "tp" : 2 + }, + "short-word" : { + "category" : "short-word", + "fn" : 0, + "fp" : 0, + "tn" : 24, + "total" : 24, + "tp" : 0 + }, + "single-word" : { + "category" : "single-word", + "fn" : 0, + "fp" : 0, + "tn" : 0, + "total" : 28, + "tp" : 28 + }, + "symbol" : { + "category" : "symbol", + "fn" : 0, + "fp" : 0, + "tn" : 4, + "total" : 4, + "tp" : 0 + }, + "url" : { + "category" : "url", + "fn" : 0, + "fp" : 0, + "tn" : 2, + "total" : 2, + "tp" : 0 + } + }, + "directionBreakdown" : { + "EN -> UK" : { + "direction" : "EN -> UK", + "fn" : 4, + "layoutFP" : 0, + "recall" : 0.8, + "textDamageFPRate" : 0, + "textFP" : 0, + "tn" : 80, + "total" : 100, + "tp" : 16 + }, + "UK -> EN" : { + "direction" : "UK -> EN", + "fn" : 0, + "layoutFP" : 0, + "recall" : 1, + "textDamageFPRate" : 0, + "textFP" : 0, + "tn" : 80, + "total" : 100, + "tp" : 20 + } + }, + "falseNegatives" : 4, + "fnNoAction" : 0, + "fnUnsafeMutation" : 0, + "fnWrongRepair" : 4, + "layoutOnlyFPRate" : { + "count" : 0, + "interval" : { + "lower" : 0, + "upper" : 0.023446102076098447 + }, + "rate" : 0, + "total" : 160 + }, + "layoutOnlyFPs" : 0, + "nAbstain" : 40, + "nNegative" : 160, + "nPositive" : 40, + "nPreserve" : 120, + "precision" : { + "count" : 36, + "interval" : { + "lower" : 0.7694825786891643, + "upper" : 0.9604204003905062 + }, + "rate" : 0.9, + "total" : 40 + }, + "recall" : { + "count" : 36, + "interval" : { + "lower" : 0.7694825786891643, + "upper" : 0.9604204003905062 + }, + "rate" : 0.9, + "total" : 40 + }, + "textDamageFPRate" : { + "count" : 0, + "interval" : { + "lower" : 0, + "upper" : 0.023446102076098447 + }, + "rate" : 0, + "total" : 160 + }, + "textDamageFPs" : 0, + "totalFPRate" : { + "count" : 0, + "interval" : { + "lower" : 0, + "upper" : 0.023446102076098447 + }, + "rate" : 0, + "total" : 160 + }, + "totalFPs" : 0, + "totalTrials" : 200, + "trueNegatives" : 160, + "truePositives" : 36 + }, + "corpus" : { + "manifestSha256" : "febe8160b901532305809eb11850a463f7522864699bf8f1dbc5ed675c389772", + "version" : "v1" + }, + "created" : "2026-10-18T23:17:19Z", + "development" : { + "categoryBreakdown" : { + "acronym" : { + "category" : "acronym", + "fn" : 0, + "fp" : 0, + "tn" : 7, + "total" : 7, + "tp" : 0 + }, + "ambiguous-short" : { + "category" : "ambiguous-short", + "fn" : 0, + "fp" : 0, + "tn" : 10, + "total" : 10, + "tp" : 0 + }, + "apostrophe" : { + "category" : "apostrophe", + "fn" : 0, + "fp" : 0, + "tn" : 4, + "total" : 4, + "tp" : 0 + }, + "back-to-back" : { + "category" : "back-to-back", + "fn" : 1, + "fp" : 0, + "tn" : 0, + "total" : 2, + "tp" : 1 + }, + "capitalized" : { + "category" : "capitalized", + "fn" : 0, + "fp" : 0, + "tn" : 0, + "total" : 2, + "tp" : 2 + }, + "code-token" : { + "category" : "code-token", + "fn" : 0, + "fp" : 0, + "tn" : 4, + "total" : 4, + "tp" : 0 + }, + "contraction" : { + "category" : "contraction", + "fn" : 0, + "fp" : 0, + "tn" : 2, + "total" : 2, + "tp" : 0 + }, + "email" : { + "category" : "email", + "fn" : 0, + "fp" : 0, + "tn" : 1, + "total" : 1, + "tp" : 0 + }, + "filename" : { + "category" : "filename", + "fn" : 0, + "fp" : 0, + "tn" : 1, + "total" : 1, + "tp" : 0 + }, + "gibberish" : { + "category" : "gibberish", + "fn" : 0, + "fp" : 0, + "tn" : 4, + "total" : 4, + "tp" : 0 + }, + "isolated-char" : { + "category" : "isolated-char", + "fn" : 0, + "fp" : 0, + "tn" : 3, + "total" : 3, + "tp" : 0 + }, + "isolated-symbol" : { + "category" : "isolated-symbol", + "fn" : 0, + "fp" : 0, + "tn" : 2, + "total" : 2, + "tp" : 0 + }, + "natural-word" : { + "category" : "natural-word", + "fn" : 0, + "fp" : 0, + "tn" : 12, + "total" : 12, + "tp" : 0 + }, + "path" : { + "category" : "path", + "fn" : 0, + "fp" : 0, + "tn" : 1, + "total" : 1, + "tp" : 0 + }, + "phrase" : { + "category" : "phrase", + "fn" : 0, + "fp" : 0, + "tn" : 2, + "total" : 2, + "tp" : 0 + }, + "proper-noun" : { + "category" : "proper-noun", + "fn" : 0, + "fp" : 0, + "tn" : 8, + "total" : 8, + "tp" : 0 + }, + "punctuation" : { + "category" : "punctuation", + "fn" : 0, + "fp" : 0, + "tn" : 4, + "total" : 4, + "tp" : 0 + }, + "question" : { + "category" : "question", + "fn" : 1, + "fp" : 0, + "tn" : 0, + "total" : 2, + "tp" : 1 + }, + "short-word" : { + "category" : "short-word", + "fn" : 0, + "fp" : 0, + "tn" : 12, + "total" : 12, + "tp" : 0 + }, + "single-word" : { + "category" : "single-word", + "fn" : 0, + "fp" : 0, + "tn" : 0, + "total" : 14, + "tp" : 14 + }, + "symbol" : { + "category" : "symbol", + "fn" : 0, + "fp" : 0, + "tn" : 2, + "total" : 2, + "tp" : 0 + }, + "url" : { + "category" : "url", + "fn" : 0, + "fp" : 0, + "tn" : 1, + "total" : 1, + "tp" : 0 + } + }, + "directionBreakdown" : { + "EN -> UK" : { + "direction" : "EN -> UK", + "fn" : 2, + "layoutFP" : 0, + "recall" : 0.8, + "textDamageFPRate" : 0, + "textFP" : 0, + "tn" : 40, + "total" : 50, + "tp" : 8 + }, + "UK -> EN" : { + "direction" : "UK -> EN", + "fn" : 0, + "layoutFP" : 0, + "recall" : 1, + "textDamageFPRate" : 0, + "textFP" : 0, + "tn" : 40, + "total" : 50, + "tp" : 10 + } + }, + "falseNegatives" : 2, + "fnNoAction" : 0, + "fnUnsafeMutation" : 0, + "fnWrongRepair" : 2, + "layoutOnlyFPRate" : { + "count" : 0, + "interval" : { + "lower" : 0, + "upper" : 0.04581795177789462 + }, + "rate" : 0, + "total" : 80 + }, + "layoutOnlyFPs" : 0, + "nAbstain" : 20, + "nNegative" : 80, + "nPositive" : 20, + "nPreserve" : 60, + "precision" : { + "count" : 18, + "interval" : { + "lower" : 0.6989668593935576, + "upper" : 0.9721334538176092 + }, + "rate" : 0.9, + "total" : 20 + }, + "recall" : { + "count" : 18, + "interval" : { + "lower" : 0.6989668593935576, + "upper" : 0.9721334538176092 + }, + "rate" : 0.9, + "total" : 20 + }, + "textDamageFPRate" : { + "count" : 0, + "interval" : { + "lower" : 0, + "upper" : 0.04581795177789462 + }, + "rate" : 0, + "total" : 80 + }, + "textDamageFPs" : 0, + "totalFPRate" : { + "count" : 0, + "interval" : { + "lower" : 0, + "upper" : 0.04581795177789462 + }, + "rate" : 0, + "total" : 80 + }, + "totalFPs" : 0, + "totalTrials" : 100, + "trueNegatives" : 80, + "truePositives" : 18 + }, + "environment" : { + "architecture" : "arm64", + "dictionaryMode" : "text-fallback-test", + "keyboardType" : "91", + "osBuild" : "24G830", + "osVersion" : "15.7.9" + }, + "holdout" : { + "categoryBreakdown" : { + "acronym" : { + "category" : "acronym", + "fn" : 0, + "fp" : 0, + "tn" : 7, + "total" : 7, + "tp" : 0 + }, + "ambiguous-short" : { + "category" : "ambiguous-short", + "fn" : 0, + "fp" : 0, + "tn" : 10, + "total" : 10, + "tp" : 0 + }, + "apostrophe" : { + "category" : "apostrophe", + "fn" : 0, + "fp" : 0, + "tn" : 4, + "total" : 4, + "tp" : 0 + }, + "back-to-back" : { + "category" : "back-to-back", + "fn" : 1, + "fp" : 0, + "tn" : 0, + "total" : 2, + "tp" : 1 + }, + "capitalized" : { + "category" : "capitalized", + "fn" : 0, + "fp" : 0, + "tn" : 0, + "total" : 2, + "tp" : 2 + }, + "code-token" : { + "category" : "code-token", + "fn" : 0, + "fp" : 0, + "tn" : 4, + "total" : 4, + "tp" : 0 + }, + "contraction" : { + "category" : "contraction", + "fn" : 0, + "fp" : 0, + "tn" : 2, + "total" : 2, + "tp" : 0 + }, + "email" : { + "category" : "email", + "fn" : 0, + "fp" : 0, + "tn" : 1, + "total" : 1, + "tp" : 0 + }, + "filename" : { + "category" : "filename", + "fn" : 0, + "fp" : 0, + "tn" : 1, + "total" : 1, + "tp" : 0 + }, + "gibberish" : { + "category" : "gibberish", + "fn" : 0, + "fp" : 0, + "tn" : 4, + "total" : 4, + "tp" : 0 + }, + "isolated-char" : { + "category" : "isolated-char", + "fn" : 0, + "fp" : 0, + "tn" : 3, + "total" : 3, + "tp" : 0 + }, + "isolated-symbol" : { + "category" : "isolated-symbol", + "fn" : 0, + "fp" : 0, + "tn" : 2, + "total" : 2, + "tp" : 0 + }, + "natural-word" : { + "category" : "natural-word", + "fn" : 0, + "fp" : 0, + "tn" : 12, + "total" : 12, + "tp" : 0 + }, + "path" : { + "category" : "path", + "fn" : 0, + "fp" : 0, + "tn" : 1, + "total" : 1, + "tp" : 0 + }, + "phrase" : { + "category" : "phrase", + "fn" : 0, + "fp" : 0, + "tn" : 2, + "total" : 2, + "tp" : 0 + }, + "proper-noun" : { + "category" : "proper-noun", + "fn" : 0, + "fp" : 0, + "tn" : 8, + "total" : 8, + "tp" : 0 + }, + "punctuation" : { + "category" : "punctuation", + "fn" : 0, + "fp" : 0, + "tn" : 4, + "total" : 4, + "tp" : 0 + }, + "question" : { + "category" : "question", + "fn" : 1, + "fp" : 0, + "tn" : 0, + "total" : 2, + "tp" : 1 + }, + "short-word" : { + "category" : "short-word", + "fn" : 0, + "fp" : 0, + "tn" : 12, + "total" : 12, + "tp" : 0 + }, + "single-word" : { + "category" : "single-word", + "fn" : 0, + "fp" : 0, + "tn" : 0, + "total" : 14, + "tp" : 14 + }, + "symbol" : { + "category" : "symbol", + "fn" : 0, + "fp" : 0, + "tn" : 2, + "total" : 2, + "tp" : 0 + }, + "url" : { + "category" : "url", + "fn" : 0, + "fp" : 0, + "tn" : 1, + "total" : 1, + "tp" : 0 + } + }, + "directionBreakdown" : { + "EN -> UK" : { + "direction" : "EN -> UK", + "fn" : 2, + "layoutFP" : 0, + "recall" : 0.8, + "textDamageFPRate" : 0, + "textFP" : 0, + "tn" : 40, + "total" : 50, + "tp" : 8 + }, + "UK -> EN" : { + "direction" : "UK -> EN", + "fn" : 0, + "layoutFP" : 0, + "recall" : 1, + "textDamageFPRate" : 0, + "textFP" : 0, + "tn" : 40, + "total" : 50, + "tp" : 10 + } + }, + "falseNegatives" : 2, + "fnNoAction" : 0, + "fnUnsafeMutation" : 0, + "fnWrongRepair" : 2, + "layoutOnlyFPRate" : { + "count" : 0, + "interval" : { + "lower" : 0, + "upper" : 0.04581795177789462 + }, + "rate" : 0, + "total" : 80 + }, + "layoutOnlyFPs" : 0, + "nAbstain" : 20, + "nNegative" : 80, + "nPositive" : 20, + "nPreserve" : 60, + "precision" : { + "count" : 18, + "interval" : { + "lower" : 0.6989668593935576, + "upper" : 0.9721334538176092 + }, + "rate" : 0.9, + "total" : 20 + }, + "recall" : { + "count" : 18, + "interval" : { + "lower" : 0.6989668593935576, + "upper" : 0.9721334538176092 + }, + "rate" : 0.9, + "total" : 20 + }, + "textDamageFPRate" : { + "count" : 0, + "interval" : { + "lower" : 0, + "upper" : 0.04581795177789462 + }, + "rate" : 0, + "total" : 80 + }, + "textDamageFPs" : 0, + "totalFPRate" : { + "count" : 0, + "interval" : { + "lower" : 0, + "upper" : 0.04581795177789462 + }, + "rate" : 0, + "total" : 80 + }, + "totalFPs" : 0, + "totalTrials" : 100, + "trueNegatives" : 80, + "truePositives" : 18 + }, + "layer" : "replay", + "profile" : "boundary-layout-only", + "runnerChecks" : { + "InputPipelineTestRunner" : { + "expectedFailure" : "TextCorrector must apply layout-only plan without deleting text", + "failed" : "1", + "passed" : "966" + }, + "LayoutBenchmarkSelfCheck" : { + "failed" : "0", + "passed" : "36" + }, + "LayoutBenchmarkValidate" : { + "splitsCount" : "2", + "status" : "passed", + "trialsCount" : "200" + }, + "TestRunner" : { + "failed" : "0", + "passed" : "313" + } + }, + "schemaVersion" : 1, + "sourceCommit" : "34a77cd75f58b014a76180b8d7e6098082b5c676" +} \ No newline at end of file diff --git a/Benchmarks/baselines/v1/baseline.md b/Benchmarks/baselines/v1/baseline.md new file mode 100644 index 0000000..9c3a06e --- /dev/null +++ b/Benchmarks/baselines/v1/baseline.md @@ -0,0 +1,40 @@ +# SwitchFix Frozen Baseline v1 + +- **Recorded Date**: `2026-10-18T23:17:19Z` +- **Source Baseline Commit**: `34a77cd75f58b014a76180b8d7e6098082b5c676` +- **Environment**: macOS 15.7.9 (24G830), arm64, keyboardType 91 +- **Profile**: `boundary-layout-only` (layer: `replay`) +- **Corpus**: `Benchmarks/layout-corpus/v1` (manifest SHA: `febe8160b901...`) + +## Runner Verification at Baseline + +| Runner | Pass / Fail | Notes | +| --- | --- | --- | +| `TestRunner` | 313 passed, 0 failed | All core unit & dictionary tests passed | +| `InputPipelineTestRunner` | 966 passed, 1 failed | Only expected Accessibility-dependent `TextCorrector` test failed | +| `LayoutBenchmark self-check` | 36 passed, 0 failed | All runner, control adapter, and scoring checks passed | +| `LayoutBenchmark validate` | 200 passed, 0 failed | All 200 trials verified against frozen layout tables | + +## Aggregate Baseline Results + +| Split | Trials | Pos / Neg | TP | FN | Text FP | Layout FP | Recall (95% CI) | Text-Damage FP (95% CI) | Precision (95% CI) | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| **Development** | 100 | 20 / 80 | 18 | 2 | 0 | 0 | 90.0% [69.9%, 97.2%] | 0.00% [0.0%, 4.6%] | 90.0% [69.9%, 97.2%] | +| **Holdout** | 100 | 20 / 80 | 18 | 2 | 0 | 0 | 90.0% [69.9%, 97.2%] | 0.00% [0.0%, 4.6%] | 90.0% [69.9%, 97.2%] | +| **Combined** | 200 | 40 / 160 | 36 | 4 | 0 | 0 | 90.0% [76.9%, 96.0%] | 0.00% [0.0%, 2.3%] | 90.0% [76.9%, 96.0%] | + +## Direction Breakdown (Combined 200 Trials) + +| Direction | Trials | TP | FN | TN | Text FP | Layout FP | Recall | Text-Damage FP | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | +| EN -> UK | 100 | 16 | 4 | 80 | 0 | 0 | 80.0% | 0.00% | +| UK -> EN | 100 | 20 | 0 | 80 | 0 | 0 | 100.0% | 0.00% | + +## Baseline Observations & Analysis + +1. **Zero Text-Damage False Positives**: Across all 160 negative trials (120 preserve, 40 abstain), SwitchFix made 0 unauthorized edits (0.00% FP rate, Wilson 95% upper bound of 2.3%). +2. **Zero Layout-Only False Positives**: SwitchFix performed 0 unauthorized input source switches on negative trials (0.00% FP rate). +3. **100% Recall on UK -> EN**: 20/20 layout errors typed under Ukrainian layout were successfully repaired to English. +4. **False Negatives in EN -> UK (4 trials)**: All 4 false negatives occurred due to static Ukrainian variant fallback ambiguity (keys 1 and 11 mapping to `и` vs `і`). For example, `добрий день` was converted to `добрій день` and `як справи` was converted to `як справі`. Plan 01 Phase A (dynamic layout mapping) is specifically designed to eliminate this static mapping ambiguity. + +This baseline is frozen and versioned as v1. It serves as the regression baseline for Plan 02 (Personal Exceptions) and Plan 01 (Dynamic Layout Mapping). \ No newline at end of file diff --git a/Benchmarks/baselines/v2/README.md b/Benchmarks/baselines/v2/README.md new file mode 100644 index 0000000..579faa6 --- /dev/null +++ b/Benchmarks/baselines/v2/README.md @@ -0,0 +1,22 @@ +# Baseline v2 + +Recorded with `LayoutBenchmark record-baseline` from a clean worktree at `c22be93` (harness fixes R2, corpus v2 R3), profile `boundary-layout-only`, replay layer, empty personal exceptions. Each `baseline.md` lists the measured environment, the runner results parsed from their logs, and every trial that did not pass. + +| Directory | Corpus | Recall | Text-damage FP | Layout-only FP | Role | +| --- | --- | --- | --- | --- | --- | +| `corpus-v2/` | v2 (220 trials) | 40/40 | 8/180 (4.4%, Wilson 95% [2.3%, 8.5%]) | 0/180 | Reference baseline for later items and the CI gate | +| `corpus-v1/` | v1 (200 trials) | 40/40 | 1/160 (0.6%, [0.1%, 3.5%]) | 0/160 | Corrected measurement of the v1 corpus, for comparison with the superseded v1 baseline | + +Every false positive is a text replacement on ABC: + +- `/usr/local/bin` → `/гик/local/bin`. `/` is a word boundary, so `usr` is judged alone. +- Seven intended tokens that are also words in the other layout: `yt`→`не`, `nfr`→`так`, `hbr`→`рік`, `lev`→`дум`, `ns`→`ти`, `ufc`→`гас`, `dsl`→`вид`. + +`vs`, `uhf`, `gap` and all five Ukrainian-typed collisions are preserved. These are product behaviors of SwitchFix at this revision. Detector thresholds were not tuned to change them. + +Gate a change with: + +```sh +swift run -c release LayoutBenchmark run --split development --corpus Benchmarks/layout-corpus/v2 \ + --baseline Benchmarks/baselines/v2/corpus-v2/baseline.json +``` diff --git a/Benchmarks/baselines/v2/corpus-v1/baseline.json b/Benchmarks/baselines/v2/corpus-v1/baseline.json new file mode 100644 index 0000000..14cfa9d --- /dev/null +++ b/Benchmarks/baselines/v2/corpus-v1/baseline.json @@ -0,0 +1,1623 @@ +{ + "baselineVersion" : "v2\/corpus-v1", + "combined" : { + "categoryBreakdown" : { + "acronym" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 14, + "nPositive" : 0, + "textFP" : 0, + "tn" : 14, + "total" : 14, + "tp" : 0, + "unsupported" : 0 + }, + "ambiguous-short" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 20, + "nPositive" : 0, + "textFP" : 0, + "tn" : 20, + "total" : 20, + "tp" : 0, + "unsupported" : 0 + }, + "apostrophe" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 8, + "nPositive" : 0, + "textFP" : 0, + "tn" : 8, + "total" : 8, + "tp" : 0, + "unsupported" : 0 + }, + "back-to-back" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 0, + "nPositive" : 4, + "textFP" : 0, + "tn" : 0, + "total" : 4, + "tp" : 4, + "unsupported" : 0 + }, + "capitalized" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 0, + "nPositive" : 4, + "textFP" : 0, + "tn" : 0, + "total" : 4, + "tp" : 4, + "unsupported" : 0 + }, + "code-token" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 8, + "nPositive" : 0, + "textFP" : 0, + "tn" : 8, + "total" : 8, + "tp" : 0, + "unsupported" : 0 + }, + "contraction" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 4, + "nPositive" : 0, + "textFP" : 0, + "tn" : 4, + "total" : 4, + "tp" : 0, + "unsupported" : 0 + }, + "email" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 2, + "nPositive" : 0, + "textFP" : 0, + "tn" : 2, + "total" : 2, + "tp" : 0, + "unsupported" : 0 + }, + "filename" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 2, + "nPositive" : 0, + "textFP" : 0, + "tn" : 2, + "total" : 2, + "tp" : 0, + "unsupported" : 0 + }, + "gibberish" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 8, + "nPositive" : 0, + "textFP" : 0, + "tn" : 8, + "total" : 8, + "tp" : 0, + "unsupported" : 0 + }, + "isolated-char" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 6, + "nPositive" : 0, + "textFP" : 0, + "tn" : 6, + "total" : 6, + "tp" : 0, + "unsupported" : 0 + }, + "isolated-symbol" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 4, + "nPositive" : 0, + "textFP" : 0, + "tn" : 4, + "total" : 4, + "tp" : 0, + "unsupported" : 0 + }, + "natural-word" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 24, + "nPositive" : 0, + "textFP" : 0, + "tn" : 24, + "total" : 24, + "tp" : 0, + "unsupported" : 0 + }, + "path" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 2, + "nPositive" : 0, + "textFP" : 1, + "tn" : 1, + "total" : 2, + "tp" : 0, + "unsupported" : 0 + }, + "phrase" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 4, + "nPositive" : 0, + "textFP" : 0, + "tn" : 4, + "total" : 4, + "tp" : 0, + "unsupported" : 0 + }, + "proper-noun" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 16, + "nPositive" : 0, + "textFP" : 0, + "tn" : 16, + "total" : 16, + "tp" : 0, + "unsupported" : 0 + }, + "punctuation" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 8, + "nPositive" : 0, + "textFP" : 0, + "tn" : 8, + "total" : 8, + "tp" : 0, + "unsupported" : 0 + }, + "question" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 0, + "nPositive" : 4, + "textFP" : 0, + "tn" : 0, + "total" : 4, + "tp" : 4, + "unsupported" : 0 + }, + "short-word" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 24, + "nPositive" : 0, + "textFP" : 0, + "tn" : 24, + "total" : 24, + "tp" : 0, + "unsupported" : 0 + }, + "single-word" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 0, + "nPositive" : 28, + "textFP" : 0, + "tn" : 0, + "total" : 28, + "tp" : 28, + "unsupported" : 0 + }, + "symbol" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 4, + "nPositive" : 0, + "textFP" : 0, + "tn" : 4, + "total" : 4, + "tp" : 0, + "unsupported" : 0 + }, + "url" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 2, + "nPositive" : 0, + "textFP" : 0, + "tn" : 2, + "total" : 2, + "tp" : 0, + "unsupported" : 0 + } + }, + "complete" : true, + "completedTrials" : 200, + "coverage" : { + "count" : 200, + "interval" : { + "lower" : 0.981154748802476, + "upper" : 1 + }, + "rate" : 1, + "total" : 200 + }, + "directionBreakdown" : { + "EN -> UK" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 80, + "nPositive" : 20, + "textFP" : 1, + "tn" : 79, + "total" : 100, + "tp" : 20, + "unsupported" : 0 + }, + "UK -> EN" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 80, + "nPositive" : 20, + "textFP" : 0, + "tn" : 80, + "total" : 100, + "tp" : 20, + "unsupported" : 0 + } + }, + "errorTrials" : 0, + "falseNegatives" : 0, + "fnError" : 0, + "fnNoAction" : 0, + "fnTransientDamage" : 0, + "fnUnsafeMutation" : 0, + "fnWrongRepair" : 0, + "fnWrongSource" : 0, + "groupCount" : 200, + "layoutOnlyFPRate" : { + "count" : 0, + "interval" : { + "lower" : 0, + "upper" : 0.023446102076098447 + }, + "rate" : 0, + "total" : 160 + }, + "layoutOnlyFPs" : 0, + "missingTrials" : 0, + "nAbstain" : 40, + "nNegative" : 160, + "nPositive" : 40, + "nPreserve" : 120, + "precision" : { + "count" : 40, + "interval" : { + "lower" : 0.8740496946990259, + "upper" : 0.9956814561174678 + }, + "rate" : 0.975609756097561, + "total" : 41 + }, + "reasonCodeCounts" : { + + }, + "recall" : { + "count" : 40, + "interval" : { + "lower" : 0.9123787238495881, + "upper" : 1 + }, + "rate" : 1, + "total" : 40 + }, + "repairAttempts" : 41, + "schemaVersion" : 2, + "textDamageFPRate" : { + "count" : 1, + "interval" : { + "lower" : 0.0011041349131113987, + "upper" : 0.034548890887035826 + }, + "rate" : 0.00625, + "total" : 160 + }, + "textDamageFPs" : 1, + "totalFPRate" : { + "count" : 1, + "interval" : { + "lower" : 0.0011041349131113987, + "upper" : 0.034548890887035826 + }, + "rate" : 0.00625, + "total" : 160 + }, + "totalFPs" : 1, + "totalTrials" : 200, + "trialOutcomes" : { + "dev-abstain-081" : "TN", + "dev-abstain-082" : "TN", + "dev-abstain-083" : "TN", + "dev-abstain-084" : "TN", + "dev-abstain-085" : "TN", + "dev-abstain-086" : "TN", + "dev-abstain-087" : "TN", + "dev-abstain-088" : "TN", + "dev-abstain-089" : "TN", + "dev-abstain-090" : "TN", + "dev-abstain-091" : "TN", + "dev-abstain-092" : "TN", + "dev-abstain-093" : "TN", + "dev-abstain-094" : "TN", + "dev-abstain-095" : "TN", + "dev-abstain-096" : "TN", + "dev-abstain-097" : "TN", + "dev-abstain-098" : "TN", + "dev-abstain-099" : "TN", + "dev-abstain-100" : "TN", + "dev-preserve-021" : "TN", + "dev-preserve-022" : "TN", + "dev-preserve-023" : "TN", + "dev-preserve-024" : "TN", + "dev-preserve-025" : "TN", + "dev-preserve-026" : "TN", + "dev-preserve-027" : "TN", + "dev-preserve-028" : "TN", + "dev-preserve-029" : "TN", + "dev-preserve-030" : "TN", + "dev-preserve-031" : "TN", + "dev-preserve-032" : "TN", + "dev-preserve-033" : "TN", + "dev-preserve-034" : "TN", + "dev-preserve-035" : "TN", + "dev-preserve-036" : "TN", + "dev-preserve-037" : "TN", + "dev-preserve-038" : "TN", + "dev-preserve-039" : "TN", + "dev-preserve-040" : "TN", + "dev-preserve-041" : "FP_text_damage", + "dev-preserve-042" : "TN", + "dev-preserve-043" : "TN", + "dev-preserve-044" : "TN", + "dev-preserve-045" : "TN", + "dev-preserve-046" : "TN", + "dev-preserve-047" : "TN", + "dev-preserve-048" : "TN", + "dev-preserve-049" : "TN", + "dev-preserve-050" : "TN", + "dev-preserve-051" : "TN", + "dev-preserve-052" : "TN", + "dev-preserve-053" : "TN", + "dev-preserve-054" : "TN", + "dev-preserve-055" : "TN", + "dev-preserve-056" : "TN", + "dev-preserve-057" : "TN", + "dev-preserve-058" : "TN", + "dev-preserve-059" : "TN", + "dev-preserve-060" : "TN", + "dev-preserve-061" : "TN", + "dev-preserve-062" : "TN", + "dev-preserve-063" : "TN", + "dev-preserve-064" : "TN", + "dev-preserve-065" : "TN", + "dev-preserve-066" : "TN", + "dev-preserve-067" : "TN", + "dev-preserve-068" : "TN", + "dev-preserve-069" : "TN", + "dev-preserve-070" : "TN", + "dev-preserve-071" : "TN", + "dev-preserve-072" : "TN", + "dev-preserve-073" : "TN", + "dev-preserve-074" : "TN", + "dev-preserve-075" : "TN", + "dev-preserve-076" : "TN", + "dev-preserve-077" : "TN", + "dev-preserve-078" : "TN", + "dev-preserve-079" : "TN", + "dev-preserve-080" : "TN", + "dev-repair-001" : "TP", + "dev-repair-002" : "TP", + "dev-repair-003" : "TP", + "dev-repair-004" : "TP", + "dev-repair-005" : "TP", + "dev-repair-006" : "TP", + "dev-repair-007" : "TP", + "dev-repair-008" : "TP", + "dev-repair-009" : "TP", + "dev-repair-010" : "TP", + "dev-repair-011" : "TP", + "dev-repair-012" : "TP", + "dev-repair-013" : "TP", + "dev-repair-014" : "TP", + "dev-repair-015" : "TP", + "dev-repair-016" : "TP", + "dev-repair-017" : "TP", + "dev-repair-018" : "TP", + "dev-repair-019" : "TP", + "dev-repair-020" : "TP", + "holdout-abstain-081" : "TN", + "holdout-abstain-082" : "TN", + "holdout-abstain-083" : "TN", + "holdout-abstain-084" : "TN", + "holdout-abstain-085" : "TN", + "holdout-abstain-086" : "TN", + "holdout-abstain-087" : "TN", + "holdout-abstain-088" : "TN", + "holdout-abstain-089" : "TN", + "holdout-abstain-090" : "TN", + "holdout-abstain-091" : "TN", + "holdout-abstain-092" : "TN", + "holdout-abstain-093" : "TN", + "holdout-abstain-094" : "TN", + "holdout-abstain-095" : "TN", + "holdout-abstain-096" : "TN", + "holdout-abstain-097" : "TN", + "holdout-abstain-098" : "TN", + "holdout-abstain-099" : "TN", + "holdout-abstain-100" : "TN", + "holdout-preserve-021" : "TN", + "holdout-preserve-022" : "TN", + "holdout-preserve-023" : "TN", + "holdout-preserve-024" : "TN", + "holdout-preserve-025" : "TN", + "holdout-preserve-026" : "TN", + "holdout-preserve-027" : "TN", + "holdout-preserve-028" : "TN", + "holdout-preserve-029" : "TN", + "holdout-preserve-030" : "TN", + "holdout-preserve-031" : "TN", + "holdout-preserve-032" : "TN", + "holdout-preserve-033" : "TN", + "holdout-preserve-034" : "TN", + "holdout-preserve-035" : "TN", + "holdout-preserve-036" : "TN", + "holdout-preserve-037" : "TN", + "holdout-preserve-038" : "TN", + "holdout-preserve-039" : "TN", + "holdout-preserve-040" : "TN", + "holdout-preserve-041" : "TN", + "holdout-preserve-042" : "TN", + "holdout-preserve-043" : "TN", + "holdout-preserve-044" : "TN", + "holdout-preserve-045" : "TN", + "holdout-preserve-046" : "TN", + "holdout-preserve-047" : "TN", + "holdout-preserve-048" : "TN", + "holdout-preserve-049" : "TN", + "holdout-preserve-050" : "TN", + "holdout-preserve-051" : "TN", + "holdout-preserve-052" : "TN", + "holdout-preserve-053" : "TN", + "holdout-preserve-054" : "TN", + "holdout-preserve-055" : "TN", + "holdout-preserve-056" : "TN", + "holdout-preserve-057" : "TN", + "holdout-preserve-058" : "TN", + "holdout-preserve-059" : "TN", + "holdout-preserve-060" : "TN", + "holdout-preserve-061" : "TN", + "holdout-preserve-062" : "TN", + "holdout-preserve-063" : "TN", + "holdout-preserve-064" : "TN", + "holdout-preserve-065" : "TN", + "holdout-preserve-066" : "TN", + "holdout-preserve-067" : "TN", + "holdout-preserve-068" : "TN", + "holdout-preserve-069" : "TN", + "holdout-preserve-070" : "TN", + "holdout-preserve-071" : "TN", + "holdout-preserve-072" : "TN", + "holdout-preserve-073" : "TN", + "holdout-preserve-074" : "TN", + "holdout-preserve-075" : "TN", + "holdout-preserve-076" : "TN", + "holdout-preserve-077" : "TN", + "holdout-preserve-078" : "TN", + "holdout-preserve-079" : "TN", + "holdout-preserve-080" : "TN", + "holdout-repair-001" : "TP", + "holdout-repair-002" : "TP", + "holdout-repair-003" : "TP", + "holdout-repair-004" : "TP", + "holdout-repair-005" : "TP", + "holdout-repair-006" : "TP", + "holdout-repair-007" : "TP", + "holdout-repair-008" : "TP", + "holdout-repair-009" : "TP", + "holdout-repair-010" : "TP", + "holdout-repair-011" : "TP", + "holdout-repair-012" : "TP", + "holdout-repair-013" : "TP", + "holdout-repair-014" : "TP", + "holdout-repair-015" : "TP", + "holdout-repair-016" : "TP", + "holdout-repair-017" : "TP", + "holdout-repair-018" : "TP", + "holdout-repair-019" : "TP", + "holdout-repair-020" : "TP" + }, + "trueNegatives" : 159, + "truePositives" : 40, + "unauthorizedSourceChangeRate" : { + "count" : 0, + "interval" : { + "lower" : 0, + "upper" : 0.023446102076098447 + }, + "rate" : 0, + "total" : 160 + }, + "unauthorizedSourceChanges" : 0, + "unsupportedTrials" : 0 + }, + "created" : "2026-10-19T08:20:47Z", + "development" : { + "categoryBreakdown" : { + "acronym" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 7, + "nPositive" : 0, + "textFP" : 0, + "tn" : 7, + "total" : 7, + "tp" : 0, + "unsupported" : 0 + }, + "ambiguous-short" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 10, + "nPositive" : 0, + "textFP" : 0, + "tn" : 10, + "total" : 10, + "tp" : 0, + "unsupported" : 0 + }, + "apostrophe" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 4, + "nPositive" : 0, + "textFP" : 0, + "tn" : 4, + "total" : 4, + "tp" : 0, + "unsupported" : 0 + }, + "back-to-back" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 0, + "nPositive" : 2, + "textFP" : 0, + "tn" : 0, + "total" : 2, + "tp" : 2, + "unsupported" : 0 + }, + "capitalized" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 0, + "nPositive" : 2, + "textFP" : 0, + "tn" : 0, + "total" : 2, + "tp" : 2, + "unsupported" : 0 + }, + "code-token" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 4, + "nPositive" : 0, + "textFP" : 0, + "tn" : 4, + "total" : 4, + "tp" : 0, + "unsupported" : 0 + }, + "contraction" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 2, + "nPositive" : 0, + "textFP" : 0, + "tn" : 2, + "total" : 2, + "tp" : 0, + "unsupported" : 0 + }, + "email" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 1, + "nPositive" : 0, + "textFP" : 0, + "tn" : 1, + "total" : 1, + "tp" : 0, + "unsupported" : 0 + }, + "filename" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 1, + "nPositive" : 0, + "textFP" : 0, + "tn" : 1, + "total" : 1, + "tp" : 0, + "unsupported" : 0 + }, + "gibberish" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 4, + "nPositive" : 0, + "textFP" : 0, + "tn" : 4, + "total" : 4, + "tp" : 0, + "unsupported" : 0 + }, + "isolated-char" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 3, + "nPositive" : 0, + "textFP" : 0, + "tn" : 3, + "total" : 3, + "tp" : 0, + "unsupported" : 0 + }, + "isolated-symbol" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 2, + "nPositive" : 0, + "textFP" : 0, + "tn" : 2, + "total" : 2, + "tp" : 0, + "unsupported" : 0 + }, + "natural-word" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 12, + "nPositive" : 0, + "textFP" : 0, + "tn" : 12, + "total" : 12, + "tp" : 0, + "unsupported" : 0 + }, + "path" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 1, + "nPositive" : 0, + "textFP" : 1, + "tn" : 0, + "total" : 1, + "tp" : 0, + "unsupported" : 0 + }, + "phrase" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 2, + "nPositive" : 0, + "textFP" : 0, + "tn" : 2, + "total" : 2, + "tp" : 0, + "unsupported" : 0 + }, + "proper-noun" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 8, + "nPositive" : 0, + "textFP" : 0, + "tn" : 8, + "total" : 8, + "tp" : 0, + "unsupported" : 0 + }, + "punctuation" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 4, + "nPositive" : 0, + "textFP" : 0, + "tn" : 4, + "total" : 4, + "tp" : 0, + "unsupported" : 0 + }, + "question" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 0, + "nPositive" : 2, + "textFP" : 0, + "tn" : 0, + "total" : 2, + "tp" : 2, + "unsupported" : 0 + }, + "short-word" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 12, + "nPositive" : 0, + "textFP" : 0, + "tn" : 12, + "total" : 12, + "tp" : 0, + "unsupported" : 0 + }, + "single-word" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 0, + "nPositive" : 14, + "textFP" : 0, + "tn" : 0, + "total" : 14, + "tp" : 14, + "unsupported" : 0 + }, + "symbol" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 2, + "nPositive" : 0, + "textFP" : 0, + "tn" : 2, + "total" : 2, + "tp" : 0, + "unsupported" : 0 + }, + "url" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 1, + "nPositive" : 0, + "textFP" : 0, + "tn" : 1, + "total" : 1, + "tp" : 0, + "unsupported" : 0 + } + }, + "complete" : true, + "completedTrials" : 100, + "coverage" : { + "count" : 100, + "interval" : { + "lower" : 0.9630066466416288, + "upper" : 0.9999999999999999 + }, + "rate" : 1, + "total" : 100 + }, + "directionBreakdown" : { + "EN -> UK" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 40, + "nPositive" : 10, + "textFP" : 1, + "tn" : 39, + "total" : 50, + "tp" : 10, + "unsupported" : 0 + }, + "UK -> EN" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 40, + "nPositive" : 10, + "textFP" : 0, + "tn" : 40, + "total" : 50, + "tp" : 10, + "unsupported" : 0 + } + }, + "errorTrials" : 0, + "falseNegatives" : 0, + "fnError" : 0, + "fnNoAction" : 0, + "fnTransientDamage" : 0, + "fnUnsafeMutation" : 0, + "fnWrongRepair" : 0, + "fnWrongSource" : 0, + "groupCount" : 100, + "layoutOnlyFPRate" : { + "count" : 0, + "interval" : { + "lower" : 0, + "upper" : 0.04581795177789462 + }, + "rate" : 0, + "total" : 80 + }, + "layoutOnlyFPs" : 0, + "missingTrials" : 0, + "nAbstain" : 20, + "nNegative" : 80, + "nPositive" : 20, + "nPreserve" : 60, + "precision" : { + "count" : 20, + "interval" : { + "lower" : 0.7733069043156979, + "upper" : 0.9915439880340766 + }, + "rate" : 0.9523809523809523, + "total" : 21 + }, + "reasonCodeCounts" : { + + }, + "recall" : { + "count" : 20, + "interval" : { + "lower" : 0.8388753915139584, + "upper" : 1 + }, + "rate" : 1, + "total" : 20 + }, + "repairAttempts" : 21, + "schemaVersion" : 2, + "textDamageFPRate" : { + "count" : 1, + "interval" : { + "lower" : 0.0022099818158947265, + "upper" : 0.06746252116755254 + }, + "rate" : 0.0125, + "total" : 80 + }, + "textDamageFPs" : 1, + "totalFPRate" : { + "count" : 1, + "interval" : { + "lower" : 0.0022099818158947265, + "upper" : 0.06746252116755254 + }, + "rate" : 0.0125, + "total" : 80 + }, + "totalFPs" : 1, + "totalTrials" : 100, + "trialOutcomes" : { + "dev-abstain-081" : "TN", + "dev-abstain-082" : "TN", + "dev-abstain-083" : "TN", + "dev-abstain-084" : "TN", + "dev-abstain-085" : "TN", + "dev-abstain-086" : "TN", + "dev-abstain-087" : "TN", + "dev-abstain-088" : "TN", + "dev-abstain-089" : "TN", + "dev-abstain-090" : "TN", + "dev-abstain-091" : "TN", + "dev-abstain-092" : "TN", + "dev-abstain-093" : "TN", + "dev-abstain-094" : "TN", + "dev-abstain-095" : "TN", + "dev-abstain-096" : "TN", + "dev-abstain-097" : "TN", + "dev-abstain-098" : "TN", + "dev-abstain-099" : "TN", + "dev-abstain-100" : "TN", + "dev-preserve-021" : "TN", + "dev-preserve-022" : "TN", + "dev-preserve-023" : "TN", + "dev-preserve-024" : "TN", + "dev-preserve-025" : "TN", + "dev-preserve-026" : "TN", + "dev-preserve-027" : "TN", + "dev-preserve-028" : "TN", + "dev-preserve-029" : "TN", + "dev-preserve-030" : "TN", + "dev-preserve-031" : "TN", + "dev-preserve-032" : "TN", + "dev-preserve-033" : "TN", + "dev-preserve-034" : "TN", + "dev-preserve-035" : "TN", + "dev-preserve-036" : "TN", + "dev-preserve-037" : "TN", + "dev-preserve-038" : "TN", + "dev-preserve-039" : "TN", + "dev-preserve-040" : "TN", + "dev-preserve-041" : "FP_text_damage", + "dev-preserve-042" : "TN", + "dev-preserve-043" : "TN", + "dev-preserve-044" : "TN", + "dev-preserve-045" : "TN", + "dev-preserve-046" : "TN", + "dev-preserve-047" : "TN", + "dev-preserve-048" : "TN", + "dev-preserve-049" : "TN", + "dev-preserve-050" : "TN", + "dev-preserve-051" : "TN", + "dev-preserve-052" : "TN", + "dev-preserve-053" : "TN", + "dev-preserve-054" : "TN", + "dev-preserve-055" : "TN", + "dev-preserve-056" : "TN", + "dev-preserve-057" : "TN", + "dev-preserve-058" : "TN", + "dev-preserve-059" : "TN", + "dev-preserve-060" : "TN", + "dev-preserve-061" : "TN", + "dev-preserve-062" : "TN", + "dev-preserve-063" : "TN", + "dev-preserve-064" : "TN", + "dev-preserve-065" : "TN", + "dev-preserve-066" : "TN", + "dev-preserve-067" : "TN", + "dev-preserve-068" : "TN", + "dev-preserve-069" : "TN", + "dev-preserve-070" : "TN", + "dev-preserve-071" : "TN", + "dev-preserve-072" : "TN", + "dev-preserve-073" : "TN", + "dev-preserve-074" : "TN", + "dev-preserve-075" : "TN", + "dev-preserve-076" : "TN", + "dev-preserve-077" : "TN", + "dev-preserve-078" : "TN", + "dev-preserve-079" : "TN", + "dev-preserve-080" : "TN", + "dev-repair-001" : "TP", + "dev-repair-002" : "TP", + "dev-repair-003" : "TP", + "dev-repair-004" : "TP", + "dev-repair-005" : "TP", + "dev-repair-006" : "TP", + "dev-repair-007" : "TP", + "dev-repair-008" : "TP", + "dev-repair-009" : "TP", + "dev-repair-010" : "TP", + "dev-repair-011" : "TP", + "dev-repair-012" : "TP", + "dev-repair-013" : "TP", + "dev-repair-014" : "TP", + "dev-repair-015" : "TP", + "dev-repair-016" : "TP", + "dev-repair-017" : "TP", + "dev-repair-018" : "TP", + "dev-repair-019" : "TP", + "dev-repair-020" : "TP" + }, + "trueNegatives" : 79, + "truePositives" : 20, + "unauthorizedSourceChangeRate" : { + "count" : 0, + "interval" : { + "lower" : 0, + "upper" : 0.04581795177789462 + }, + "rate" : 0, + "total" : 80 + }, + "unauthorizedSourceChanges" : 0, + "unsupportedTrials" : 0 + }, + "holdout" : { + "categoryBreakdown" : { + "acronym" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 7, + "nPositive" : 0, + "textFP" : 0, + "tn" : 7, + "total" : 7, + "tp" : 0, + "unsupported" : 0 + }, + "ambiguous-short" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 10, + "nPositive" : 0, + "textFP" : 0, + "tn" : 10, + "total" : 10, + "tp" : 0, + "unsupported" : 0 + }, + "apostrophe" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 4, + "nPositive" : 0, + "textFP" : 0, + "tn" : 4, + "total" : 4, + "tp" : 0, + "unsupported" : 0 + }, + "back-to-back" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 0, + "nPositive" : 2, + "textFP" : 0, + "tn" : 0, + "total" : 2, + "tp" : 2, + "unsupported" : 0 + }, + "capitalized" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 0, + "nPositive" : 2, + "textFP" : 0, + "tn" : 0, + "total" : 2, + "tp" : 2, + "unsupported" : 0 + }, + "code-token" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 4, + "nPositive" : 0, + "textFP" : 0, + "tn" : 4, + "total" : 4, + "tp" : 0, + "unsupported" : 0 + }, + "contraction" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 2, + "nPositive" : 0, + "textFP" : 0, + "tn" : 2, + "total" : 2, + "tp" : 0, + "unsupported" : 0 + }, + "email" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 1, + "nPositive" : 0, + "textFP" : 0, + "tn" : 1, + "total" : 1, + "tp" : 0, + "unsupported" : 0 + }, + "filename" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 1, + "nPositive" : 0, + "textFP" : 0, + "tn" : 1, + "total" : 1, + "tp" : 0, + "unsupported" : 0 + }, + "gibberish" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 4, + "nPositive" : 0, + "textFP" : 0, + "tn" : 4, + "total" : 4, + "tp" : 0, + "unsupported" : 0 + }, + "isolated-char" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 3, + "nPositive" : 0, + "textFP" : 0, + "tn" : 3, + "total" : 3, + "tp" : 0, + "unsupported" : 0 + }, + "isolated-symbol" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 2, + "nPositive" : 0, + "textFP" : 0, + "tn" : 2, + "total" : 2, + "tp" : 0, + "unsupported" : 0 + }, + "natural-word" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 12, + "nPositive" : 0, + "textFP" : 0, + "tn" : 12, + "total" : 12, + "tp" : 0, + "unsupported" : 0 + }, + "path" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 1, + "nPositive" : 0, + "textFP" : 0, + "tn" : 1, + "total" : 1, + "tp" : 0, + "unsupported" : 0 + }, + "phrase" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 2, + "nPositive" : 0, + "textFP" : 0, + "tn" : 2, + "total" : 2, + "tp" : 0, + "unsupported" : 0 + }, + "proper-noun" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 8, + "nPositive" : 0, + "textFP" : 0, + "tn" : 8, + "total" : 8, + "tp" : 0, + "unsupported" : 0 + }, + "punctuation" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 4, + "nPositive" : 0, + "textFP" : 0, + "tn" : 4, + "total" : 4, + "tp" : 0, + "unsupported" : 0 + }, + "question" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 0, + "nPositive" : 2, + "textFP" : 0, + "tn" : 0, + "total" : 2, + "tp" : 2, + "unsupported" : 0 + }, + "short-word" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 12, + "nPositive" : 0, + "textFP" : 0, + "tn" : 12, + "total" : 12, + "tp" : 0, + "unsupported" : 0 + }, + "single-word" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 0, + "nPositive" : 14, + "textFP" : 0, + "tn" : 0, + "total" : 14, + "tp" : 14, + "unsupported" : 0 + }, + "symbol" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 2, + "nPositive" : 0, + "textFP" : 0, + "tn" : 2, + "total" : 2, + "tp" : 0, + "unsupported" : 0 + }, + "url" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 1, + "nPositive" : 0, + "textFP" : 0, + "tn" : 1, + "total" : 1, + "tp" : 0, + "unsupported" : 0 + } + }, + "complete" : true, + "completedTrials" : 100, + "coverage" : { + "count" : 100, + "interval" : { + "lower" : 0.9630066466416288, + "upper" : 0.9999999999999999 + }, + "rate" : 1, + "total" : 100 + }, + "directionBreakdown" : { + "EN -> UK" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 40, + "nPositive" : 10, + "textFP" : 0, + "tn" : 40, + "total" : 50, + "tp" : 10, + "unsupported" : 0 + }, + "UK -> EN" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 40, + "nPositive" : 10, + "textFP" : 0, + "tn" : 40, + "total" : 50, + "tp" : 10, + "unsupported" : 0 + } + }, + "errorTrials" : 0, + "falseNegatives" : 0, + "fnError" : 0, + "fnNoAction" : 0, + "fnTransientDamage" : 0, + "fnUnsafeMutation" : 0, + "fnWrongRepair" : 0, + "fnWrongSource" : 0, + "groupCount" : 100, + "layoutOnlyFPRate" : { + "count" : 0, + "interval" : { + "lower" : 0, + "upper" : 0.04581795177789462 + }, + "rate" : 0, + "total" : 80 + }, + "layoutOnlyFPs" : 0, + "missingTrials" : 0, + "nAbstain" : 20, + "nNegative" : 80, + "nPositive" : 20, + "nPreserve" : 60, + "precision" : { + "count" : 20, + "interval" : { + "lower" : 0.8388753915139584, + "upper" : 1 + }, + "rate" : 1, + "total" : 20 + }, + "reasonCodeCounts" : { + + }, + "recall" : { + "count" : 20, + "interval" : { + "lower" : 0.8388753915139584, + "upper" : 1 + }, + "rate" : 1, + "total" : 20 + }, + "repairAttempts" : 20, + "schemaVersion" : 2, + "textDamageFPRate" : { + "count" : 0, + "interval" : { + "lower" : 0, + "upper" : 0.04581795177789462 + }, + "rate" : 0, + "total" : 80 + }, + "textDamageFPs" : 0, + "totalFPRate" : { + "count" : 0, + "interval" : { + "lower" : 0, + "upper" : 0.04581795177789462 + }, + "rate" : 0, + "total" : 80 + }, + "totalFPs" : 0, + "totalTrials" : 100, + "trialOutcomes" : { + "holdout-abstain-081" : "TN", + "holdout-abstain-082" : "TN", + "holdout-abstain-083" : "TN", + "holdout-abstain-084" : "TN", + "holdout-abstain-085" : "TN", + "holdout-abstain-086" : "TN", + "holdout-abstain-087" : "TN", + "holdout-abstain-088" : "TN", + "holdout-abstain-089" : "TN", + "holdout-abstain-090" : "TN", + "holdout-abstain-091" : "TN", + "holdout-abstain-092" : "TN", + "holdout-abstain-093" : "TN", + "holdout-abstain-094" : "TN", + "holdout-abstain-095" : "TN", + "holdout-abstain-096" : "TN", + "holdout-abstain-097" : "TN", + "holdout-abstain-098" : "TN", + "holdout-abstain-099" : "TN", + "holdout-abstain-100" : "TN", + "holdout-preserve-021" : "TN", + "holdout-preserve-022" : "TN", + "holdout-preserve-023" : "TN", + "holdout-preserve-024" : "TN", + "holdout-preserve-025" : "TN", + "holdout-preserve-026" : "TN", + "holdout-preserve-027" : "TN", + "holdout-preserve-028" : "TN", + "holdout-preserve-029" : "TN", + "holdout-preserve-030" : "TN", + "holdout-preserve-031" : "TN", + "holdout-preserve-032" : "TN", + "holdout-preserve-033" : "TN", + "holdout-preserve-034" : "TN", + "holdout-preserve-035" : "TN", + "holdout-preserve-036" : "TN", + "holdout-preserve-037" : "TN", + "holdout-preserve-038" : "TN", + "holdout-preserve-039" : "TN", + "holdout-preserve-040" : "TN", + "holdout-preserve-041" : "TN", + "holdout-preserve-042" : "TN", + "holdout-preserve-043" : "TN", + "holdout-preserve-044" : "TN", + "holdout-preserve-045" : "TN", + "holdout-preserve-046" : "TN", + "holdout-preserve-047" : "TN", + "holdout-preserve-048" : "TN", + "holdout-preserve-049" : "TN", + "holdout-preserve-050" : "TN", + "holdout-preserve-051" : "TN", + "holdout-preserve-052" : "TN", + "holdout-preserve-053" : "TN", + "holdout-preserve-054" : "TN", + "holdout-preserve-055" : "TN", + "holdout-preserve-056" : "TN", + "holdout-preserve-057" : "TN", + "holdout-preserve-058" : "TN", + "holdout-preserve-059" : "TN", + "holdout-preserve-060" : "TN", + "holdout-preserve-061" : "TN", + "holdout-preserve-062" : "TN", + "holdout-preserve-063" : "TN", + "holdout-preserve-064" : "TN", + "holdout-preserve-065" : "TN", + "holdout-preserve-066" : "TN", + "holdout-preserve-067" : "TN", + "holdout-preserve-068" : "TN", + "holdout-preserve-069" : "TN", + "holdout-preserve-070" : "TN", + "holdout-preserve-071" : "TN", + "holdout-preserve-072" : "TN", + "holdout-preserve-073" : "TN", + "holdout-preserve-074" : "TN", + "holdout-preserve-075" : "TN", + "holdout-preserve-076" : "TN", + "holdout-preserve-077" : "TN", + "holdout-preserve-078" : "TN", + "holdout-preserve-079" : "TN", + "holdout-preserve-080" : "TN", + "holdout-repair-001" : "TP", + "holdout-repair-002" : "TP", + "holdout-repair-003" : "TP", + "holdout-repair-004" : "TP", + "holdout-repair-005" : "TP", + "holdout-repair-006" : "TP", + "holdout-repair-007" : "TP", + "holdout-repair-008" : "TP", + "holdout-repair-009" : "TP", + "holdout-repair-010" : "TP", + "holdout-repair-011" : "TP", + "holdout-repair-012" : "TP", + "holdout-repair-013" : "TP", + "holdout-repair-014" : "TP", + "holdout-repair-015" : "TP", + "holdout-repair-016" : "TP", + "holdout-repair-017" : "TP", + "holdout-repair-018" : "TP", + "holdout-repair-019" : "TP", + "holdout-repair-020" : "TP" + }, + "trueNegatives" : 80, + "truePositives" : 20, + "unauthorizedSourceChangeRate" : { + "count" : 0, + "interval" : { + "lower" : 0, + "upper" : 0.04581795177789462 + }, + "rate" : 0, + "total" : 80 + }, + "unauthorizedSourceChanges" : 0, + "unsupportedTrials" : 0 + }, + "metadata" : { + "architecture" : "arm64", + "comparisonStatus" : "not_run", + "corpusManifestSha256" : "febe8160b901532305809eb11850a463f7522864699bf8f1dbc5ed675c389772", + "corpusVersion" : "v1", + "detectorThresholds" : { + "consecutiveThreshold" : 1, + "detectionThreshold" : 3, + "lowConfidenceConfirmations" : 2, + "lowConfidenceMaxLength" : 3, + "shortWordSuppressionContextWindow" : 6, + "shortWordSuppressionLength" : 2, + "shortWordSuppressionMinValidContext" : 2 + }, + "dictionaryMode" : "text-fallback-test", + "keyboardTypes" : [ + 91 + ], + "layer" : "replay", + "osBuild" : "24G830", + "osVersion" : "15.7.9", + "personalExceptions" : 0, + "profile" : "boundary-layout-only", + "sourceCommit" : "c22be931515086a5935237d14b72d7c50ce2d281", + "sourceTreeState" : "clean", + "swiftVersion" : "Apple Swift version 6.2.4 (swiftlang-6.2.4.1.4 clang-1700.6.4.2)", + "tablesVersion" : "v1" + }, + "runnerChecks" : { + "InputPipelineTestRunner" : { + "failed" : 1, + "failures" : [ + "FAIL: TextCorrector must apply layout-only plan without deleting text" + ], + "log" : "Benchmarks\/results\/logs\/R4-InputPipelineTestRunner.log", + "passed" : 1012 + }, + "LayoutBenchmarkSelfCheck" : { + "failed" : 0, + "failures" : [ + + ], + "log" : "Benchmarks\/results\/logs\/R4-LayoutBenchmark-self-check.log", + "passed" : 106 + }, + "TestRunner" : { + "failed" : 0, + "failures" : [ + + ], + "log" : "Benchmarks\/results\/logs\/R4-TestRunner.log", + "passed" : 455 + } + }, + "schemaVersion" : 2 +} \ No newline at end of file diff --git a/Benchmarks/baselines/v2/corpus-v1/baseline.md b/Benchmarks/baselines/v2/corpus-v1/baseline.md new file mode 100644 index 0000000..5cc19d2 --- /dev/null +++ b/Benchmarks/baselines/v2/corpus-v1/baseline.md @@ -0,0 +1,66 @@ +# SwitchFix Baseline v2/corpus-v1 + +Generated by `LayoutBenchmark record-baseline`; every value below is measured or read from the corpus. + +- **Recorded**: `2026-10-19T08:20:47Z` +- **Source**: `c22be931515086a5935237d14b72d7c50ce2d281` (clean) +- **Environment**: macOS 15.7.9 (24G830), arm64, keyboard types 91, Apple Swift version 6.2.4 (swiftlang-6.2.4.1.4 clang-1700.6.4.2) +- **Profile**: `boundary-layout-only` (layer `replay`), dictionaries `text-fallback-test`, 0 personal exceptions, comparison `not_run` +- **Corpus**: `v1` (manifest SHA-256 `febe8160b901532305809eb11850a463f7522864699bf8f1dbc5ed675c389772`), tables `v1` +- **Detector thresholds**: consecutiveThreshold=1, detectionThreshold=3, lowConfidenceConfirmations=2, lowConfidenceMaxLength=3, shortWordSuppressionContextWindow=6, shortWordSuppressionLength=2, shortWordSuppressionMinValidContext=2 + +## Runner Results + +| Runner | Passed | Failed | Failures | Log | +| --- | --- | --- | --- | --- | +| `InputPipelineTestRunner` | 1012 | 1 | FAIL: TextCorrector must apply layout-only plan without deleting text | `Benchmarks/results/logs/R4-InputPipelineTestRunner.log` | +| `LayoutBenchmarkSelfCheck` | 106 | 0 | none | `Benchmarks/results/logs/R4-LayoutBenchmark-self-check.log` | +| `TestRunner` | 455 | 0 | none | `Benchmarks/results/logs/R4-TestRunner.log` | + +## Aggregate Results + +| Split | Trials | Groups | Complete | Pos / Neg | TP | FN | Text FP | Layout FP | Recall (95% CI) | Text-Damage FP (95% CI) | Layout-Only FP (95% CI) | Precision (95% CI) | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| **Development** | 100 | 100 | yes | 20 / 80 | 20 | 0 | 1 | 0 | 100.0% [83.9%, 100.0%] | 1.25% [0.2%, 6.7%] | 0.00% [0.0%, 4.6%] | 95.2% [77.3%, 99.2%] | +| **Holdout** | 100 | 100 | yes | 20 / 80 | 20 | 0 | 0 | 0 | 100.0% [83.9%, 100.0%] | 0.00% [0.0%, 4.6%] | 0.00% [0.0%, 4.6%] | 100.0% [83.9%, 100.0%] | +| **Combined** | 200 | 200 | yes | 40 / 160 | 40 | 0 | 1 | 0 | 100.0% [91.2%, 100.0%] | 0.62% [0.1%, 3.5%] | 0.00% [0.0%, 2.3%] | 97.6% [87.4%, 99.6%] | + +## Direction Breakdown (Combined) + +| Direction | Trials | TP | FN | TN | Text FP | Layout FP | Errors | Unsupported | Recall | Text-Damage FP | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| EN -> UK | 100 | 20 | 0 | 79 | 1 | 0 | 0 | 0 | 100.0% | 1.25% | +| UK -> EN | 100 | 20 | 0 | 80 | 0 | 0 | 0 | 0 | 100.0% | 0.00% | + +## Category Breakdown (Combined) + +| Category | Trials | TP | FN | TN | Text FP | Layout FP | +| --- | --- | --- | --- | --- | --- | --- | +| acronym | 14 | 0 | 0 | 14 | 0 | 0 | +| ambiguous-short | 20 | 0 | 0 | 20 | 0 | 0 | +| apostrophe | 8 | 0 | 0 | 8 | 0 | 0 | +| back-to-back | 4 | 4 | 0 | 0 | 0 | 0 | +| capitalized | 4 | 4 | 0 | 0 | 0 | 0 | +| code-token | 8 | 0 | 0 | 8 | 0 | 0 | +| contraction | 4 | 0 | 0 | 4 | 0 | 0 | +| email | 2 | 0 | 0 | 2 | 0 | 0 | +| filename | 2 | 0 | 0 | 2 | 0 | 0 | +| gibberish | 8 | 0 | 0 | 8 | 0 | 0 | +| isolated-char | 6 | 0 | 0 | 6 | 0 | 0 | +| isolated-symbol | 4 | 0 | 0 | 4 | 0 | 0 | +| natural-word | 24 | 0 | 0 | 24 | 0 | 0 | +| path | 2 | 0 | 0 | 1 | 1 | 0 | +| phrase | 4 | 0 | 0 | 4 | 0 | 0 | +| proper-noun | 16 | 0 | 0 | 16 | 0 | 0 | +| punctuation | 8 | 0 | 0 | 8 | 0 | 0 | +| question | 4 | 4 | 0 | 0 | 0 | 0 | +| short-word | 24 | 0 | 0 | 24 | 0 | 0 | +| single-word | 28 | 28 | 0 | 0 | 0 | 0 | +| symbol | 4 | 0 | 0 | 4 | 0 | 0 | +| url | 2 | 0 | 0 | 2 | 0 | 0 | + +## Trials That Did Not Pass + +| Trial | Label | Category | Outcome | Expected | Observed | Reason codes | +| --- | --- | --- | --- | --- | --- | --- | +| dev-preserve-041 | preserve | path | FP_text_damage | `/usr/local/bin ` @ com.apple.keylayout.ABC | `/гик/local/bin ` @ com.apple.keylayout.ABC | | diff --git a/Benchmarks/baselines/v2/corpus-v2/baseline.json b/Benchmarks/baselines/v2/corpus-v2/baseline.json new file mode 100644 index 0000000..803842d --- /dev/null +++ b/Benchmarks/baselines/v2/corpus-v2/baseline.json @@ -0,0 +1,1699 @@ +{ + "baselineVersion" : "v2\/corpus-v2", + "combined" : { + "categoryBreakdown" : { + "acronym" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 14, + "nPositive" : 0, + "textFP" : 0, + "tn" : 14, + "total" : 14, + "tp" : 0, + "unsupported" : 0 + }, + "ambiguous-short" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 20, + "nPositive" : 0, + "textFP" : 0, + "tn" : 20, + "total" : 20, + "tp" : 0, + "unsupported" : 0 + }, + "apostrophe" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 8, + "nPositive" : 0, + "textFP" : 0, + "tn" : 8, + "total" : 8, + "tp" : 0, + "unsupported" : 0 + }, + "back-to-back" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 0, + "nPositive" : 4, + "textFP" : 0, + "tn" : 0, + "total" : 4, + "tp" : 4, + "unsupported" : 0 + }, + "capitalized" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 0, + "nPositive" : 4, + "textFP" : 0, + "tn" : 0, + "total" : 4, + "tp" : 4, + "unsupported" : 0 + }, + "code-token" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 8, + "nPositive" : 0, + "textFP" : 0, + "tn" : 8, + "total" : 8, + "tp" : 0, + "unsupported" : 0 + }, + "contraction" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 4, + "nPositive" : 0, + "textFP" : 0, + "tn" : 4, + "total" : 4, + "tp" : 0, + "unsupported" : 0 + }, + "cross-layout-collision" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 20, + "nPositive" : 0, + "textFP" : 7, + "tn" : 13, + "total" : 20, + "tp" : 0, + "unsupported" : 0 + }, + "email" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 2, + "nPositive" : 0, + "textFP" : 0, + "tn" : 2, + "total" : 2, + "tp" : 0, + "unsupported" : 0 + }, + "filename" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 2, + "nPositive" : 0, + "textFP" : 0, + "tn" : 2, + "total" : 2, + "tp" : 0, + "unsupported" : 0 + }, + "gibberish" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 8, + "nPositive" : 0, + "textFP" : 0, + "tn" : 8, + "total" : 8, + "tp" : 0, + "unsupported" : 0 + }, + "isolated-char" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 6, + "nPositive" : 0, + "textFP" : 0, + "tn" : 6, + "total" : 6, + "tp" : 0, + "unsupported" : 0 + }, + "isolated-symbol" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 4, + "nPositive" : 0, + "textFP" : 0, + "tn" : 4, + "total" : 4, + "tp" : 0, + "unsupported" : 0 + }, + "natural-word" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 24, + "nPositive" : 0, + "textFP" : 0, + "tn" : 24, + "total" : 24, + "tp" : 0, + "unsupported" : 0 + }, + "path" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 2, + "nPositive" : 0, + "textFP" : 1, + "tn" : 1, + "total" : 2, + "tp" : 0, + "unsupported" : 0 + }, + "phrase" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 4, + "nPositive" : 0, + "textFP" : 0, + "tn" : 4, + "total" : 4, + "tp" : 0, + "unsupported" : 0 + }, + "proper-noun" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 16, + "nPositive" : 0, + "textFP" : 0, + "tn" : 16, + "total" : 16, + "tp" : 0, + "unsupported" : 0 + }, + "punctuation" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 8, + "nPositive" : 0, + "textFP" : 0, + "tn" : 8, + "total" : 8, + "tp" : 0, + "unsupported" : 0 + }, + "question" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 0, + "nPositive" : 4, + "textFP" : 0, + "tn" : 0, + "total" : 4, + "tp" : 4, + "unsupported" : 0 + }, + "short-word" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 24, + "nPositive" : 0, + "textFP" : 0, + "tn" : 24, + "total" : 24, + "tp" : 0, + "unsupported" : 0 + }, + "single-word" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 0, + "nPositive" : 28, + "textFP" : 0, + "tn" : 0, + "total" : 28, + "tp" : 28, + "unsupported" : 0 + }, + "symbol" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 4, + "nPositive" : 0, + "textFP" : 0, + "tn" : 4, + "total" : 4, + "tp" : 0, + "unsupported" : 0 + }, + "url" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 2, + "nPositive" : 0, + "textFP" : 0, + "tn" : 2, + "total" : 2, + "tp" : 0, + "unsupported" : 0 + } + }, + "complete" : true, + "completedTrials" : 220, + "coverage" : { + "count" : 220, + "interval" : { + "lower" : 0.9828385523848673, + "upper" : 1 + }, + "rate" : 1, + "total" : 220 + }, + "directionBreakdown" : { + "EN -> UK" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 90, + "nPositive" : 20, + "textFP" : 8, + "tn" : 82, + "total" : 110, + "tp" : 20, + "unsupported" : 0 + }, + "UK -> EN" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 90, + "nPositive" : 20, + "textFP" : 0, + "tn" : 90, + "total" : 110, + "tp" : 20, + "unsupported" : 0 + } + }, + "errorTrials" : 0, + "falseNegatives" : 0, + "fnError" : 0, + "fnNoAction" : 0, + "fnTransientDamage" : 0, + "fnUnsafeMutation" : 0, + "fnWrongRepair" : 0, + "fnWrongSource" : 0, + "groupCount" : 216, + "layoutOnlyFPRate" : { + "count" : 0, + "interval" : { + "lower" : 0, + "upper" : 0.020895414737293398 + }, + "rate" : 0, + "total" : 180 + }, + "layoutOnlyFPs" : 0, + "missingTrials" : 0, + "nAbstain" : 40, + "nNegative" : 180, + "nPositive" : 40, + "nPreserve" : 140, + "precision" : { + "count" : 40, + "interval" : { + "lower" : 0.7042220165092958, + "upper" : 0.9130447496869889 + }, + "rate" : 0.8333333333333334, + "total" : 48 + }, + "reasonCodeCounts" : { + + }, + "recall" : { + "count" : 40, + "interval" : { + "lower" : 0.9123787238495881, + "upper" : 1 + }, + "rate" : 1, + "total" : 40 + }, + "repairAttempts" : 48, + "schemaVersion" : 2, + "textDamageFPRate" : { + "count" : 8, + "interval" : { + "lower" : 0.02269012357466975, + "upper" : 0.08523680985264202 + }, + "rate" : 0.044444444444444446, + "total" : 180 + }, + "textDamageFPs" : 8, + "totalFPRate" : { + "count" : 8, + "interval" : { + "lower" : 0.02269012357466975, + "upper" : 0.08523680985264202 + }, + "rate" : 0.044444444444444446, + "total" : 180 + }, + "totalFPs" : 8, + "totalTrials" : 220, + "trialOutcomes" : { + "dev-abstain-081" : "TN", + "dev-abstain-082" : "TN", + "dev-abstain-083" : "TN", + "dev-abstain-084" : "TN", + "dev-abstain-085" : "TN", + "dev-abstain-086" : "TN", + "dev-abstain-087" : "TN", + "dev-abstain-088" : "TN", + "dev-abstain-089" : "TN", + "dev-abstain-090" : "TN", + "dev-abstain-091" : "TN", + "dev-abstain-092" : "TN", + "dev-abstain-093" : "TN", + "dev-abstain-094" : "TN", + "dev-abstain-095" : "TN", + "dev-abstain-096" : "TN", + "dev-abstain-097" : "TN", + "dev-abstain-098" : "TN", + "dev-abstain-099" : "TN", + "dev-abstain-100" : "TN", + "dev-preserve-021" : "TN", + "dev-preserve-022" : "TN", + "dev-preserve-023" : "TN", + "dev-preserve-024" : "TN", + "dev-preserve-025" : "TN", + "dev-preserve-026" : "TN", + "dev-preserve-027" : "TN", + "dev-preserve-028" : "TN", + "dev-preserve-029" : "TN", + "dev-preserve-030" : "TN", + "dev-preserve-031" : "TN", + "dev-preserve-032" : "TN", + "dev-preserve-033" : "TN", + "dev-preserve-034" : "TN", + "dev-preserve-035" : "TN", + "dev-preserve-036" : "TN", + "dev-preserve-037" : "TN", + "dev-preserve-038" : "TN", + "dev-preserve-039" : "TN", + "dev-preserve-040" : "TN", + "dev-preserve-041" : "FP_text_damage", + "dev-preserve-042" : "TN", + "dev-preserve-043" : "TN", + "dev-preserve-044" : "TN", + "dev-preserve-045" : "TN", + "dev-preserve-046" : "TN", + "dev-preserve-047" : "TN", + "dev-preserve-048" : "TN", + "dev-preserve-049" : "TN", + "dev-preserve-050" : "TN", + "dev-preserve-051" : "TN", + "dev-preserve-052" : "TN", + "dev-preserve-053" : "TN", + "dev-preserve-054" : "TN", + "dev-preserve-055" : "TN", + "dev-preserve-056" : "TN", + "dev-preserve-057" : "TN", + "dev-preserve-058" : "TN", + "dev-preserve-059" : "TN", + "dev-preserve-060" : "TN", + "dev-preserve-061" : "TN", + "dev-preserve-062" : "TN", + "dev-preserve-063" : "TN", + "dev-preserve-064" : "TN", + "dev-preserve-065" : "TN", + "dev-preserve-066" : "TN", + "dev-preserve-067" : "TN", + "dev-preserve-068" : "TN", + "dev-preserve-069" : "TN", + "dev-preserve-070" : "TN", + "dev-preserve-071" : "TN", + "dev-preserve-072" : "TN", + "dev-preserve-073" : "TN", + "dev-preserve-074" : "TN", + "dev-preserve-075" : "TN", + "dev-preserve-076" : "TN", + "dev-preserve-077" : "TN", + "dev-preserve-078" : "TN", + "dev-preserve-079" : "TN", + "dev-preserve-080" : "TN", + "dev-preserve-101" : "TN", + "dev-preserve-102" : "FP_text_damage", + "dev-preserve-103" : "FP_text_damage", + "dev-preserve-104" : "TN", + "dev-preserve-105" : "FP_text_damage", + "dev-preserve-106" : "TN", + "dev-preserve-107" : "TN", + "dev-preserve-108" : "TN", + "dev-preserve-109" : "TN", + "dev-preserve-110" : "TN", + "dev-repair-001" : "TP", + "dev-repair-002" : "TP", + "dev-repair-003" : "TP", + "dev-repair-004" : "TP", + "dev-repair-005" : "TP", + "dev-repair-006" : "TP", + "dev-repair-007" : "TP", + "dev-repair-008" : "TP", + "dev-repair-009" : "TP", + "dev-repair-010" : "TP", + "dev-repair-011" : "TP", + "dev-repair-012" : "TP", + "dev-repair-013" : "TP", + "dev-repair-014" : "TP", + "dev-repair-015" : "TP", + "dev-repair-016" : "TP", + "dev-repair-017" : "TP", + "dev-repair-018" : "TP", + "dev-repair-019" : "TP", + "dev-repair-020" : "TP", + "holdout-abstain-081" : "TN", + "holdout-abstain-082" : "TN", + "holdout-abstain-083" : "TN", + "holdout-abstain-084" : "TN", + "holdout-abstain-085" : "TN", + "holdout-abstain-086" : "TN", + "holdout-abstain-087" : "TN", + "holdout-abstain-088" : "TN", + "holdout-abstain-089" : "TN", + "holdout-abstain-090" : "TN", + "holdout-abstain-091" : "TN", + "holdout-abstain-092" : "TN", + "holdout-abstain-093-v2" : "TN", + "holdout-abstain-094-v2" : "TN", + "holdout-abstain-095" : "TN", + "holdout-abstain-096" : "TN", + "holdout-abstain-097" : "TN", + "holdout-abstain-098-v2" : "TN", + "holdout-abstain-099" : "TN", + "holdout-abstain-100" : "TN", + "holdout-preserve-021" : "TN", + "holdout-preserve-022" : "TN", + "holdout-preserve-023" : "TN", + "holdout-preserve-024" : "TN", + "holdout-preserve-025" : "TN", + "holdout-preserve-026" : "TN", + "holdout-preserve-027" : "TN", + "holdout-preserve-028" : "TN", + "holdout-preserve-029" : "TN", + "holdout-preserve-030" : "TN", + "holdout-preserve-031" : "TN", + "holdout-preserve-032" : "TN", + "holdout-preserve-033" : "TN", + "holdout-preserve-034" : "TN", + "holdout-preserve-035" : "TN", + "holdout-preserve-036" : "TN", + "holdout-preserve-037" : "TN", + "holdout-preserve-038" : "TN", + "holdout-preserve-039" : "TN", + "holdout-preserve-040" : "TN", + "holdout-preserve-041" : "TN", + "holdout-preserve-042" : "TN", + "holdout-preserve-043" : "TN", + "holdout-preserve-044" : "TN", + "holdout-preserve-045" : "TN", + "holdout-preserve-046" : "TN", + "holdout-preserve-047" : "TN", + "holdout-preserve-048" : "TN", + "holdout-preserve-049" : "TN", + "holdout-preserve-050" : "TN", + "holdout-preserve-051" : "TN", + "holdout-preserve-052" : "TN", + "holdout-preserve-053" : "TN", + "holdout-preserve-054" : "TN", + "holdout-preserve-055" : "TN", + "holdout-preserve-056" : "TN", + "holdout-preserve-057" : "TN", + "holdout-preserve-058-v2" : "TN", + "holdout-preserve-059" : "TN", + "holdout-preserve-060" : "TN", + "holdout-preserve-061" : "TN", + "holdout-preserve-062" : "TN", + "holdout-preserve-063" : "TN", + "holdout-preserve-064" : "TN", + "holdout-preserve-065" : "TN", + "holdout-preserve-066" : "TN", + "holdout-preserve-067" : "TN", + "holdout-preserve-068" : "TN", + "holdout-preserve-069" : "TN", + "holdout-preserve-070" : "TN", + "holdout-preserve-071" : "TN", + "holdout-preserve-072" : "TN", + "holdout-preserve-073" : "TN", + "holdout-preserve-074" : "TN", + "holdout-preserve-075" : "TN", + "holdout-preserve-076" : "TN", + "holdout-preserve-077" : "TN", + "holdout-preserve-078" : "TN", + "holdout-preserve-079" : "TN", + "holdout-preserve-080" : "TN", + "holdout-preserve-101" : "FP_text_damage", + "holdout-preserve-102" : "FP_text_damage", + "holdout-preserve-103" : "TN", + "holdout-preserve-104" : "FP_text_damage", + "holdout-preserve-105" : "FP_text_damage", + "holdout-preserve-106" : "TN", + "holdout-preserve-107" : "TN", + "holdout-preserve-108" : "TN", + "holdout-preserve-109" : "TN", + "holdout-preserve-110" : "TN", + "holdout-repair-001" : "TP", + "holdout-repair-002" : "TP", + "holdout-repair-003" : "TP", + "holdout-repair-004" : "TP", + "holdout-repair-005" : "TP", + "holdout-repair-006" : "TP", + "holdout-repair-007" : "TP", + "holdout-repair-008" : "TP", + "holdout-repair-009" : "TP", + "holdout-repair-010" : "TP", + "holdout-repair-011-v2" : "TP", + "holdout-repair-012" : "TP", + "holdout-repair-013" : "TP", + "holdout-repair-014" : "TP", + "holdout-repair-015" : "TP", + "holdout-repair-016" : "TP", + "holdout-repair-017" : "TP", + "holdout-repair-018" : "TP", + "holdout-repair-019" : "TP", + "holdout-repair-020" : "TP" + }, + "trueNegatives" : 172, + "truePositives" : 40, + "unauthorizedSourceChangeRate" : { + "count" : 0, + "interval" : { + "lower" : 0, + "upper" : 0.020895414737293398 + }, + "rate" : 0, + "total" : 180 + }, + "unauthorizedSourceChanges" : 0, + "unsupportedTrials" : 0 + }, + "created" : "2026-10-19T08:20:34Z", + "development" : { + "categoryBreakdown" : { + "acronym" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 7, + "nPositive" : 0, + "textFP" : 0, + "tn" : 7, + "total" : 7, + "tp" : 0, + "unsupported" : 0 + }, + "ambiguous-short" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 10, + "nPositive" : 0, + "textFP" : 0, + "tn" : 10, + "total" : 10, + "tp" : 0, + "unsupported" : 0 + }, + "apostrophe" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 4, + "nPositive" : 0, + "textFP" : 0, + "tn" : 4, + "total" : 4, + "tp" : 0, + "unsupported" : 0 + }, + "back-to-back" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 0, + "nPositive" : 2, + "textFP" : 0, + "tn" : 0, + "total" : 2, + "tp" : 2, + "unsupported" : 0 + }, + "capitalized" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 0, + "nPositive" : 2, + "textFP" : 0, + "tn" : 0, + "total" : 2, + "tp" : 2, + "unsupported" : 0 + }, + "code-token" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 4, + "nPositive" : 0, + "textFP" : 0, + "tn" : 4, + "total" : 4, + "tp" : 0, + "unsupported" : 0 + }, + "contraction" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 2, + "nPositive" : 0, + "textFP" : 0, + "tn" : 2, + "total" : 2, + "tp" : 0, + "unsupported" : 0 + }, + "cross-layout-collision" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 10, + "nPositive" : 0, + "textFP" : 3, + "tn" : 7, + "total" : 10, + "tp" : 0, + "unsupported" : 0 + }, + "email" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 1, + "nPositive" : 0, + "textFP" : 0, + "tn" : 1, + "total" : 1, + "tp" : 0, + "unsupported" : 0 + }, + "filename" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 1, + "nPositive" : 0, + "textFP" : 0, + "tn" : 1, + "total" : 1, + "tp" : 0, + "unsupported" : 0 + }, + "gibberish" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 4, + "nPositive" : 0, + "textFP" : 0, + "tn" : 4, + "total" : 4, + "tp" : 0, + "unsupported" : 0 + }, + "isolated-char" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 3, + "nPositive" : 0, + "textFP" : 0, + "tn" : 3, + "total" : 3, + "tp" : 0, + "unsupported" : 0 + }, + "isolated-symbol" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 2, + "nPositive" : 0, + "textFP" : 0, + "tn" : 2, + "total" : 2, + "tp" : 0, + "unsupported" : 0 + }, + "natural-word" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 12, + "nPositive" : 0, + "textFP" : 0, + "tn" : 12, + "total" : 12, + "tp" : 0, + "unsupported" : 0 + }, + "path" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 1, + "nPositive" : 0, + "textFP" : 1, + "tn" : 0, + "total" : 1, + "tp" : 0, + "unsupported" : 0 + }, + "phrase" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 2, + "nPositive" : 0, + "textFP" : 0, + "tn" : 2, + "total" : 2, + "tp" : 0, + "unsupported" : 0 + }, + "proper-noun" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 8, + "nPositive" : 0, + "textFP" : 0, + "tn" : 8, + "total" : 8, + "tp" : 0, + "unsupported" : 0 + }, + "punctuation" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 4, + "nPositive" : 0, + "textFP" : 0, + "tn" : 4, + "total" : 4, + "tp" : 0, + "unsupported" : 0 + }, + "question" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 0, + "nPositive" : 2, + "textFP" : 0, + "tn" : 0, + "total" : 2, + "tp" : 2, + "unsupported" : 0 + }, + "short-word" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 12, + "nPositive" : 0, + "textFP" : 0, + "tn" : 12, + "total" : 12, + "tp" : 0, + "unsupported" : 0 + }, + "single-word" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 0, + "nPositive" : 14, + "textFP" : 0, + "tn" : 0, + "total" : 14, + "tp" : 14, + "unsupported" : 0 + }, + "symbol" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 2, + "nPositive" : 0, + "textFP" : 0, + "tn" : 2, + "total" : 2, + "tp" : 0, + "unsupported" : 0 + }, + "url" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 1, + "nPositive" : 0, + "textFP" : 0, + "tn" : 1, + "total" : 1, + "tp" : 0, + "unsupported" : 0 + } + }, + "complete" : true, + "completedTrials" : 110, + "coverage" : { + "count" : 110, + "interval" : { + "lower" : 0.9662561972725762, + "upper" : 0.9999999999999999 + }, + "rate" : 1, + "total" : 110 + }, + "directionBreakdown" : { + "EN -> UK" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 45, + "nPositive" : 10, + "textFP" : 4, + "tn" : 41, + "total" : 55, + "tp" : 10, + "unsupported" : 0 + }, + "UK -> EN" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 45, + "nPositive" : 10, + "textFP" : 0, + "tn" : 45, + "total" : 55, + "tp" : 10, + "unsupported" : 0 + } + }, + "errorTrials" : 0, + "falseNegatives" : 0, + "fnError" : 0, + "fnNoAction" : 0, + "fnTransientDamage" : 0, + "fnUnsafeMutation" : 0, + "fnWrongRepair" : 0, + "fnWrongSource" : 0, + "groupCount" : 108, + "layoutOnlyFPRate" : { + "count" : 0, + "interval" : { + "lower" : 0, + "upper" : 0.04093546593638175 + }, + "rate" : 0, + "total" : 90 + }, + "layoutOnlyFPs" : 0, + "missingTrials" : 0, + "nAbstain" : 20, + "nNegative" : 90, + "nPositive" : 20, + "nPreserve" : 70, + "precision" : { + "count" : 20, + "interval" : { + "lower" : 0.6414697357497665, + "upper" : 0.9332131168647311 + }, + "rate" : 0.8333333333333334, + "total" : 24 + }, + "reasonCodeCounts" : { + + }, + "recall" : { + "count" : 20, + "interval" : { + "lower" : 0.8388753915139584, + "upper" : 1 + }, + "rate" : 1, + "total" : 20 + }, + "repairAttempts" : 24, + "schemaVersion" : 2, + "textDamageFPRate" : { + "count" : 4, + "interval" : { + "lower" : 0.017417273034118934, + "upper" : 0.10876837370791775 + }, + "rate" : 0.044444444444444446, + "total" : 90 + }, + "textDamageFPs" : 4, + "totalFPRate" : { + "count" : 4, + "interval" : { + "lower" : 0.017417273034118934, + "upper" : 0.10876837370791775 + }, + "rate" : 0.044444444444444446, + "total" : 90 + }, + "totalFPs" : 4, + "totalTrials" : 110, + "trialOutcomes" : { + "dev-abstain-081" : "TN", + "dev-abstain-082" : "TN", + "dev-abstain-083" : "TN", + "dev-abstain-084" : "TN", + "dev-abstain-085" : "TN", + "dev-abstain-086" : "TN", + "dev-abstain-087" : "TN", + "dev-abstain-088" : "TN", + "dev-abstain-089" : "TN", + "dev-abstain-090" : "TN", + "dev-abstain-091" : "TN", + "dev-abstain-092" : "TN", + "dev-abstain-093" : "TN", + "dev-abstain-094" : "TN", + "dev-abstain-095" : "TN", + "dev-abstain-096" : "TN", + "dev-abstain-097" : "TN", + "dev-abstain-098" : "TN", + "dev-abstain-099" : "TN", + "dev-abstain-100" : "TN", + "dev-preserve-021" : "TN", + "dev-preserve-022" : "TN", + "dev-preserve-023" : "TN", + "dev-preserve-024" : "TN", + "dev-preserve-025" : "TN", + "dev-preserve-026" : "TN", + "dev-preserve-027" : "TN", + "dev-preserve-028" : "TN", + "dev-preserve-029" : "TN", + "dev-preserve-030" : "TN", + "dev-preserve-031" : "TN", + "dev-preserve-032" : "TN", + "dev-preserve-033" : "TN", + "dev-preserve-034" : "TN", + "dev-preserve-035" : "TN", + "dev-preserve-036" : "TN", + "dev-preserve-037" : "TN", + "dev-preserve-038" : "TN", + "dev-preserve-039" : "TN", + "dev-preserve-040" : "TN", + "dev-preserve-041" : "FP_text_damage", + "dev-preserve-042" : "TN", + "dev-preserve-043" : "TN", + "dev-preserve-044" : "TN", + "dev-preserve-045" : "TN", + "dev-preserve-046" : "TN", + "dev-preserve-047" : "TN", + "dev-preserve-048" : "TN", + "dev-preserve-049" : "TN", + "dev-preserve-050" : "TN", + "dev-preserve-051" : "TN", + "dev-preserve-052" : "TN", + "dev-preserve-053" : "TN", + "dev-preserve-054" : "TN", + "dev-preserve-055" : "TN", + "dev-preserve-056" : "TN", + "dev-preserve-057" : "TN", + "dev-preserve-058" : "TN", + "dev-preserve-059" : "TN", + "dev-preserve-060" : "TN", + "dev-preserve-061" : "TN", + "dev-preserve-062" : "TN", + "dev-preserve-063" : "TN", + "dev-preserve-064" : "TN", + "dev-preserve-065" : "TN", + "dev-preserve-066" : "TN", + "dev-preserve-067" : "TN", + "dev-preserve-068" : "TN", + "dev-preserve-069" : "TN", + "dev-preserve-070" : "TN", + "dev-preserve-071" : "TN", + "dev-preserve-072" : "TN", + "dev-preserve-073" : "TN", + "dev-preserve-074" : "TN", + "dev-preserve-075" : "TN", + "dev-preserve-076" : "TN", + "dev-preserve-077" : "TN", + "dev-preserve-078" : "TN", + "dev-preserve-079" : "TN", + "dev-preserve-080" : "TN", + "dev-preserve-101" : "TN", + "dev-preserve-102" : "FP_text_damage", + "dev-preserve-103" : "FP_text_damage", + "dev-preserve-104" : "TN", + "dev-preserve-105" : "FP_text_damage", + "dev-preserve-106" : "TN", + "dev-preserve-107" : "TN", + "dev-preserve-108" : "TN", + "dev-preserve-109" : "TN", + "dev-preserve-110" : "TN", + "dev-repair-001" : "TP", + "dev-repair-002" : "TP", + "dev-repair-003" : "TP", + "dev-repair-004" : "TP", + "dev-repair-005" : "TP", + "dev-repair-006" : "TP", + "dev-repair-007" : "TP", + "dev-repair-008" : "TP", + "dev-repair-009" : "TP", + "dev-repair-010" : "TP", + "dev-repair-011" : "TP", + "dev-repair-012" : "TP", + "dev-repair-013" : "TP", + "dev-repair-014" : "TP", + "dev-repair-015" : "TP", + "dev-repair-016" : "TP", + "dev-repair-017" : "TP", + "dev-repair-018" : "TP", + "dev-repair-019" : "TP", + "dev-repair-020" : "TP" + }, + "trueNegatives" : 86, + "truePositives" : 20, + "unauthorizedSourceChangeRate" : { + "count" : 0, + "interval" : { + "lower" : 0, + "upper" : 0.04093546593638175 + }, + "rate" : 0, + "total" : 90 + }, + "unauthorizedSourceChanges" : 0, + "unsupportedTrials" : 0 + }, + "holdout" : { + "categoryBreakdown" : { + "acronym" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 7, + "nPositive" : 0, + "textFP" : 0, + "tn" : 7, + "total" : 7, + "tp" : 0, + "unsupported" : 0 + }, + "ambiguous-short" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 10, + "nPositive" : 0, + "textFP" : 0, + "tn" : 10, + "total" : 10, + "tp" : 0, + "unsupported" : 0 + }, + "apostrophe" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 4, + "nPositive" : 0, + "textFP" : 0, + "tn" : 4, + "total" : 4, + "tp" : 0, + "unsupported" : 0 + }, + "back-to-back" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 0, + "nPositive" : 2, + "textFP" : 0, + "tn" : 0, + "total" : 2, + "tp" : 2, + "unsupported" : 0 + }, + "capitalized" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 0, + "nPositive" : 2, + "textFP" : 0, + "tn" : 0, + "total" : 2, + "tp" : 2, + "unsupported" : 0 + }, + "code-token" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 4, + "nPositive" : 0, + "textFP" : 0, + "tn" : 4, + "total" : 4, + "tp" : 0, + "unsupported" : 0 + }, + "contraction" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 2, + "nPositive" : 0, + "textFP" : 0, + "tn" : 2, + "total" : 2, + "tp" : 0, + "unsupported" : 0 + }, + "cross-layout-collision" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 10, + "nPositive" : 0, + "textFP" : 4, + "tn" : 6, + "total" : 10, + "tp" : 0, + "unsupported" : 0 + }, + "email" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 1, + "nPositive" : 0, + "textFP" : 0, + "tn" : 1, + "total" : 1, + "tp" : 0, + "unsupported" : 0 + }, + "filename" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 1, + "nPositive" : 0, + "textFP" : 0, + "tn" : 1, + "total" : 1, + "tp" : 0, + "unsupported" : 0 + }, + "gibberish" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 4, + "nPositive" : 0, + "textFP" : 0, + "tn" : 4, + "total" : 4, + "tp" : 0, + "unsupported" : 0 + }, + "isolated-char" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 3, + "nPositive" : 0, + "textFP" : 0, + "tn" : 3, + "total" : 3, + "tp" : 0, + "unsupported" : 0 + }, + "isolated-symbol" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 2, + "nPositive" : 0, + "textFP" : 0, + "tn" : 2, + "total" : 2, + "tp" : 0, + "unsupported" : 0 + }, + "natural-word" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 12, + "nPositive" : 0, + "textFP" : 0, + "tn" : 12, + "total" : 12, + "tp" : 0, + "unsupported" : 0 + }, + "path" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 1, + "nPositive" : 0, + "textFP" : 0, + "tn" : 1, + "total" : 1, + "tp" : 0, + "unsupported" : 0 + }, + "phrase" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 2, + "nPositive" : 0, + "textFP" : 0, + "tn" : 2, + "total" : 2, + "tp" : 0, + "unsupported" : 0 + }, + "proper-noun" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 8, + "nPositive" : 0, + "textFP" : 0, + "tn" : 8, + "total" : 8, + "tp" : 0, + "unsupported" : 0 + }, + "punctuation" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 4, + "nPositive" : 0, + "textFP" : 0, + "tn" : 4, + "total" : 4, + "tp" : 0, + "unsupported" : 0 + }, + "question" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 0, + "nPositive" : 2, + "textFP" : 0, + "tn" : 0, + "total" : 2, + "tp" : 2, + "unsupported" : 0 + }, + "short-word" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 12, + "nPositive" : 0, + "textFP" : 0, + "tn" : 12, + "total" : 12, + "tp" : 0, + "unsupported" : 0 + }, + "single-word" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 0, + "nPositive" : 14, + "textFP" : 0, + "tn" : 0, + "total" : 14, + "tp" : 14, + "unsupported" : 0 + }, + "symbol" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 2, + "nPositive" : 0, + "textFP" : 0, + "tn" : 2, + "total" : 2, + "tp" : 0, + "unsupported" : 0 + }, + "url" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 1, + "nPositive" : 0, + "textFP" : 0, + "tn" : 1, + "total" : 1, + "tp" : 0, + "unsupported" : 0 + } + }, + "complete" : true, + "completedTrials" : 110, + "coverage" : { + "count" : 110, + "interval" : { + "lower" : 0.9662561972725762, + "upper" : 0.9999999999999999 + }, + "rate" : 1, + "total" : 110 + }, + "directionBreakdown" : { + "EN -> UK" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 45, + "nPositive" : 10, + "textFP" : 4, + "tn" : 41, + "total" : 55, + "tp" : 10, + "unsupported" : 0 + }, + "UK -> EN" : { + "errors" : 0, + "fn" : 0, + "layoutFP" : 0, + "nNegative" : 45, + "nPositive" : 10, + "textFP" : 0, + "tn" : 45, + "total" : 55, + "tp" : 10, + "unsupported" : 0 + } + }, + "errorTrials" : 0, + "falseNegatives" : 0, + "fnError" : 0, + "fnNoAction" : 0, + "fnTransientDamage" : 0, + "fnUnsafeMutation" : 0, + "fnWrongRepair" : 0, + "fnWrongSource" : 0, + "groupCount" : 108, + "layoutOnlyFPRate" : { + "count" : 0, + "interval" : { + "lower" : 0, + "upper" : 0.04093546593638175 + }, + "rate" : 0, + "total" : 90 + }, + "layoutOnlyFPs" : 0, + "missingTrials" : 0, + "nAbstain" : 20, + "nNegative" : 90, + "nPositive" : 20, + "nPreserve" : 70, + "precision" : { + "count" : 20, + "interval" : { + "lower" : 0.6414697357497665, + "upper" : 0.9332131168647311 + }, + "rate" : 0.8333333333333334, + "total" : 24 + }, + "reasonCodeCounts" : { + + }, + "recall" : { + "count" : 20, + "interval" : { + "lower" : 0.8388753915139584, + "upper" : 1 + }, + "rate" : 1, + "total" : 20 + }, + "repairAttempts" : 24, + "schemaVersion" : 2, + "textDamageFPRate" : { + "count" : 4, + "interval" : { + "lower" : 0.017417273034118934, + "upper" : 0.10876837370791775 + }, + "rate" : 0.044444444444444446, + "total" : 90 + }, + "textDamageFPs" : 4, + "totalFPRate" : { + "count" : 4, + "interval" : { + "lower" : 0.017417273034118934, + "upper" : 0.10876837370791775 + }, + "rate" : 0.044444444444444446, + "total" : 90 + }, + "totalFPs" : 4, + "totalTrials" : 110, + "trialOutcomes" : { + "holdout-abstain-081" : "TN", + "holdout-abstain-082" : "TN", + "holdout-abstain-083" : "TN", + "holdout-abstain-084" : "TN", + "holdout-abstain-085" : "TN", + "holdout-abstain-086" : "TN", + "holdout-abstain-087" : "TN", + "holdout-abstain-088" : "TN", + "holdout-abstain-089" : "TN", + "holdout-abstain-090" : "TN", + "holdout-abstain-091" : "TN", + "holdout-abstain-092" : "TN", + "holdout-abstain-093-v2" : "TN", + "holdout-abstain-094-v2" : "TN", + "holdout-abstain-095" : "TN", + "holdout-abstain-096" : "TN", + "holdout-abstain-097" : "TN", + "holdout-abstain-098-v2" : "TN", + "holdout-abstain-099" : "TN", + "holdout-abstain-100" : "TN", + "holdout-preserve-021" : "TN", + "holdout-preserve-022" : "TN", + "holdout-preserve-023" : "TN", + "holdout-preserve-024" : "TN", + "holdout-preserve-025" : "TN", + "holdout-preserve-026" : "TN", + "holdout-preserve-027" : "TN", + "holdout-preserve-028" : "TN", + "holdout-preserve-029" : "TN", + "holdout-preserve-030" : "TN", + "holdout-preserve-031" : "TN", + "holdout-preserve-032" : "TN", + "holdout-preserve-033" : "TN", + "holdout-preserve-034" : "TN", + "holdout-preserve-035" : "TN", + "holdout-preserve-036" : "TN", + "holdout-preserve-037" : "TN", + "holdout-preserve-038" : "TN", + "holdout-preserve-039" : "TN", + "holdout-preserve-040" : "TN", + "holdout-preserve-041" : "TN", + "holdout-preserve-042" : "TN", + "holdout-preserve-043" : "TN", + "holdout-preserve-044" : "TN", + "holdout-preserve-045" : "TN", + "holdout-preserve-046" : "TN", + "holdout-preserve-047" : "TN", + "holdout-preserve-048" : "TN", + "holdout-preserve-049" : "TN", + "holdout-preserve-050" : "TN", + "holdout-preserve-051" : "TN", + "holdout-preserve-052" : "TN", + "holdout-preserve-053" : "TN", + "holdout-preserve-054" : "TN", + "holdout-preserve-055" : "TN", + "holdout-preserve-056" : "TN", + "holdout-preserve-057" : "TN", + "holdout-preserve-058-v2" : "TN", + "holdout-preserve-059" : "TN", + "holdout-preserve-060" : "TN", + "holdout-preserve-061" : "TN", + "holdout-preserve-062" : "TN", + "holdout-preserve-063" : "TN", + "holdout-preserve-064" : "TN", + "holdout-preserve-065" : "TN", + "holdout-preserve-066" : "TN", + "holdout-preserve-067" : "TN", + "holdout-preserve-068" : "TN", + "holdout-preserve-069" : "TN", + "holdout-preserve-070" : "TN", + "holdout-preserve-071" : "TN", + "holdout-preserve-072" : "TN", + "holdout-preserve-073" : "TN", + "holdout-preserve-074" : "TN", + "holdout-preserve-075" : "TN", + "holdout-preserve-076" : "TN", + "holdout-preserve-077" : "TN", + "holdout-preserve-078" : "TN", + "holdout-preserve-079" : "TN", + "holdout-preserve-080" : "TN", + "holdout-preserve-101" : "FP_text_damage", + "holdout-preserve-102" : "FP_text_damage", + "holdout-preserve-103" : "TN", + "holdout-preserve-104" : "FP_text_damage", + "holdout-preserve-105" : "FP_text_damage", + "holdout-preserve-106" : "TN", + "holdout-preserve-107" : "TN", + "holdout-preserve-108" : "TN", + "holdout-preserve-109" : "TN", + "holdout-preserve-110" : "TN", + "holdout-repair-001" : "TP", + "holdout-repair-002" : "TP", + "holdout-repair-003" : "TP", + "holdout-repair-004" : "TP", + "holdout-repair-005" : "TP", + "holdout-repair-006" : "TP", + "holdout-repair-007" : "TP", + "holdout-repair-008" : "TP", + "holdout-repair-009" : "TP", + "holdout-repair-010" : "TP", + "holdout-repair-011-v2" : "TP", + "holdout-repair-012" : "TP", + "holdout-repair-013" : "TP", + "holdout-repair-014" : "TP", + "holdout-repair-015" : "TP", + "holdout-repair-016" : "TP", + "holdout-repair-017" : "TP", + "holdout-repair-018" : "TP", + "holdout-repair-019" : "TP", + "holdout-repair-020" : "TP" + }, + "trueNegatives" : 86, + "truePositives" : 20, + "unauthorizedSourceChangeRate" : { + "count" : 0, + "interval" : { + "lower" : 0, + "upper" : 0.04093546593638175 + }, + "rate" : 0, + "total" : 90 + }, + "unauthorizedSourceChanges" : 0, + "unsupportedTrials" : 0 + }, + "metadata" : { + "architecture" : "arm64", + "comparisonStatus" : "not_run", + "corpusManifestSha256" : "e211651b52a6fd3a9057108f2dad85fa1794454611dc91e3ef3149e832fe922b", + "corpusVersion" : "v2", + "detectorThresholds" : { + "consecutiveThreshold" : 1, + "detectionThreshold" : 3, + "lowConfidenceConfirmations" : 2, + "lowConfidenceMaxLength" : 3, + "shortWordSuppressionContextWindow" : 6, + "shortWordSuppressionLength" : 2, + "shortWordSuppressionMinValidContext" : 2 + }, + "dictionaryMode" : "text-fallback-test", + "keyboardTypes" : [ + 91 + ], + "layer" : "replay", + "osBuild" : "24G830", + "osVersion" : "15.7.9", + "personalExceptions" : 0, + "profile" : "boundary-layout-only", + "sourceCommit" : "c22be931515086a5935237d14b72d7c50ce2d281", + "sourceTreeState" : "clean", + "swiftVersion" : "Apple Swift version 6.2.4 (swiftlang-6.2.4.1.4 clang-1700.6.4.2)", + "tablesVersion" : "v1" + }, + "runnerChecks" : { + "InputPipelineTestRunner" : { + "failed" : 1, + "failures" : [ + "FAIL: TextCorrector must apply layout-only plan without deleting text" + ], + "log" : "Benchmarks\/results\/logs\/R4-InputPipelineTestRunner.log", + "passed" : 1012 + }, + "LayoutBenchmarkSelfCheck" : { + "failed" : 0, + "failures" : [ + + ], + "log" : "Benchmarks\/results\/logs\/R4-LayoutBenchmark-self-check.log", + "passed" : 106 + }, + "TestRunner" : { + "failed" : 0, + "failures" : [ + + ], + "log" : "Benchmarks\/results\/logs\/R4-TestRunner.log", + "passed" : 455 + } + }, + "schemaVersion" : 2 +} \ No newline at end of file diff --git a/Benchmarks/baselines/v2/corpus-v2/baseline.md b/Benchmarks/baselines/v2/corpus-v2/baseline.md new file mode 100644 index 0000000..4918c53 --- /dev/null +++ b/Benchmarks/baselines/v2/corpus-v2/baseline.md @@ -0,0 +1,74 @@ +# SwitchFix Baseline v2/corpus-v2 + +Generated by `LayoutBenchmark record-baseline`; every value below is measured or read from the corpus. + +- **Recorded**: `2026-10-19T08:20:34Z` +- **Source**: `c22be931515086a5935237d14b72d7c50ce2d281` (clean) +- **Environment**: macOS 15.7.9 (24G830), arm64, keyboard types 91, Apple Swift version 6.2.4 (swiftlang-6.2.4.1.4 clang-1700.6.4.2) +- **Profile**: `boundary-layout-only` (layer `replay`), dictionaries `text-fallback-test`, 0 personal exceptions, comparison `not_run` +- **Corpus**: `v2` (manifest SHA-256 `e211651b52a6fd3a9057108f2dad85fa1794454611dc91e3ef3149e832fe922b`), tables `v1` +- **Detector thresholds**: consecutiveThreshold=1, detectionThreshold=3, lowConfidenceConfirmations=2, lowConfidenceMaxLength=3, shortWordSuppressionContextWindow=6, shortWordSuppressionLength=2, shortWordSuppressionMinValidContext=2 + +## Runner Results + +| Runner | Passed | Failed | Failures | Log | +| --- | --- | --- | --- | --- | +| `InputPipelineTestRunner` | 1012 | 1 | FAIL: TextCorrector must apply layout-only plan without deleting text | `Benchmarks/results/logs/R4-InputPipelineTestRunner.log` | +| `LayoutBenchmarkSelfCheck` | 106 | 0 | none | `Benchmarks/results/logs/R4-LayoutBenchmark-self-check.log` | +| `TestRunner` | 455 | 0 | none | `Benchmarks/results/logs/R4-TestRunner.log` | + +## Aggregate Results + +| Split | Trials | Groups | Complete | Pos / Neg | TP | FN | Text FP | Layout FP | Recall (95% CI) | Text-Damage FP (95% CI) | Layout-Only FP (95% CI) | Precision (95% CI) | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| **Development** | 110 | 108 | yes | 20 / 90 | 20 | 0 | 4 | 0 | 100.0% [83.9%, 100.0%] | 4.44% [1.7%, 10.9%] | 0.00% [0.0%, 4.1%] | 83.3% [64.1%, 93.3%] | +| **Holdout** | 110 | 108 | yes | 20 / 90 | 20 | 0 | 4 | 0 | 100.0% [83.9%, 100.0%] | 4.44% [1.7%, 10.9%] | 0.00% [0.0%, 4.1%] | 83.3% [64.1%, 93.3%] | +| **Combined** | 220 | 216 | yes | 40 / 180 | 40 | 0 | 8 | 0 | 100.0% [91.2%, 100.0%] | 4.44% [2.3%, 8.5%] | 0.00% [0.0%, 2.1%] | 83.3% [70.4%, 91.3%] | + +## Direction Breakdown (Combined) + +| Direction | Trials | TP | FN | TN | Text FP | Layout FP | Errors | Unsupported | Recall | Text-Damage FP | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| EN -> UK | 110 | 20 | 0 | 82 | 8 | 0 | 0 | 0 | 100.0% | 8.89% | +| UK -> EN | 110 | 20 | 0 | 90 | 0 | 0 | 0 | 0 | 100.0% | 0.00% | + +## Category Breakdown (Combined) + +| Category | Trials | TP | FN | TN | Text FP | Layout FP | +| --- | --- | --- | --- | --- | --- | --- | +| acronym | 14 | 0 | 0 | 14 | 0 | 0 | +| ambiguous-short | 20 | 0 | 0 | 20 | 0 | 0 | +| apostrophe | 8 | 0 | 0 | 8 | 0 | 0 | +| back-to-back | 4 | 4 | 0 | 0 | 0 | 0 | +| capitalized | 4 | 4 | 0 | 0 | 0 | 0 | +| code-token | 8 | 0 | 0 | 8 | 0 | 0 | +| contraction | 4 | 0 | 0 | 4 | 0 | 0 | +| cross-layout-collision | 20 | 0 | 0 | 13 | 7 | 0 | +| email | 2 | 0 | 0 | 2 | 0 | 0 | +| filename | 2 | 0 | 0 | 2 | 0 | 0 | +| gibberish | 8 | 0 | 0 | 8 | 0 | 0 | +| isolated-char | 6 | 0 | 0 | 6 | 0 | 0 | +| isolated-symbol | 4 | 0 | 0 | 4 | 0 | 0 | +| natural-word | 24 | 0 | 0 | 24 | 0 | 0 | +| path | 2 | 0 | 0 | 1 | 1 | 0 | +| phrase | 4 | 0 | 0 | 4 | 0 | 0 | +| proper-noun | 16 | 0 | 0 | 16 | 0 | 0 | +| punctuation | 8 | 0 | 0 | 8 | 0 | 0 | +| question | 4 | 4 | 0 | 0 | 0 | 0 | +| short-word | 24 | 0 | 0 | 24 | 0 | 0 | +| single-word | 28 | 28 | 0 | 0 | 0 | 0 | +| symbol | 4 | 0 | 0 | 4 | 0 | 0 | +| url | 2 | 0 | 0 | 2 | 0 | 0 | + +## Trials That Did Not Pass + +| Trial | Label | Category | Outcome | Expected | Observed | Reason codes | +| --- | --- | --- | --- | --- | --- | --- | +| dev-preserve-041 | preserve | path | FP_text_damage | `/usr/local/bin ` @ com.apple.keylayout.ABC | `/гик/local/bin ` @ com.apple.keylayout.ABC | | +| dev-preserve-102 | preserve | cross-layout-collision | FP_text_damage | `yt ` @ com.apple.keylayout.ABC | `не ` @ com.apple.keylayout.ABC | | +| dev-preserve-103 | preserve | cross-layout-collision | FP_text_damage | `nfr ` @ com.apple.keylayout.ABC | `так ` @ com.apple.keylayout.ABC | | +| dev-preserve-105 | preserve | cross-layout-collision | FP_text_damage | `hbr ` @ com.apple.keylayout.ABC | `рік ` @ com.apple.keylayout.ABC | | +| holdout-preserve-101 | preserve | cross-layout-collision | FP_text_damage | `lev ` @ com.apple.keylayout.ABC | `дум ` @ com.apple.keylayout.ABC | | +| holdout-preserve-102 | preserve | cross-layout-collision | FP_text_damage | `ns ` @ com.apple.keylayout.ABC | `ти ` @ com.apple.keylayout.ABC | | +| holdout-preserve-104 | preserve | cross-layout-collision | FP_text_damage | `ufc ` @ com.apple.keylayout.ABC | `гас ` @ com.apple.keylayout.ABC | | +| holdout-preserve-105 | preserve | cross-layout-collision | FP_text_damage | `dsl ` @ com.apple.keylayout.ABC | `вид ` @ com.apple.keylayout.ABC | | diff --git a/Benchmarks/layout-corpus/v1/README.md b/Benchmarks/layout-corpus/v1/README.md new file mode 100644 index 0000000..dfa092c --- /dev/null +++ b/Benchmarks/layout-corpus/v1/README.md @@ -0,0 +1,40 @@ +# Layout Benchmark Corpus v1 + +Version: 1.0 (Frozen Seed Baseline) +Specification: `docs/plans/03-comparative-false-positive-benchmark.md` +Source commit: `b8471649300162827cc4053d553c14a895820e8b` + +## Overview + +This directory contains the frozen 200-trial seed corpus for the SwitchFix comparative layout reliability benchmark: +- `development.jsonl`: 100 trials used during development and regression testing +- `holdout.jsonl`: 100 trials reserved for generalization evaluation +- `manifest.json`: Cryptographic integrity hashes and split distributions + +## Split Structure (Per Split) + +Each split contains exactly 100 trials: +- **20 Repair** trials: Intentional layout errors requiring automatic repair (10 English → Ukrainian, 10 Ukrainian → English) +- **60 Preserve** trials: Intentional correct text requiring preservation with no modifications (30 typed under ABC, 30 typed under Ukrainian) +- **20 Abstain** trials: Ambiguous or unsupported inputs requiring no automatic action (10 typed under ABC, 10 typed under Ukrainian) + +Base group IDs (`groupID`) are completely disjoint across the development and holdout splits. + +## Trial Schema & Provenance + +Every row in the JSONL files is an independent JSON object: +- `schemaVersion`: 1 +- `id`: Unique identifier across all splits +- `groupID`: Base entity / sentence group (disjoint across splits) +- `split`: `"development"` or `"holdout"` +- `languages`: Array of relevant language codes (e.g. `["en", "uk"]`) +- `category`: Strata label (e.g. `single-word`, `capitalized`, `code-token`, `acronym`, `ambiguous-short`, etc.) +- `label`: Target behavior (`repair`, `preserve`, or `abstain`) +- `provenance`: Synthetic generation metadata with `"review": "pending-human"` +- `initial`: Starting editor state (sourceID, language, text, caretUTF16, preferredCyrillic, keyboardType) +- `events`: Array of physical key and boundary events (keyCode, modifiers, rendered) +- `expected`: Target outcome (text, language, sourceID, allowedAutomaticActions, checkpoints) + +## Deterministic Rendering + +Events are rendered using the frozen layout tables in `Benchmarks/layout-tables/v1/`. The `rendered` field in each event is verified against the table output for the given keyCode and modifiers. \ No newline at end of file diff --git a/Benchmarks/layout-corpus/v1/development.jsonl b/Benchmarks/layout-corpus/v1/development.jsonl new file mode 100644 index 0000000..5b8dfdd --- /dev/null +++ b/Benchmarks/layout-corpus/v1/development.jsonl @@ -0,0 +1,100 @@ +{"languages":["en","uk"],"label":"repair","expected":{"language":"uk","checkpoints":[{"afterEvent":7,"language":"uk","text":"привіт "}],"allowedAutomaticActions":["replaceSuffix","switchSource"],"sourceID":"com.apple.keylayout.Ukrainian","text":"привіт "},"id":"dev-repair-001","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"groupID":"dev-grp-001","events":[{"rendered":"g","type":"key","keyCode":5,"modifiers":[]},{"rendered":"h","type":"key","keyCode":4,"modifiers":[]},{"rendered":"s","type":"key","keyCode":1,"modifiers":[]},{"rendered":"d","type":"key","keyCode":2,"modifiers":[]},{"rendered":"b","type":"key","keyCode":11,"modifiers":[]},{"rendered":"n","type":"key","keyCode":45,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"category":"single-word","split":"development","schemaVersion":1} +{"schemaVersion":1,"id":"dev-repair-002","category":"single-word","events":[{"rendered":"h","type":"key","keyCode":4,"modifiers":[]},{"rendered":"j","type":"key","keyCode":38,"modifiers":[]},{"rendered":",","type":"key","keyCode":43,"modifiers":[]},{"rendered":"j","type":"key","keyCode":38,"modifiers":[]},{"rendered":"n","type":"key","keyCode":45,"modifiers":[]},{"rendered":"f","type":"key","keyCode":3,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"split":"development","languages":["en","uk"],"expected":{"language":"uk","checkpoints":[{"afterEvent":7,"text":"робота ","language":"uk"}],"allowedAutomaticActions":["replaceSuffix","switchSource"],"sourceID":"com.apple.keylayout.Ukrainian","text":"робота "},"groupID":"dev-grp-002","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"repair"} +{"schemaVersion":1,"id":"dev-repair-003","category":"single-word","events":[{"rendered":"k","type":"key","keyCode":40,"modifiers":[]},{"rendered":".","type":"key","keyCode":47,"modifiers":[]},{"rendered":"l","type":"key","keyCode":37,"modifiers":[]},{"rendered":"s","type":"key","keyCode":1,"modifiers":[]},{"rendered":"y","type":"key","keyCode":16,"modifiers":[]},{"rendered":"f","type":"key","keyCode":3,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"split":"development","languages":["en","uk"],"expected":{"language":"uk","checkpoints":[{"text":"людина ","afterEvent":7,"language":"uk"}],"allowedAutomaticActions":["replaceSuffix","switchSource"],"sourceID":"com.apple.keylayout.Ukrainian","text":"людина "},"groupID":"dev-grp-003","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"repair"} +{"schemaVersion":1,"id":"dev-repair-004","category":"single-word","events":[{"rendered":"l","type":"key","keyCode":37,"modifiers":[]},{"rendered":"z","type":"key","keyCode":6,"modifiers":[]},{"rendered":"r","type":"key","keyCode":15,"modifiers":[]},{"rendered":"e","type":"key","keyCode":14,"modifiers":[]},{"rendered":".","type":"key","keyCode":47,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"split":"development","languages":["en","uk"],"expected":{"language":"uk","checkpoints":[{"afterEvent":6,"text":"дякую ","language":"uk"}],"allowedAutomaticActions":["replaceSuffix","switchSource"],"sourceID":"com.apple.keylayout.Ukrainian","text":"дякую "},"groupID":"dev-grp-004","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"repair"} +{"schemaVersion":1,"id":"dev-repair-005","category":"capitalized","events":[{"rendered":"R","type":"key","keyCode":15,"modifiers":["shift"]},{"rendered":"s","type":"key","keyCode":1,"modifiers":[]},{"rendered":"]","type":"key","keyCode":30,"modifiers":[]},{"rendered":"d","type":"key","keyCode":2,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"split":"development","languages":["en","uk"],"expected":{"language":"uk","checkpoints":[{"text":"Київ ","afterEvent":5,"language":"uk"}],"allowedAutomaticActions":["replaceSuffix","switchSource"],"sourceID":"com.apple.keylayout.Ukrainian","text":"Київ "},"groupID":"dev-grp-005","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"repair"} +{"schemaVersion":1,"id":"dev-repair-006","category":"back-to-back","events":[{"rendered":"l","type":"key","keyCode":37,"modifiers":[]},{"rendered":"j","type":"key","keyCode":38,"modifiers":[]},{"rendered":",","type":"key","keyCode":43,"modifiers":[]},{"rendered":"h","type":"key","keyCode":4,"modifiers":[]},{"rendered":"s","type":"key","keyCode":1,"modifiers":[]},{"rendered":"q","type":"key","keyCode":12,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]},{"rendered":"l","type":"key","keyCode":37,"modifiers":[]},{"rendered":"t","type":"key","keyCode":17,"modifiers":[]},{"rendered":"y","type":"key","keyCode":16,"modifiers":[]},{"rendered":"m","type":"key","keyCode":46,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"split":"development","languages":["en","uk"],"expected":{"language":"uk","checkpoints":[{"language":"uk","afterEvent":12,"text":"добрий день "}],"allowedAutomaticActions":["replaceSuffix","switchSource"],"sourceID":"com.apple.keylayout.Ukrainian","text":"добрий день "},"groupID":"dev-grp-006","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"repair"} +{"schemaVersion":1,"id":"dev-repair-007","category":"single-word","events":[{"rendered":"l","type":"key","keyCode":37,"modifiers":[]},{"rendered":"s","type":"key","keyCode":1,"modifiers":[]},{"rendered":"d","type":"key","keyCode":2,"modifiers":[]},{"rendered":"s","type":"key","keyCode":1,"modifiers":[]},{"rendered":"v","type":"key","keyCode":9,"modifiers":[]},{"rendered":"j","type":"key","keyCode":38,"modifiers":[]},{"rendered":"c","type":"key","keyCode":8,"modifiers":[]},{"rendered":"m","type":"key","keyCode":46,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"split":"development","languages":["en","uk"],"expected":{"language":"uk","checkpoints":[{"afterEvent":9,"language":"uk","text":"дивимось "}],"allowedAutomaticActions":["replaceSuffix","switchSource"],"sourceID":"com.apple.keylayout.Ukrainian","text":"дивимось "},"groupID":"dev-grp-007","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"repair"} +{"schemaVersion":1,"id":"dev-repair-008","category":"single-word","events":[{"rendered":"x","type":"key","keyCode":7,"modifiers":[]},{"rendered":"e","type":"key","keyCode":14,"modifiers":[]},{"rendered":"l","type":"key","keyCode":37,"modifiers":[]},{"rendered":"j","type":"key","keyCode":38,"modifiers":[]},{"rendered":"d","type":"key","keyCode":2,"modifiers":[]},{"rendered":"j","type":"key","keyCode":38,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"split":"development","languages":["en","uk"],"expected":{"language":"uk","checkpoints":[{"text":"чудово ","afterEvent":7,"language":"uk"}],"allowedAutomaticActions":["replaceSuffix","switchSource"],"sourceID":"com.apple.keylayout.Ukrainian","text":"чудово "},"groupID":"dev-grp-008","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"repair"} +{"schemaVersion":1,"id":"dev-repair-009","category":"single-word","events":[{"rendered":"g","type":"key","keyCode":5,"modifiers":[]},{"rendered":"h","type":"key","keyCode":4,"modifiers":[]},{"rendered":"f","type":"key","keyCode":3,"modifiers":[]},{"rendered":"d","type":"key","keyCode":2,"modifiers":[]},{"rendered":"l","type":"key","keyCode":37,"modifiers":[]},{"rendered":"f","type":"key","keyCode":3,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"split":"development","languages":["en","uk"],"expected":{"language":"uk","checkpoints":[{"text":"правда ","afterEvent":7,"language":"uk"}],"allowedAutomaticActions":["replaceSuffix","switchSource"],"sourceID":"com.apple.keylayout.Ukrainian","text":"правда "},"groupID":"dev-grp-009","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"repair"} +{"schemaVersion":1,"id":"dev-repair-010","category":"question","events":[{"rendered":"z","keyCode":6,"type":"key","modifiers":[]},{"rendered":"r","keyCode":15,"type":"key","modifiers":[]},{"rendered":" ","keyCode":49,"type":"boundary","modifiers":[]},{"rendered":"c","keyCode":8,"type":"key","modifiers":[]},{"rendered":"g","keyCode":5,"type":"key","modifiers":[]},{"rendered":"h","keyCode":4,"type":"key","modifiers":[]},{"rendered":"f","keyCode":3,"type":"key","modifiers":[]},{"rendered":"d","keyCode":2,"type":"key","modifiers":[]},{"rendered":"s","keyCode":1,"type":"key","modifiers":[]},{"rendered":" ","keyCode":49,"type":"boundary","modifiers":[]}],"initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"split":"development","languages":["en","uk"],"expected":{"language":"uk","checkpoints":[{"text":"як справи ","language":"uk","afterEvent":10}],"allowedAutomaticActions":["replaceSuffix","switchSource"],"sourceID":"com.apple.keylayout.Ukrainian","text":"як справи "},"groupID":"dev-grp-010","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"repair"} +{"schemaVersion":1,"id":"dev-repair-011","category":"single-word","events":[{"type":"key","keyCode":4,"modifiers":[],"rendered":"р"},{"type":"key","keyCode":14,"modifiers":[],"rendered":"у"},{"type":"key","keyCode":37,"modifiers":[],"rendered":"д"},{"type":"key","keyCode":37,"modifiers":[],"rendered":"д"},{"type":"key","keyCode":31,"modifiers":[],"rendered":"щ"},{"type":"boundary","keyCode":49,"modifiers":[],"rendered":" "}],"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"split":"development","languages":["en","uk"],"expected":{"sourceID":"com.apple.keylayout.ABC","allowedAutomaticActions":["replaceSuffix","switchSource"],"checkpoints":[{"language":"en","afterEvent":6,"text":"hello "}],"text":"hello ","language":"en"},"groupID":"dev-grp-011","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"repair"} +{"schemaVersion":1,"id":"dev-repair-012","category":"single-word","events":[{"type":"key","keyCode":13,"modifiers":[],"rendered":"ц"},{"type":"key","keyCode":31,"modifiers":[],"rendered":"щ"},{"type":"key","keyCode":15,"modifiers":[],"rendered":"к"},{"type":"key","keyCode":37,"modifiers":[],"rendered":"д"},{"type":"key","keyCode":2,"modifiers":[],"rendered":"в"},{"type":"boundary","keyCode":49,"modifiers":[],"rendered":" "}],"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"split":"development","languages":["en","uk"],"expected":{"sourceID":"com.apple.keylayout.ABC","allowedAutomaticActions":["replaceSuffix","switchSource"],"checkpoints":[{"afterEvent":6,"text":"world ","language":"en"}],"text":"world ","language":"en"},"groupID":"dev-grp-012","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"repair"} +{"schemaVersion":1,"id":"dev-repair-013","category":"single-word","events":[{"type":"key","keyCode":1,"modifiers":[],"rendered":"и"},{"type":"key","keyCode":16,"modifiers":[],"rendered":"н"},{"type":"key","keyCode":1,"modifiers":[],"rendered":"и"},{"type":"key","keyCode":17,"modifiers":[],"rendered":"е"},{"type":"key","keyCode":14,"modifiers":[],"rendered":"у"},{"type":"key","keyCode":46,"modifiers":[],"rendered":"ь"},{"type":"boundary","keyCode":49,"modifiers":[],"rendered":" "}],"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"split":"development","languages":["en","uk"],"expected":{"sourceID":"com.apple.keylayout.ABC","allowedAutomaticActions":["replaceSuffix","switchSource"],"checkpoints":[{"language":"en","afterEvent":7,"text":"system "}],"text":"system ","language":"en"},"groupID":"dev-grp-013","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"repair"} +{"schemaVersion":1,"id":"dev-repair-014","category":"single-word","events":[{"rendered":"ц","type":"key","keyCode":13,"modifiers":[]},{"rendered":"ш","type":"key","keyCode":34,"modifiers":[]},{"rendered":"т","type":"key","keyCode":45,"modifiers":[]},{"rendered":"в","type":"key","keyCode":2,"modifiers":[]},{"rendered":"щ","type":"key","keyCode":31,"modifiers":[]},{"rendered":"ц","type":"key","keyCode":13,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"split":"development","languages":["en","uk"],"expected":{"text":"window ","language":"en","sourceID":"com.apple.keylayout.ABC","allowedAutomaticActions":["replaceSuffix","switchSource"],"checkpoints":[{"afterEvent":7,"text":"window ","language":"en"}]},"groupID":"dev-grp-014","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"repair"} +{"schemaVersion":1,"id":"dev-repair-015","category":"capitalized","events":[{"type":"key","keyCode":37,"modifiers":["shift"],"rendered":"Д"},{"type":"key","keyCode":31,"modifiers":[],"rendered":"щ"},{"type":"key","keyCode":45,"modifiers":[],"rendered":"т"},{"type":"key","keyCode":2,"modifiers":[],"rendered":"в"},{"type":"key","keyCode":31,"modifiers":[],"rendered":"щ"},{"type":"key","keyCode":45,"modifiers":[],"rendered":"т"},{"type":"boundary","keyCode":49,"modifiers":[],"rendered":" "}],"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"split":"development","languages":["en","uk"],"expected":{"sourceID":"com.apple.keylayout.ABC","allowedAutomaticActions":["replaceSuffix","switchSource"],"checkpoints":[{"language":"en","afterEvent":7,"text":"London "}],"text":"London ","language":"en"},"groupID":"dev-grp-015","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"repair"} +{"schemaVersion":1,"id":"dev-repair-016","category":"back-to-back","events":[{"rendered":"п","type":"key","keyCode":5,"modifiers":[]},{"rendered":"щ","type":"key","keyCode":31,"modifiers":[]},{"rendered":"щ","type":"key","keyCode":31,"modifiers":[]},{"rendered":"в","type":"key","keyCode":2,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]},{"rendered":"ь","type":"key","keyCode":46,"modifiers":[]},{"rendered":"щ","type":"key","keyCode":31,"modifiers":[]},{"rendered":"к","type":"key","keyCode":15,"modifiers":[]},{"rendered":"т","type":"key","keyCode":45,"modifiers":[]},{"rendered":"ш","type":"key","keyCode":34,"modifiers":[]},{"rendered":"т","type":"key","keyCode":45,"modifiers":[]},{"rendered":"п","type":"key","keyCode":5,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"split":"development","languages":["en","uk"],"expected":{"language":"en","allowedAutomaticActions":["replaceSuffix","switchSource"],"text":"good morning ","checkpoints":[{"language":"en","text":"good morning ","afterEvent":13}],"sourceID":"com.apple.keylayout.ABC"},"groupID":"dev-grp-016","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"repair"} +{"schemaVersion":1,"id":"dev-repair-017","category":"single-word","events":[{"rendered":"у","type":"key","keyCode":14,"modifiers":[]},{"rendered":"ч","type":"key","keyCode":7,"modifiers":[]},{"rendered":"з","type":"key","keyCode":35,"modifiers":[]},{"rendered":"у","type":"key","keyCode":14,"modifiers":[]},{"rendered":"с","type":"key","keyCode":8,"modifiers":[]},{"rendered":"е","type":"key","keyCode":17,"modifiers":[]},{"rendered":"у","type":"key","keyCode":14,"modifiers":[]},{"rendered":"в","type":"key","keyCode":2,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"split":"development","languages":["en","uk"],"expected":{"text":"expected ","language":"en","sourceID":"com.apple.keylayout.ABC","allowedAutomaticActions":["replaceSuffix","switchSource"],"checkpoints":[{"afterEvent":9,"language":"en","text":"expected "}]},"groupID":"dev-grp-017","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"repair"} +{"schemaVersion":1,"languages":["en","uk"],"label":"repair","events":[{"rendered":"е","type":"key","keyCode":17,"modifiers":[]},{"rendered":"р","type":"key","keyCode":4,"modifiers":[]},{"rendered":"ф","type":"key","keyCode":0,"modifiers":[]},{"rendered":"т","type":"key","keyCode":45,"modifiers":[]},{"rendered":"л","type":"key","keyCode":40,"modifiers":[]},{"rendered":"и","type":"key","keyCode":1,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"split":"development","id":"dev-repair-018","groupID":"dev-grp-018","initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"category":"single-word","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"expected":{"language":"en","checkpoints":[{"afterEvent":7,"language":"en","text":"thanks "}],"allowedAutomaticActions":["replaceSuffix","switchSource"],"sourceID":"com.apple.keylayout.ABC","text":"thanks "}} +{"schemaVersion":1,"languages":["en","uk"],"label":"repair","events":[{"rendered":"з","type":"key","keyCode":35,"modifiers":[]},{"rendered":"к","type":"key","keyCode":15,"modifiers":[]},{"rendered":"щ","type":"key","keyCode":31,"modifiers":[]},{"rendered":"о","type":"key","keyCode":38,"modifiers":[]},{"rendered":"у","type":"key","keyCode":14,"modifiers":[]},{"rendered":"с","type":"key","keyCode":8,"modifiers":[]},{"rendered":"е","type":"key","keyCode":17,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"split":"development","id":"dev-repair-019","groupID":"dev-grp-019","initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"category":"single-word","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"expected":{"language":"en","checkpoints":[{"afterEvent":8,"language":"en","text":"project "}],"allowedAutomaticActions":["replaceSuffix","switchSource"],"sourceID":"com.apple.keylayout.ABC","text":"project "}} +{"schemaVersion":1,"languages":["en","uk"],"label":"repair","events":[{"rendered":"р","type":"key","keyCode":4,"modifiers":[]},{"rendered":"щ","type":"key","keyCode":31,"modifiers":[]},{"rendered":"ц","type":"key","keyCode":13,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]},{"rendered":"ф","type":"key","keyCode":0,"modifiers":[]},{"rendered":"к","type":"key","keyCode":15,"modifiers":[]},{"rendered":"у","type":"key","keyCode":14,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]},{"rendered":"н","type":"key","keyCode":16,"modifiers":[]},{"rendered":"щ","type":"key","keyCode":31,"modifiers":[]},{"rendered":"г","type":"key","keyCode":32,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"split":"development","id":"dev-repair-020","groupID":"dev-grp-020","initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"category":"question","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"expected":{"language":"en","checkpoints":[{"language":"en","afterEvent":12,"text":"how are you "}],"allowedAutomaticActions":["replaceSuffix","switchSource"],"sourceID":"com.apple.keylayout.ABC","text":"how are you "}} +{"schemaVersion":1,"languages":["en","uk"],"label":"preserve","events":[{"rendered":"a","type":"key","keyCode":0,"modifiers":[]},{"rendered":"p","type":"key","keyCode":35,"modifiers":[]},{"rendered":"p","type":"key","keyCode":35,"modifiers":[]},{"rendered":"l","type":"key","keyCode":37,"modifiers":[]},{"rendered":"e","type":"key","keyCode":14,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"split":"development","id":"dev-preserve-021","groupID":"dev-grp-021","initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"category":"natural-word","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"expected":{"language":"en","checkpoints":[{"language":"en","afterEvent":6,"text":"apple "}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.ABC","text":"apple "}} +{"schemaVersion":1,"languages":["en","uk"],"label":"preserve","events":[{"rendered":"s","type":"key","keyCode":1,"modifiers":[]},{"rendered":"w","type":"key","keyCode":13,"modifiers":[]},{"rendered":"i","type":"key","keyCode":34,"modifiers":[]},{"rendered":"f","type":"key","keyCode":3,"modifiers":[]},{"rendered":"t","type":"key","keyCode":17,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"split":"development","id":"dev-preserve-022","groupID":"dev-grp-022","initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"category":"natural-word","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"expected":{"language":"en","checkpoints":[{"afterEvent":6,"language":"en","text":"swift "}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.ABC","text":"swift "}} +{"schemaVersion":1,"languages":["en","uk"],"label":"preserve","events":[{"rendered":"d","type":"key","keyCode":2,"modifiers":[]},{"rendered":"e","type":"key","keyCode":14,"modifiers":[]},{"rendered":"v","type":"key","keyCode":9,"modifiers":[]},{"rendered":"e","type":"key","keyCode":14,"modifiers":[]},{"rendered":"l","type":"key","keyCode":37,"modifiers":[]},{"rendered":"o","type":"key","keyCode":31,"modifiers":[]},{"rendered":"p","type":"key","keyCode":35,"modifiers":[]},{"rendered":"e","type":"key","keyCode":14,"modifiers":[]},{"rendered":"r","type":"key","keyCode":15,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"split":"development","id":"dev-preserve-023","groupID":"dev-grp-023","initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"category":"natural-word","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"expected":{"language":"en","checkpoints":[{"language":"en","afterEvent":10,"text":"developer "}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.ABC","text":"developer "}} +{"schemaVersion":1,"languages":["en","uk"],"label":"preserve","events":[{"rendered":"t","type":"key","keyCode":17,"modifiers":[]},{"rendered":"e","type":"key","keyCode":14,"modifiers":[]},{"rendered":"r","type":"key","keyCode":15,"modifiers":[]},{"rendered":"m","type":"key","keyCode":46,"modifiers":[]},{"rendered":"i","type":"key","keyCode":34,"modifiers":[]},{"rendered":"n","type":"key","keyCode":45,"modifiers":[]},{"rendered":"a","type":"key","keyCode":0,"modifiers":[]},{"rendered":"l","type":"key","keyCode":37,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"split":"development","id":"dev-preserve-024","groupID":"dev-grp-024","initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"category":"natural-word","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"expected":{"language":"en","checkpoints":[{"text":"terminal ","language":"en","afterEvent":9}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.ABC","text":"terminal "}} +{"schemaVersion":1,"languages":["en","uk"],"label":"preserve","events":[{"rendered":"r","type":"key","keyCode":15,"modifiers":[]},{"rendered":"e","type":"key","keyCode":14,"modifiers":[]},{"rendered":"l","type":"key","keyCode":37,"modifiers":[]},{"rendered":"e","type":"key","keyCode":14,"modifiers":[]},{"rendered":"a","type":"key","keyCode":0,"modifiers":[]},{"rendered":"s","type":"key","keyCode":1,"modifiers":[]},{"rendered":"e","type":"key","keyCode":14,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"split":"development","id":"dev-preserve-025","groupID":"dev-grp-025","initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"category":"natural-word","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"expected":{"language":"en","checkpoints":[{"language":"en","text":"release ","afterEvent":8}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.ABC","text":"release "}} +{"schemaVersion":1,"languages":["en","uk"],"label":"preserve","events":[{"rendered":"k","type":"key","keyCode":40,"modifiers":[]},{"rendered":"e","type":"key","keyCode":14,"modifiers":[]},{"rendered":"y","type":"key","keyCode":16,"modifiers":[]},{"rendered":"b","type":"key","keyCode":11,"modifiers":[]},{"rendered":"o","type":"key","keyCode":31,"modifiers":[]},{"rendered":"a","type":"key","keyCode":0,"modifiers":[]},{"rendered":"r","type":"key","keyCode":15,"modifiers":[]},{"rendered":"d","type":"key","keyCode":2,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"split":"development","id":"dev-preserve-026","groupID":"dev-grp-026","initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"category":"natural-word","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"expected":{"language":"en","checkpoints":[{"afterEvent":9,"language":"en","text":"keyboard "}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.ABC","text":"keyboard "}} +{"schemaVersion":1,"languages":["en","uk"],"label":"preserve","events":[{"rendered":"t","type":"key","keyCode":17,"modifiers":[]},{"rendered":"h","type":"key","keyCode":4,"modifiers":[]},{"rendered":"e","type":"key","keyCode":14,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"split":"development","id":"dev-preserve-027","groupID":"dev-grp-027","initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"category":"short-word","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"expected":{"language":"en","checkpoints":[{"text":"the ","language":"en","afterEvent":4}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.ABC","text":"the "}} +{"schemaVersion":1,"languages":["en","uk"],"label":"preserve","events":[{"rendered":"a","type":"key","keyCode":0,"modifiers":[]},{"rendered":"n","type":"key","keyCode":45,"modifiers":[]},{"rendered":"d","type":"key","keyCode":2,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"split":"development","id":"dev-preserve-028","groupID":"dev-grp-028","initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"category":"short-word","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"expected":{"language":"en","checkpoints":[{"language":"en","afterEvent":4,"text":"and "}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.ABC","text":"and "}} +{"schemaVersion":1,"languages":["en","uk"],"label":"preserve","events":[{"rendered":"f","type":"key","keyCode":3,"modifiers":[]},{"rendered":"o","type":"key","keyCode":31,"modifiers":[]},{"rendered":"r","type":"key","keyCode":15,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"split":"development","id":"dev-preserve-029","groupID":"dev-grp-029","initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"category":"short-word","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"expected":{"language":"en","checkpoints":[{"afterEvent":4,"text":"for ","language":"en"}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.ABC","text":"for "}} +{"schemaVersion":1,"languages":["en","uk"],"label":"preserve","events":[{"rendered":"n","type":"key","keyCode":45,"modifiers":[]},{"rendered":"o","type":"key","keyCode":31,"modifiers":[]},{"rendered":"t","type":"key","keyCode":17,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"split":"development","id":"dev-preserve-030","groupID":"dev-grp-030","initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"category":"short-word","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"expected":{"language":"en","checkpoints":[{"text":"not ","language":"en","afterEvent":4}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.ABC","text":"not "}} +{"schemaVersion":1,"languages":["en","uk"],"label":"preserve","events":[{"rendered":"b","type":"key","keyCode":11,"modifiers":[]},{"rendered":"u","type":"key","keyCode":32,"modifiers":[]},{"rendered":"t","type":"key","keyCode":17,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"split":"development","id":"dev-preserve-031","groupID":"dev-grp-031","initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"category":"short-word","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"expected":{"language":"en","checkpoints":[{"text":"but ","language":"en","afterEvent":4}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.ABC","text":"but "}} +{"schemaVersion":1,"languages":["en","uk"],"label":"preserve","events":[{"rendered":"y","type":"key","keyCode":16,"modifiers":[]},{"rendered":"e","type":"key","keyCode":14,"modifiers":[]},{"rendered":"s","type":"key","keyCode":1,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"split":"development","id":"dev-preserve-032","groupID":"dev-grp-032","initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"category":"short-word","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"expected":{"language":"en","checkpoints":[{"language":"en","text":"yes ","afterEvent":4}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.ABC","text":"yes "}} +{"schemaVersion":1,"languages":["en","uk"],"label":"preserve","events":[{"rendered":"G","type":"key","keyCode":5,"modifiers":["shift"]},{"rendered":"i","type":"key","keyCode":34,"modifiers":[]},{"rendered":"t","type":"key","keyCode":17,"modifiers":[]},{"rendered":"H","type":"key","keyCode":4,"modifiers":["shift"]},{"rendered":"u","type":"key","keyCode":32,"modifiers":[]},{"rendered":"b","type":"key","keyCode":11,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"split":"development","id":"dev-preserve-033","groupID":"dev-grp-033","initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"category":"proper-noun","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"expected":{"language":"en","checkpoints":[{"language":"en","afterEvent":7,"text":"GitHub "}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.ABC","text":"GitHub "}} +{"schemaVersion":1,"id":"dev-preserve-034","category":"proper-noun","events":[{"rendered":"U","type":"key","keyCode":32,"modifiers":["shift"]},{"rendered":"n","type":"key","keyCode":45,"modifiers":[]},{"rendered":"i","type":"key","keyCode":34,"modifiers":[]},{"rendered":"x","type":"key","keyCode":7,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"split":"development","languages":["en","uk"],"expected":{"language":"en","checkpoints":[{"text":"Unix ","language":"en","afterEvent":5}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.ABC","text":"Unix "},"groupID":"dev-grp-034","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"dev-preserve-035","category":"proper-noun","events":[{"rendered":"X","type":"key","keyCode":7,"modifiers":["shift"]},{"rendered":"c","type":"key","keyCode":8,"modifiers":[]},{"rendered":"o","type":"key","keyCode":31,"modifiers":[]},{"rendered":"d","type":"key","keyCode":2,"modifiers":[]},{"rendered":"e","type":"key","keyCode":14,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"split":"development","languages":["en","uk"],"expected":{"language":"en","checkpoints":[{"afterEvent":6,"text":"Xcode ","language":"en"}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.ABC","text":"Xcode "},"groupID":"dev-grp-035","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"dev-preserve-036","category":"proper-noun","events":[{"rendered":"m","type":"key","keyCode":46,"modifiers":[]},{"rendered":"a","type":"key","keyCode":0,"modifiers":[]},{"rendered":"c","type":"key","keyCode":8,"modifiers":[]},{"rendered":"O","type":"key","keyCode":31,"modifiers":["shift"]},{"rendered":"S","type":"key","keyCode":1,"modifiers":["shift"]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"split":"development","languages":["en","uk"],"expected":{"language":"en","checkpoints":[{"text":"macOS ","language":"en","afterEvent":6}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.ABC","text":"macOS "},"groupID":"dev-grp-036","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"dev-preserve-037","category":"code-token","events":[{"rendered":"c","type":"key","keyCode":8,"modifiers":[]},{"rendered":"a","type":"key","keyCode":0,"modifiers":[]},{"rendered":"m","type":"key","keyCode":46,"modifiers":[]},{"rendered":"e","type":"key","keyCode":14,"modifiers":[]},{"rendered":"l","type":"key","keyCode":37,"modifiers":[]},{"rendered":"C","type":"key","keyCode":8,"modifiers":["shift"]},{"rendered":"a","type":"key","keyCode":0,"modifiers":[]},{"rendered":"s","type":"key","keyCode":1,"modifiers":[]},{"rendered":"e","type":"key","keyCode":14,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"split":"development","languages":["en","uk"],"expected":{"sourceID":"com.apple.keylayout.ABC","allowedAutomaticActions":[],"text":"camelCase ","checkpoints":[{"afterEvent":10,"text":"camelCase ","language":"en"}],"language":"en"},"groupID":"dev-grp-037","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"dev-preserve-038","category":"code-token","events":[{"rendered":"s","type":"key","keyCode":1,"modifiers":[]},{"rendered":"n","type":"key","keyCode":45,"modifiers":[]},{"rendered":"a","type":"key","keyCode":0,"modifiers":[]},{"rendered":"k","type":"key","keyCode":40,"modifiers":[]},{"rendered":"e","type":"key","keyCode":14,"modifiers":[]},{"rendered":"_","type":"key","keyCode":27,"modifiers":["shift"]},{"rendered":"c","type":"key","keyCode":8,"modifiers":[]},{"rendered":"a","type":"key","keyCode":0,"modifiers":[]},{"rendered":"s","type":"key","keyCode":1,"modifiers":[]},{"rendered":"e","type":"key","keyCode":14,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"split":"development","languages":["en","uk"],"expected":{"language":"en","checkpoints":[{"afterEvent":11,"text":"snake_case ","language":"en"}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.ABC","text":"snake_case "},"groupID":"dev-grp-038","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"dev-preserve-039","category":"code-token","events":[{"rendered":"k","type":"key","keyCode":40,"modifiers":[]},{"rendered":"U","type":"key","keyCode":32,"modifiers":["shift"]},{"rendered":"C","type":"key","keyCode":8,"modifiers":["shift"]},{"rendered":"K","type":"key","keyCode":40,"modifiers":["shift"]},{"rendered":"e","type":"key","keyCode":14,"modifiers":[]},{"rendered":"y","type":"key","keyCode":16,"modifiers":[]},{"rendered":"A","type":"key","keyCode":0,"modifiers":["shift"]},{"rendered":"c","type":"key","keyCode":8,"modifiers":[]},{"rendered":"t","type":"key","keyCode":17,"modifiers":[]},{"rendered":"i","type":"key","keyCode":34,"modifiers":[]},{"rendered":"o","type":"key","keyCode":31,"modifiers":[]},{"rendered":"n","type":"key","keyCode":45,"modifiers":[]},{"rendered":"D","type":"key","keyCode":2,"modifiers":["shift"]},{"rendered":"o","type":"key","keyCode":31,"modifiers":[]},{"rendered":"w","type":"key","keyCode":13,"modifiers":[]},{"rendered":"n","type":"key","keyCode":45,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"split":"development","languages":["en","uk"],"expected":{"language":"en","checkpoints":[{"text":"kUCKeyActionDown ","language":"en","afterEvent":17}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.ABC","text":"kUCKeyActionDown "},"groupID":"dev-grp-039","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"dev-preserve-040","category":"code-token","events":[{"rendered":"i","type":"key","keyCode":34,"modifiers":[]},{"rendered":"n","type":"key","keyCode":45,"modifiers":[]},{"rendered":"i","type":"key","keyCode":34,"modifiers":[]},{"rendered":"t","type":"key","keyCode":17,"modifiers":[]},{"rendered":"(","type":"key","keyCode":25,"modifiers":["shift"]},{"rendered":")","type":"key","keyCode":29,"modifiers":["shift"]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"split":"development","languages":["en","uk"],"expected":{"language":"en","checkpoints":[{"afterEvent":7,"language":"en","text":"init() "}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.ABC","text":"init() "},"groupID":"dev-grp-040","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"dev-preserve-041","category":"path","events":[{"rendered":"\/","type":"key","keyCode":44,"modifiers":[]},{"rendered":"u","type":"key","keyCode":32,"modifiers":[]},{"rendered":"s","type":"key","keyCode":1,"modifiers":[]},{"rendered":"r","type":"key","keyCode":15,"modifiers":[]},{"rendered":"\/","type":"key","keyCode":44,"modifiers":[]},{"rendered":"l","type":"key","keyCode":37,"modifiers":[]},{"rendered":"o","type":"key","keyCode":31,"modifiers":[]},{"rendered":"c","type":"key","keyCode":8,"modifiers":[]},{"rendered":"a","type":"key","keyCode":0,"modifiers":[]},{"rendered":"l","type":"key","keyCode":37,"modifiers":[]},{"rendered":"\/","type":"key","keyCode":44,"modifiers":[]},{"rendered":"b","type":"key","keyCode":11,"modifiers":[]},{"rendered":"i","type":"key","keyCode":34,"modifiers":[]},{"rendered":"n","type":"key","keyCode":45,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"split":"development","languages":["en","uk"],"expected":{"text":"\/usr\/local\/bin ","language":"en","allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.ABC","checkpoints":[{"text":"\/usr\/local\/bin ","language":"en","afterEvent":15}]},"groupID":"dev-grp-041","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"dev-preserve-042","category":"filename","events":[{"rendered":"P","type":"key","keyCode":35,"modifiers":["shift"]},{"rendered":"a","type":"key","keyCode":0,"modifiers":[]},{"rendered":"c","type":"key","keyCode":8,"modifiers":[]},{"rendered":"k","type":"key","keyCode":40,"modifiers":[]},{"rendered":"a","type":"key","keyCode":0,"modifiers":[]},{"rendered":"g","type":"key","keyCode":5,"modifiers":[]},{"rendered":"e","type":"key","keyCode":14,"modifiers":[]},{"rendered":".","type":"key","keyCode":47,"modifiers":[]},{"rendered":"s","type":"key","keyCode":1,"modifiers":[]},{"rendered":"w","type":"key","keyCode":13,"modifiers":[]},{"rendered":"i","type":"key","keyCode":34,"modifiers":[]},{"rendered":"f","type":"key","keyCode":3,"modifiers":[]},{"rendered":"t","type":"key","keyCode":17,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"split":"development","languages":["en","uk"],"expected":{"language":"en","checkpoints":[{"language":"en","afterEvent":14,"text":"Package.swift "}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.ABC","text":"Package.swift "},"groupID":"dev-grp-042","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"dev-preserve-043","category":"url","events":[{"rendered":"h","type":"key","keyCode":4,"modifiers":[]},{"rendered":"t","type":"key","keyCode":17,"modifiers":[]},{"rendered":"t","type":"key","keyCode":17,"modifiers":[]},{"rendered":"p","type":"key","keyCode":35,"modifiers":[]},{"rendered":"s","type":"key","keyCode":1,"modifiers":[]},{"rendered":":","type":"key","keyCode":41,"modifiers":["shift"]},{"rendered":"\/","type":"key","keyCode":44,"modifiers":[]},{"rendered":"\/","type":"key","keyCode":44,"modifiers":[]},{"rendered":"a","type":"key","keyCode":0,"modifiers":[]},{"rendered":"p","type":"key","keyCode":35,"modifiers":[]},{"rendered":"p","type":"key","keyCode":35,"modifiers":[]},{"rendered":"l","type":"key","keyCode":37,"modifiers":[]},{"rendered":"e","type":"key","keyCode":14,"modifiers":[]},{"rendered":".","type":"key","keyCode":47,"modifiers":[]},{"rendered":"c","type":"key","keyCode":8,"modifiers":[]},{"rendered":"o","type":"key","keyCode":31,"modifiers":[]},{"rendered":"m","type":"key","keyCode":46,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"split":"development","languages":["en","uk"],"expected":{"language":"en","checkpoints":[{"text":"https:\/\/apple.com ","language":"en","afterEvent":18}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.ABC","text":"https:\/\/apple.com "},"groupID":"dev-grp-043","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"dev-preserve-044","category":"email","events":[{"rendered":"d","type":"key","keyCode":2,"modifiers":[]},{"rendered":"e","type":"key","keyCode":14,"modifiers":[]},{"rendered":"v","type":"key","keyCode":9,"modifiers":[]},{"rendered":"@","type":"key","keyCode":19,"modifiers":["shift"]},{"rendered":"e","type":"key","keyCode":14,"modifiers":[]},{"rendered":"x","type":"key","keyCode":7,"modifiers":[]},{"rendered":"a","type":"key","keyCode":0,"modifiers":[]},{"rendered":"m","type":"key","keyCode":46,"modifiers":[]},{"rendered":"p","type":"key","keyCode":35,"modifiers":[]},{"rendered":"l","type":"key","keyCode":37,"modifiers":[]},{"rendered":"e","type":"key","keyCode":14,"modifiers":[]},{"rendered":".","type":"key","keyCode":47,"modifiers":[]},{"rendered":"o","type":"key","keyCode":31,"modifiers":[]},{"rendered":"r","type":"key","keyCode":15,"modifiers":[]},{"rendered":"g","type":"key","keyCode":5,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"split":"development","languages":["en","uk"],"expected":{"text":"dev@example.org ","language":"en","allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.ABC","checkpoints":[{"language":"en","afterEvent":16,"text":"dev@example.org "}]},"groupID":"dev-grp-044","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"dev-preserve-045","category":"acronym","events":[{"rendered":"H","type":"key","keyCode":4,"modifiers":["shift"]},{"rendered":"T","type":"key","keyCode":17,"modifiers":["shift"]},{"rendered":"T","type":"key","keyCode":17,"modifiers":["shift"]},{"rendered":"P","type":"key","keyCode":35,"modifiers":["shift"]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"split":"development","languages":["en","uk"],"expected":{"language":"en","checkpoints":[{"text":"HTTP ","language":"en","afterEvent":5}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.ABC","text":"HTTP "},"groupID":"dev-grp-045","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"dev-preserve-046","category":"acronym","events":[{"rendered":"J","type":"key","keyCode":38,"modifiers":["shift"]},{"rendered":"S","type":"key","keyCode":1,"modifiers":["shift"]},{"rendered":"O","type":"key","keyCode":31,"modifiers":["shift"]},{"rendered":"N","type":"key","keyCode":45,"modifiers":["shift"]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"split":"development","languages":["en","uk"],"expected":{"language":"en","checkpoints":[{"afterEvent":5,"language":"en","text":"JSON "}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.ABC","text":"JSON "},"groupID":"dev-grp-046","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"dev-preserve-047","category":"acronym","events":[{"rendered":"U","type":"key","keyCode":32,"modifiers":["shift"]},{"rendered":"U","type":"key","keyCode":32,"modifiers":["shift"]},{"rendered":"I","type":"key","keyCode":34,"modifiers":["shift"]},{"rendered":"D","type":"key","keyCode":2,"modifiers":["shift"]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"split":"development","languages":["en","uk"],"expected":{"language":"en","checkpoints":[{"text":"UUID ","afterEvent":5,"language":"en"}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.ABC","text":"UUID "},"groupID":"dev-grp-047","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"dev-preserve-048","category":"contraction","events":[{"rendered":"d","type":"key","keyCode":2,"modifiers":[]},{"rendered":"o","type":"key","keyCode":31,"modifiers":[]},{"rendered":"n","type":"key","keyCode":45,"modifiers":[]},{"rendered":"'","type":"key","keyCode":39,"modifiers":[]},{"rendered":"t","type":"key","keyCode":17,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"split":"development","languages":["en","uk"],"expected":{"language":"en","checkpoints":[{"text":"don't ","afterEvent":6,"language":"en"}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.ABC","text":"don't "},"groupID":"dev-grp-048","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"dev-preserve-049","category":"contraction","events":[{"rendered":"i","type":"key","keyCode":34,"modifiers":[]},{"rendered":"t","type":"key","keyCode":17,"modifiers":[]},{"rendered":"'","type":"key","keyCode":39,"modifiers":[]},{"rendered":"s","type":"key","keyCode":1,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"split":"development","languages":["en","uk"],"expected":{"language":"en","checkpoints":[{"text":"it's ","afterEvent":5,"language":"en"}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.ABC","text":"it's "},"groupID":"dev-grp-049","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"dev-preserve-050","category":"punctuation","events":[{"rendered":"r","type":"key","keyCode":15,"modifiers":[]},{"rendered":"e","type":"key","keyCode":14,"modifiers":[]},{"rendered":"s","type":"key","keyCode":1,"modifiers":[]},{"rendered":"u","type":"key","keyCode":32,"modifiers":[]},{"rendered":"l","type":"key","keyCode":37,"modifiers":[]},{"rendered":"t","type":"key","keyCode":17,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]},{"rendered":"=","type":"key","keyCode":24,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]},{"rendered":"t","type":"key","keyCode":17,"modifiers":[]},{"rendered":"r","type":"key","keyCode":15,"modifiers":[]},{"rendered":"u","type":"key","keyCode":32,"modifiers":[]},{"rendered":"e","type":"key","keyCode":14,"modifiers":[]},{"rendered":";","type":"key","keyCode":41,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"split":"development","languages":["en","uk"],"expected":{"text":"result = true; ","checkpoints":[{"text":"result = true; ","afterEvent":15,"language":"en"}],"sourceID":"com.apple.keylayout.ABC","language":"en","allowedAutomaticActions":[]},"groupID":"dev-grp-050","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"dev-preserve-051","category":"natural-word","events":[{"rendered":"я","type":"key","keyCode":6,"modifiers":[]},{"rendered":"б","type":"key","keyCode":43,"modifiers":[]},{"rendered":"л","type":"key","keyCode":40,"modifiers":[]},{"rendered":"у","type":"key","keyCode":14,"modifiers":[]},{"rendered":"к","type":"key","keyCode":15,"modifiers":[]},{"rendered":"о","type":"key","keyCode":38,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"split":"development","languages":["en","uk"],"expected":{"language":"uk","checkpoints":[{"afterEvent":7,"language":"uk","text":"яблуко "}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.Ukrainian","text":"яблуко "},"groupID":"dev-grp-051","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"dev-preserve-052","category":"natural-word","events":[{"rendered":"р","type":"key","keyCode":4,"modifiers":[]},{"rendered":"и","type":"key","keyCode":1,"modifiers":[]},{"rendered":"н","type":"key","keyCode":16,"modifiers":[]},{"rendered":"о","type":"key","keyCode":38,"modifiers":[]},{"rendered":"к","type":"key","keyCode":15,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"split":"development","languages":["en","uk"],"expected":{"language":"uk","checkpoints":[{"afterEvent":6,"text":"ринок ","language":"uk"}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.Ukrainian","text":"ринок "},"groupID":"dev-grp-052","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"dev-preserve-053","category":"natural-word","events":[{"rendered":"р","type":"key","keyCode":4,"modifiers":[]},{"rendered":"о","type":"key","keyCode":38,"modifiers":[]},{"rendered":"з","type":"key","keyCode":35,"modifiers":[]},{"rendered":"р","type":"key","keyCode":4,"modifiers":[]},{"rendered":"о","type":"key","keyCode":38,"modifiers":[]},{"rendered":"б","type":"key","keyCode":43,"modifiers":[]},{"rendered":"н","type":"key","keyCode":16,"modifiers":[]},{"rendered":"и","type":"key","keyCode":1,"modifiers":[]},{"rendered":"к","type":"key","keyCode":15,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"split":"development","languages":["en","uk"],"expected":{"language":"uk","allowedAutomaticActions":[],"text":"розробник ","sourceID":"com.apple.keylayout.Ukrainian","checkpoints":[{"text":"розробник ","language":"uk","afterEvent":10}]},"groupID":"dev-grp-053","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"dev-preserve-054","category":"natural-word","events":[{"rendered":"т","type":"key","keyCode":45,"modifiers":[]},{"rendered":"е","type":"key","keyCode":17,"modifiers":[]},{"rendered":"р","type":"key","keyCode":4,"modifiers":[]},{"rendered":"м","type":"key","keyCode":9,"modifiers":[]},{"rendered":"і","type":"key","keyCode":11,"modifiers":[]},{"rendered":"н","type":"key","keyCode":16,"modifiers":[]},{"rendered":"а","type":"key","keyCode":3,"modifiers":[]},{"rendered":"л","type":"key","keyCode":40,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"split":"development","languages":["en","uk"],"expected":{"language":"uk","checkpoints":[{"afterEvent":9,"language":"uk","text":"термінал "}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.Ukrainian","text":"термінал "},"groupID":"dev-grp-054","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"dev-preserve-055","category":"natural-word","events":[{"rendered":"р","type":"key","keyCode":4,"modifiers":[]},{"rendered":"е","type":"key","keyCode":17,"modifiers":[]},{"rendered":"л","type":"key","keyCode":40,"modifiers":[]},{"rendered":"і","type":"key","keyCode":11,"modifiers":[]},{"rendered":"з","type":"key","keyCode":35,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"split":"development","languages":["en","uk"],"expected":{"language":"uk","checkpoints":[{"text":"реліз ","afterEvent":6,"language":"uk"}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.Ukrainian","text":"реліз "},"groupID":"dev-grp-055","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"dev-preserve-056","category":"natural-word","events":[{"rendered":"с","type":"key","keyCode":8,"modifiers":[]},{"rendered":"и","type":"key","keyCode":1,"modifiers":[]},{"rendered":"с","type":"key","keyCode":8,"modifiers":[]},{"rendered":"т","type":"key","keyCode":45,"modifiers":[]},{"rendered":"е","type":"key","keyCode":17,"modifiers":[]},{"rendered":"м","type":"key","keyCode":9,"modifiers":[]},{"rendered":"а","type":"key","keyCode":3,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"split":"development","languages":["en","uk"],"expected":{"language":"uk","checkpoints":[{"text":"система ","afterEvent":8,"language":"uk"}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.Ukrainian","text":"система "},"groupID":"dev-grp-056","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"dev-preserve-057","category":"short-word","events":[{"rendered":"ц","type":"key","keyCode":13,"modifiers":[]},{"rendered":"е","type":"key","keyCode":17,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"split":"development","languages":["en","uk"],"expected":{"language":"uk","checkpoints":[{"afterEvent":3,"text":"це ","language":"uk"}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.Ukrainian","text":"це "},"groupID":"dev-grp-057","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"dev-preserve-058","category":"short-word","events":[{"rendered":"і","type":"key","keyCode":11,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"split":"development","languages":["en","uk"],"expected":{"language":"uk","checkpoints":[{"text":"і ","afterEvent":2,"language":"uk"}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.Ukrainian","text":"і "},"groupID":"dev-grp-058","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"dev-preserve-059","category":"short-word","events":[{"rendered":"н","type":"key","keyCode":16,"modifiers":[]},{"rendered":"а","type":"key","keyCode":3,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"split":"development","languages":["en","uk"],"expected":{"language":"uk","checkpoints":[{"afterEvent":3,"text":"на ","language":"uk"}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.Ukrainian","text":"на "},"groupID":"dev-grp-059","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"dev-preserve-060","category":"short-word","events":[{"rendered":"д","type":"key","keyCode":37,"modifiers":[]},{"rendered":"о","type":"key","keyCode":38,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"split":"development","languages":["en","uk"],"expected":{"language":"uk","checkpoints":[{"language":"uk","afterEvent":3,"text":"до "}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.Ukrainian","text":"до "},"groupID":"dev-grp-060","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"dev-preserve-061","category":"short-word","events":[{"rendered":"т","type":"key","keyCode":45,"modifiers":[]},{"rendered":"а","type":"key","keyCode":3,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"split":"development","languages":["en","uk"],"expected":{"language":"uk","checkpoints":[{"text":"та ","afterEvent":3,"language":"uk"}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.Ukrainian","text":"та "},"groupID":"dev-grp-061","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"dev-preserve-062","category":"short-word","events":[{"rendered":"м","type":"key","keyCode":9,"modifiers":[]},{"rendered":"и","type":"key","keyCode":1,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"split":"development","languages":["en","uk"],"expected":{"language":"uk","checkpoints":[{"text":"ми ","language":"uk","afterEvent":3}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.Ukrainian","text":"ми "},"groupID":"dev-grp-062","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"dev-preserve-063","category":"proper-noun","events":[{"rendered":"Л","type":"key","keyCode":40,"modifiers":["shift"]},{"rendered":"ь","type":"key","keyCode":46,"modifiers":[]},{"rendered":"в","type":"key","keyCode":2,"modifiers":[]},{"rendered":"і","type":"key","keyCode":11,"modifiers":[]},{"rendered":"в","type":"key","keyCode":2,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"split":"development","languages":["en","uk"],"expected":{"language":"uk","checkpoints":[{"language":"uk","afterEvent":6,"text":"Львів "}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.Ukrainian","text":"Львів "},"groupID":"dev-grp-063","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"dev-preserve-064","category":"proper-noun","events":[{"rendered":"Т","type":"key","keyCode":45,"modifiers":["shift"]},{"rendered":"а","type":"key","keyCode":3,"modifiers":[]},{"rendered":"р","type":"key","keyCode":4,"modifiers":[]},{"rendered":"а","type":"key","keyCode":3,"modifiers":[]},{"rendered":"с","type":"key","keyCode":8,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"split":"development","languages":["en","uk"],"expected":{"language":"uk","checkpoints":[{"language":"uk","afterEvent":6,"text":"Тарас "}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.Ukrainian","text":"Тарас "},"groupID":"dev-grp-064","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"dev-preserve-065","category":"proper-noun","events":[{"rendered":"Ш","type":"key","keyCode":34,"modifiers":["shift"]},{"rendered":"е","type":"key","keyCode":17,"modifiers":[]},{"rendered":"в","type":"key","keyCode":2,"modifiers":[]},{"rendered":"ч","type":"key","keyCode":7,"modifiers":[]},{"rendered":"е","type":"key","keyCode":17,"modifiers":[]},{"rendered":"н","type":"key","keyCode":16,"modifiers":[]},{"rendered":"к","type":"key","keyCode":15,"modifiers":[]},{"rendered":"о","type":"key","keyCode":38,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"split":"development","languages":["en","uk"],"expected":{"language":"uk","checkpoints":[{"afterEvent":9,"language":"uk","text":"Шевченко "}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.Ukrainian","text":"Шевченко "},"groupID":"dev-grp-065","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"dev-preserve-066","category":"proper-noun","events":[{"rendered":"Д","type":"key","keyCode":37,"modifiers":["shift"]},{"rendered":"н","type":"key","keyCode":16,"modifiers":[]},{"rendered":"і","type":"key","keyCode":11,"modifiers":[]},{"rendered":"п","type":"key","keyCode":5,"modifiers":[]},{"rendered":"р","type":"key","keyCode":4,"modifiers":[]},{"rendered":"о","type":"key","keyCode":38,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"split":"development","languages":["en","uk"],"expected":{"language":"uk","checkpoints":[{"text":"Дніпро ","language":"uk","afterEvent":7}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.Ukrainian","text":"Дніпро "},"groupID":"dev-grp-066","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"dev-preserve-067","category":"apostrophe","events":[{"rendered":"с","type":"key","keyCode":8,"modifiers":[]},{"rendered":"і","type":"key","keyCode":11,"modifiers":[]},{"rendered":"м","type":"key","keyCode":9,"modifiers":[]},{"rendered":"'","type":"key","keyCode":50,"modifiers":[]},{"rendered":"я","type":"key","keyCode":6,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"split":"development","languages":["en","uk"],"expected":{"language":"uk","checkpoints":[{"language":"uk","afterEvent":6,"text":"сім'я "}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.Ukrainian","text":"сім'я "},"groupID":"dev-grp-067","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"dev-preserve-068","category":"apostrophe","events":[{"rendered":"м","type":"key","keyCode":9,"modifiers":[]},{"rendered":"'","type":"key","keyCode":50,"modifiers":[]},{"rendered":"я","type":"key","keyCode":6,"modifiers":[]},{"rendered":"с","type":"key","keyCode":8,"modifiers":[]},{"rendered":"о","type":"key","keyCode":38,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"split":"development","languages":["en","uk"],"expected":{"language":"uk","checkpoints":[{"afterEvent":6,"text":"м'ясо ","language":"uk"}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.Ukrainian","text":"м'ясо "},"groupID":"dev-grp-068","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"dev-preserve-069","category":"apostrophe","events":[{"rendered":"в","type":"key","keyCode":2,"modifiers":[]},{"rendered":"'","type":"key","keyCode":50,"modifiers":[]},{"rendered":"я","type":"key","keyCode":6,"modifiers":[]},{"rendered":"з","type":"key","keyCode":35,"modifiers":[]},{"rendered":"а","type":"key","keyCode":3,"modifiers":[]},{"rendered":"т","type":"key","keyCode":45,"modifiers":[]},{"rendered":"и","type":"key","keyCode":1,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"split":"development","languages":["en","uk"],"expected":{"language":"uk","checkpoints":[{"language":"uk","afterEvent":8,"text":"в'язати "}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.Ukrainian","text":"в'язати "},"groupID":"dev-grp-069","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"dev-preserve-070","category":"apostrophe","events":[{"rendered":"к","type":"key","keyCode":15,"modifiers":[]},{"rendered":"о","type":"key","keyCode":38,"modifiers":[]},{"rendered":"м","type":"key","keyCode":9,"modifiers":[]},{"rendered":"п","type":"key","keyCode":5,"modifiers":[]},{"rendered":"'","type":"key","keyCode":50,"modifiers":[]},{"rendered":"ю","type":"key","keyCode":47,"modifiers":[]},{"rendered":"т","type":"key","keyCode":45,"modifiers":[]},{"rendered":"е","type":"key","keyCode":17,"modifiers":[]},{"rendered":"р","type":"key","keyCode":4,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"split":"development","languages":["en","uk"],"expected":{"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.Ukrainian","checkpoints":[{"afterEvent":10,"text":"комп'ютер ","language":"uk"}],"text":"комп'ютер ","language":"uk"},"groupID":"dev-grp-070","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"dev-preserve-071","category":"acronym","events":[{"rendered":"Н","type":"key","keyCode":16,"modifiers":["shift"]},{"rendered":"А","type":"key","keyCode":3,"modifiers":["shift"]},{"rendered":"Т","type":"key","keyCode":45,"modifiers":["shift"]},{"rendered":"О","type":"key","keyCode":38,"modifiers":["shift"]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"split":"development","languages":["en","uk"],"expected":{"language":"uk","checkpoints":[{"language":"uk","text":"НАТО ","afterEvent":5}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.Ukrainian","text":"НАТО "},"groupID":"dev-grp-071","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"dev-preserve-072","category":"acronym","events":[{"rendered":"О","type":"key","keyCode":38,"modifiers":["shift"]},{"rendered":"О","type":"key","keyCode":38,"modifiers":["shift"]},{"rendered":"Н","type":"key","keyCode":16,"modifiers":["shift"]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"split":"development","languages":["en","uk"],"expected":{"language":"uk","checkpoints":[{"text":"ООН ","language":"uk","afterEvent":4}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.Ukrainian","text":"ООН "},"groupID":"dev-grp-072","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"dev-preserve-073","category":"acronym","events":[{"rendered":"С","type":"key","keyCode":8,"modifiers":["shift"]},{"rendered":"Ш","type":"key","keyCode":34,"modifiers":["shift"]},{"rendered":"А","type":"key","keyCode":3,"modifiers":["shift"]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"split":"development","languages":["en","uk"],"expected":{"language":"uk","checkpoints":[{"afterEvent":4,"text":"США ","language":"uk"}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.Ukrainian","text":"США "},"groupID":"dev-grp-073","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"dev-preserve-074","category":"acronym","events":[{"rendered":"Н","type":"key","keyCode":16,"modifiers":["shift"]},{"rendered":"Б","type":"key","keyCode":43,"modifiers":["shift"]},{"rendered":"У","type":"key","keyCode":14,"modifiers":["shift"]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"split":"development","languages":["en","uk"],"expected":{"language":"uk","checkpoints":[{"afterEvent":4,"text":"НБУ ","language":"uk"}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.Ukrainian","text":"НБУ "},"groupID":"dev-grp-074","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"dev-preserve-075","category":"punctuation","events":[{"rendered":"д","type":"key","keyCode":37,"modifiers":[]},{"rendered":"о","type":"key","keyCode":38,"modifiers":[]},{"rendered":"б","type":"key","keyCode":43,"modifiers":[]},{"rendered":"р","type":"key","keyCode":4,"modifiers":[]},{"rendered":"е","type":"key","keyCode":17,"modifiers":[]},{"rendered":"!","type":"key","keyCode":18,"modifiers":["shift"]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"split":"development","languages":["en","uk"],"expected":{"language":"uk","checkpoints":[{"afterEvent":7,"language":"uk","text":"добре! "}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.Ukrainian","text":"добре! "},"groupID":"dev-grp-075","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"dev-preserve-076","category":"punctuation","events":[{"rendered":"(","type":"key","keyCode":25,"modifiers":["shift"]},{"rendered":"т","type":"key","keyCode":45,"modifiers":[]},{"rendered":"а","type":"key","keyCode":3,"modifiers":[]},{"rendered":"к","type":"key","keyCode":15,"modifiers":[]},{"rendered":")","type":"key","keyCode":29,"modifiers":["shift"]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"split":"development","languages":["en","uk"],"expected":{"language":"uk","checkpoints":[{"afterEvent":6,"language":"uk","text":"(так) "}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.Ukrainian","text":"(так) "},"groupID":"dev-grp-076","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"dev-preserve-077","category":"punctuation","events":[{"rendered":"с","type":"key","keyCode":8,"modifiers":[]},{"rendered":"л","type":"key","keyCode":40,"modifiers":[]},{"rendered":"о","type":"key","keyCode":38,"modifiers":[]},{"rendered":"в","type":"key","keyCode":2,"modifiers":[]},{"rendered":"о","type":"key","keyCode":38,"modifiers":[]},{"rendered":":","type":"key","keyCode":23,"modifiers":["shift"]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"split":"development","languages":["en","uk"],"expected":{"language":"uk","checkpoints":[{"language":"uk","afterEvent":7,"text":"слово: "}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.Ukrainian","text":"слово: "},"groupID":"dev-grp-077","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"dev-preserve-078","category":"symbol","events":[{"rendered":"1","type":"key","keyCode":18,"modifiers":[]},{"rendered":"0","type":"key","keyCode":29,"modifiers":[]},{"rendered":"0","type":"key","keyCode":29,"modifiers":[]},{"rendered":"%","type":"key","keyCode":21,"modifiers":["shift"]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"split":"development","languages":["en","uk"],"expected":{"language":"uk","checkpoints":[{"language":"uk","afterEvent":5,"text":"100% "}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.Ukrainian","text":"100% "},"groupID":"dev-grp-078","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"dev-preserve-079","category":"phrase","events":[{"rendered":"д","type":"key","keyCode":37,"modifiers":[]},{"rendered":"о","type":"key","keyCode":38,"modifiers":[]},{"rendered":"б","type":"key","keyCode":43,"modifiers":[]},{"rendered":"р","type":"key","keyCode":4,"modifiers":[]},{"rendered":"и","type":"key","keyCode":1,"modifiers":[]},{"rendered":"й","type":"key","keyCode":12,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]},{"rendered":"р","type":"key","keyCode":4,"modifiers":[]},{"rendered":"а","type":"key","keyCode":3,"modifiers":[]},{"rendered":"н","type":"key","keyCode":16,"modifiers":[]},{"rendered":"о","type":"key","keyCode":38,"modifiers":[]},{"rendered":"к","type":"key","keyCode":15,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"split":"development","languages":["en","uk"],"expected":{"language":"uk","checkpoints":[{"text":"добрий ранок ","afterEvent":13,"language":"uk"}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.Ukrainian","text":"добрий ранок "},"groupID":"dev-grp-079","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"dev-preserve-080","category":"phrase","events":[{"rendered":"г","type":"key","keyCode":32,"modifiers":[]},{"rendered":"а","type":"key","keyCode":3,"modifiers":[]},{"rendered":"р","type":"key","keyCode":4,"modifiers":[]},{"rendered":"н","type":"key","keyCode":16,"modifiers":[]},{"rendered":"о","type":"key","keyCode":38,"modifiers":[]},{"rendered":"г","type":"key","keyCode":32,"modifiers":[]},{"rendered":"о","type":"key","keyCode":38,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]},{"rendered":"д","type":"key","keyCode":37,"modifiers":[]},{"rendered":"н","type":"key","keyCode":16,"modifiers":[]},{"rendered":"я","type":"key","keyCode":6,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"split":"development","languages":["en","uk"],"expected":{"language":"uk","checkpoints":[{"afterEvent":12,"language":"uk","text":"гарного дня "}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.Ukrainian","text":"гарного дня "},"groupID":"dev-grp-080","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"dev-abstain-081","category":"ambiguous-short","events":[{"rendered":"t","type":"key","keyCode":17,"modifiers":[]},{"rendered":"o","type":"key","keyCode":31,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"split":"development","languages":["en","uk"],"expected":{"language":"en","checkpoints":[{"language":"en","text":"to ","afterEvent":3}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.ABC","text":"to "},"groupID":"dev-grp-081","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"abstain"} +{"schemaVersion":1,"id":"dev-abstain-082","category":"ambiguous-short","events":[{"rendered":"m","type":"key","keyCode":46,"modifiers":[]},{"rendered":"e","type":"key","keyCode":14,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"split":"development","languages":["en","uk"],"expected":{"language":"en","checkpoints":[{"afterEvent":3,"language":"en","text":"me "}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.ABC","text":"me "},"groupID":"dev-grp-082","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"abstain"} +{"schemaVersion":1,"id":"dev-abstain-083","category":"ambiguous-short","events":[{"rendered":"d","type":"key","keyCode":2,"modifiers":[]},{"rendered":"o","type":"key","keyCode":31,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"split":"development","languages":["en","uk"],"expected":{"language":"en","checkpoints":[{"text":"do ","language":"en","afterEvent":3}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.ABC","text":"do "},"groupID":"dev-grp-083","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"abstain"} +{"schemaVersion":1,"id":"dev-abstain-084","category":"ambiguous-short","events":[{"rendered":"n","type":"key","keyCode":45,"modifiers":[]},{"rendered":"o","type":"key","keyCode":31,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"split":"development","languages":["en","uk"],"expected":{"language":"en","checkpoints":[{"text":"no ","afterEvent":3,"language":"en"}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.ABC","text":"no "},"groupID":"dev-grp-084","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"abstain"} +{"schemaVersion":1,"id":"dev-abstain-085","category":"ambiguous-short","events":[{"rendered":"o","type":"key","keyCode":31,"modifiers":[]},{"rendered":"r","type":"key","keyCode":15,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"split":"development","languages":["en","uk"],"expected":{"language":"en","checkpoints":[{"afterEvent":3,"language":"en","text":"or "}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.ABC","text":"or "},"groupID":"dev-grp-085","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"abstain"} +{"schemaVersion":1,"id":"dev-abstain-086","category":"gibberish","events":[{"rendered":"a","type":"key","keyCode":0,"modifiers":[]},{"rendered":"s","type":"key","keyCode":1,"modifiers":[]},{"rendered":"d","type":"key","keyCode":2,"modifiers":[]},{"rendered":"f","type":"key","keyCode":3,"modifiers":[]},{"rendered":"g","type":"key","keyCode":5,"modifiers":[]},{"rendered":"h","type":"key","keyCode":4,"modifiers":[]},{"rendered":"j","type":"key","keyCode":38,"modifiers":[]},{"rendered":"k","type":"key","keyCode":40,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"split":"development","languages":["en","uk"],"expected":{"language":"en","checkpoints":[{"language":"en","afterEvent":9,"text":"asdfghjk "}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.ABC","text":"asdfghjk "},"groupID":"dev-grp-086","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"abstain"} +{"schemaVersion":1,"id":"dev-abstain-087","category":"gibberish","events":[{"rendered":"q","type":"key","keyCode":12,"modifiers":[]},{"rendered":"w","type":"key","keyCode":13,"modifiers":[]},{"rendered":"e","type":"key","keyCode":14,"modifiers":[]},{"rendered":"r","type":"key","keyCode":15,"modifiers":[]},{"rendered":"t","type":"key","keyCode":17,"modifiers":[]},{"rendered":"y","type":"key","keyCode":16,"modifiers":[]},{"rendered":"u","type":"key","keyCode":32,"modifiers":[]},{"rendered":"i","type":"key","keyCode":34,"modifiers":[]},{"rendered":"o","type":"key","keyCode":31,"modifiers":[]},{"rendered":"p","type":"key","keyCode":35,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"split":"development","languages":["en","uk"],"expected":{"language":"en","checkpoints":[{"afterEvent":11,"text":"qwertyuiop ","language":"en"}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.ABC","text":"qwertyuiop "},"groupID":"dev-grp-087","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"abstain"} +{"schemaVersion":1,"id":"dev-abstain-088","category":"isolated-char","events":[{"rendered":"a","type":"key","keyCode":0,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"split":"development","languages":["en","uk"],"expected":{"language":"en","checkpoints":[{"text":"a ","afterEvent":2,"language":"en"}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.ABC","text":"a "},"groupID":"dev-grp-088","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"abstain"} +{"schemaVersion":1,"id":"dev-abstain-089","category":"isolated-char","events":[{"rendered":"x","type":"key","keyCode":7,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"split":"development","languages":["en","uk"],"expected":{"language":"en","checkpoints":[{"text":"x ","language":"en","afterEvent":2}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.ABC","text":"x "},"groupID":"dev-grp-089","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"abstain"} +{"schemaVersion":1,"id":"dev-abstain-090","category":"isolated-symbol","events":[{"rendered":":","type":"key","keyCode":41,"modifiers":["shift"]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"split":"development","languages":["en","uk"],"expected":{"language":"en","checkpoints":[{"afterEvent":2,"text":": ","language":"en"}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.ABC","text":": "},"groupID":"dev-grp-090","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"abstain"} +{"schemaVersion":1,"id":"dev-abstain-091","category":"ambiguous-short","events":[{"rendered":"т","type":"key","keyCode":45,"modifiers":[]},{"rendered":"о","type":"key","keyCode":38,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"split":"development","languages":["en","uk"],"expected":{"language":"uk","checkpoints":[{"afterEvent":3,"text":"то ","language":"uk"}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.Ukrainian","text":"то "},"groupID":"dev-grp-091","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"abstain"} +{"schemaVersion":1,"id":"dev-abstain-092","category":"ambiguous-short","events":[{"rendered":"н","type":"key","keyCode":16,"modifiers":[]},{"rendered":"е","type":"key","keyCode":17,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"split":"development","languages":["en","uk"],"expected":{"language":"uk","checkpoints":[{"language":"uk","afterEvent":3,"text":"не "}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.Ukrainian","text":"не "},"groupID":"dev-grp-092","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"abstain"} +{"schemaVersion":1,"id":"dev-abstain-093","category":"ambiguous-short","events":[{"rendered":"ч","type":"key","keyCode":7,"modifiers":[]},{"rendered":"и","type":"key","keyCode":1,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"split":"development","languages":["en","uk"],"expected":{"language":"uk","checkpoints":[{"text":"чи ","language":"uk","afterEvent":3}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.Ukrainian","text":"чи "},"groupID":"dev-grp-093","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"abstain"} +{"schemaVersion":1,"id":"dev-abstain-094","category":"ambiguous-short","events":[{"rendered":"з","type":"key","keyCode":35,"modifiers":[]},{"rendered":"а","type":"key","keyCode":3,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"split":"development","languages":["en","uk"],"expected":{"language":"uk","checkpoints":[{"text":"за ","afterEvent":3,"language":"uk"}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.Ukrainian","text":"за "},"groupID":"dev-grp-094","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"abstain"} +{"schemaVersion":1,"id":"dev-abstain-095","category":"ambiguous-short","events":[{"rendered":"я","type":"key","keyCode":6,"modifiers":[]},{"rendered":"к","type":"key","keyCode":15,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"split":"development","languages":["en","uk"],"expected":{"language":"uk","checkpoints":[{"afterEvent":3,"text":"як ","language":"uk"}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.Ukrainian","text":"як "},"groupID":"dev-grp-095","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"abstain"} +{"schemaVersion":1,"id":"dev-abstain-096","category":"gibberish","events":[{"rendered":"й","type":"key","keyCode":12,"modifiers":[]},{"rendered":"ц","type":"key","keyCode":13,"modifiers":[]},{"rendered":"у","type":"key","keyCode":14,"modifiers":[]},{"rendered":"к","type":"key","keyCode":15,"modifiers":[]},{"rendered":"е","type":"key","keyCode":17,"modifiers":[]},{"rendered":"н","type":"key","keyCode":16,"modifiers":[]},{"rendered":"г","type":"key","keyCode":32,"modifiers":[]},{"rendered":"ш","type":"key","keyCode":34,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"split":"development","languages":["en","uk"],"expected":{"language":"uk","checkpoints":[{"afterEvent":9,"text":"йцукенгш ","language":"uk"}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.Ukrainian","text":"йцукенгш "},"groupID":"dev-grp-096","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"abstain"} +{"schemaVersion":1,"id":"dev-abstain-097","category":"gibberish","events":[{"rendered":"ф","type":"key","keyCode":0,"modifiers":[]},{"rendered":"і","type":"key","keyCode":11,"modifiers":[]},{"rendered":"в","type":"key","keyCode":2,"modifiers":[]},{"rendered":"а","type":"key","keyCode":3,"modifiers":[]},{"rendered":"п","type":"key","keyCode":5,"modifiers":[]},{"rendered":"р","type":"key","keyCode":4,"modifiers":[]},{"rendered":"о","type":"key","keyCode":38,"modifiers":[]},{"rendered":"л","type":"key","keyCode":40,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"split":"development","languages":["en","uk"],"expected":{"language":"uk","checkpoints":[{"afterEvent":9,"text":"фівапрол ","language":"uk"}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.Ukrainian","text":"фівапрол "},"groupID":"dev-grp-097","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"abstain"} +{"schemaVersion":1,"id":"dev-abstain-098","category":"isolated-char","events":[{"rendered":"я","type":"key","keyCode":6,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"split":"development","languages":["en","uk"],"expected":{"language":"uk","checkpoints":[{"afterEvent":2,"language":"uk","text":"я "}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.Ukrainian","text":"я "},"groupID":"dev-grp-098","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"abstain"} +{"schemaVersion":1,"id":"dev-abstain-099","category":"isolated-symbol","events":[{"rendered":";","type":"key","keyCode":28,"modifiers":["shift"]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"split":"development","languages":["en","uk"],"expected":{"language":"uk","checkpoints":[{"afterEvent":2,"text":"; ","language":"uk"}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.Ukrainian","text":"; "},"groupID":"dev-grp-099","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"abstain"} +{"schemaVersion":1,"id":"dev-abstain-100","category":"symbol","events":[{"rendered":"-","type":"key","keyCode":27,"modifiers":[]},{"rendered":"-","type":"key","keyCode":27,"modifiers":[]},{"rendered":"-","type":"key","keyCode":27,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"split":"development","languages":["en","uk"],"expected":{"language":"uk","checkpoints":[{"text":"--- ","language":"uk","afterEvent":4}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.Ukrainian","text":"--- "},"groupID":"dev-grp-100","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"abstain"} diff --git a/Benchmarks/layout-corpus/v1/holdout.jsonl b/Benchmarks/layout-corpus/v1/holdout.jsonl new file mode 100644 index 0000000..b49fe35 --- /dev/null +++ b/Benchmarks/layout-corpus/v1/holdout.jsonl @@ -0,0 +1,100 @@ +{"schemaVersion":1,"id":"holdout-repair-001","category":"single-word","events":[{"modifiers":[],"type":"key","keyCode":9,"rendered":"v"},{"modifiers":[],"type":"key","keyCode":38,"rendered":"j"},{"modifiers":[],"type":"key","keyCode":2,"rendered":"d"},{"modifiers":[],"type":"key","keyCode":3,"rendered":"f"},{"modifiers":[],"type":"boundary","keyCode":49,"rendered":" "}],"initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"split":"holdout","languages":["en","uk"],"expected":{"text":"мова ","allowedAutomaticActions":["replaceSuffix","switchSource"],"checkpoints":[{"afterEvent":5,"text":"мова ","language":"uk"}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian"},"groupID":"holdout-grp-001","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"repair"} +{"schemaVersion":1,"id":"holdout-repair-002","category":"capitalized","events":[{"rendered":"E","type":"key","modifiers":["shift"],"keyCode":14},{"rendered":"r","type":"key","modifiers":[],"keyCode":15},{"rendered":"h","type":"key","modifiers":[],"keyCode":4},{"rendered":"f","type":"key","modifiers":[],"keyCode":3},{"rendered":"]","type":"key","modifiers":[],"keyCode":30},{"rendered":"y","type":"key","modifiers":[],"keyCode":16},{"rendered":"f","type":"key","modifiers":[],"keyCode":3},{"rendered":" ","type":"boundary","modifiers":[],"keyCode":49}],"initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"split":"holdout","languages":["en","uk"],"expected":{"sourceID":"com.apple.keylayout.Ukrainian","text":"Україна ","language":"uk","allowedAutomaticActions":["replaceSuffix","switchSource"],"checkpoints":[{"afterEvent":8,"text":"Україна ","language":"uk"}]},"groupID":"holdout-grp-002","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"repair"} +{"schemaVersion":1,"id":"holdout-repair-003","category":"single-word","events":[{"rendered":"l","type":"key","modifiers":[],"keyCode":37},{"rendered":"z","type":"key","modifiers":[],"keyCode":6},{"rendered":"r","type":"key","modifiers":[],"keyCode":15},{"rendered":"e","type":"key","modifiers":[],"keyCode":14},{"rendered":"'","type":"key","modifiers":[],"keyCode":39},{"rendered":"v","type":"key","modifiers":[],"keyCode":9},{"rendered":"j","type":"key","modifiers":[],"keyCode":38},{"rendered":" ","type":"boundary","modifiers":[],"keyCode":49}],"initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"split":"holdout","languages":["en","uk"],"expected":{"sourceID":"com.apple.keylayout.Ukrainian","text":"дякуємо ","language":"uk","allowedAutomaticActions":["replaceSuffix","switchSource"],"checkpoints":[{"text":"дякуємо ","afterEvent":8,"language":"uk"}]},"groupID":"holdout-grp-003","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"repair"} +{"schemaVersion":1,"id":"holdout-repair-004","category":"back-to-back","events":[{"rendered":"y","type":"key","modifiers":[],"keyCode":16},{"rendered":"j","type":"key","modifiers":[],"keyCode":38},{"rendered":"d","type":"key","modifiers":[],"keyCode":2},{"rendered":"s","type":"key","modifiers":[],"keyCode":1},{"rendered":"q","type":"key","modifiers":[],"keyCode":12},{"rendered":" ","type":"boundary","modifiers":[],"keyCode":49},{"rendered":"l","type":"key","modifiers":[],"keyCode":37},{"rendered":"t","type":"key","modifiers":[],"keyCode":17},{"rendered":"y","type":"key","modifiers":[],"keyCode":16},{"rendered":"m","type":"key","modifiers":[],"keyCode":46},{"rendered":" ","type":"boundary","modifiers":[],"keyCode":49}],"initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"split":"holdout","languages":["en","uk"],"expected":{"language":"uk","checkpoints":[{"afterEvent":11,"language":"uk","text":"новий день "}],"allowedAutomaticActions":["replaceSuffix","switchSource"],"sourceID":"com.apple.keylayout.Ukrainian","text":"новий день "},"groupID":"holdout-grp-004","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"repair"} +{"schemaVersion":1,"id":"holdout-repair-005","category":"single-word","events":[{"rendered":"r","type":"key","modifiers":[],"keyCode":15},{"rendered":"j","type":"key","modifiers":[],"keyCode":38},{"rendered":"h","type":"key","modifiers":[],"keyCode":4},{"rendered":"s","type":"key","modifiers":[],"keyCode":1},{"rendered":"c","type":"key","modifiers":[],"keyCode":8},{"rendered":"n","type":"key","modifiers":[],"keyCode":45},{"rendered":"e","type":"key","modifiers":[],"keyCode":14},{"rendered":"d","type":"key","modifiers":[],"keyCode":2},{"rendered":"f","type":"key","modifiers":[],"keyCode":3},{"rendered":"x","type":"key","modifiers":[],"keyCode":7},{"rendered":" ","type":"boundary","modifiers":[],"keyCode":49}],"initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"split":"holdout","languages":["en","uk"],"expected":{"language":"uk","checkpoints":[{"afterEvent":11,"language":"uk","text":"користувач "}],"allowedAutomaticActions":["replaceSuffix","switchSource"],"sourceID":"com.apple.keylayout.Ukrainian","text":"користувач "},"groupID":"holdout-grp-005","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"repair"} +{"schemaVersion":1,"id":"holdout-repair-006","category":"single-word","events":[{"rendered":"l","type":"key","modifiers":[],"keyCode":37},{"rendered":"j","type":"key","modifiers":[],"keyCode":38},{"rendered":"r","type":"key","modifiers":[],"keyCode":15},{"rendered":"e","type":"key","modifiers":[],"keyCode":14},{"rendered":"v","type":"key","modifiers":[],"keyCode":9},{"rendered":"t","type":"key","modifiers":[],"keyCode":17},{"rendered":"y","type":"key","modifiers":[],"keyCode":16},{"rendered":"n","type":"key","modifiers":[],"keyCode":45},{"rendered":" ","type":"boundary","modifiers":[],"keyCode":49}],"initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"split":"holdout","languages":["en","uk"],"expected":{"sourceID":"com.apple.keylayout.Ukrainian","text":"документ ","language":"uk","allowedAutomaticActions":["replaceSuffix","switchSource"],"checkpoints":[{"text":"документ ","afterEvent":9,"language":"uk"}]},"groupID":"holdout-grp-006","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"repair"} +{"schemaVersion":1,"id":"holdout-repair-007","category":"single-word","events":[{"rendered":"v","type":"key","modifiers":[],"keyCode":9},{"rendered":"j","type":"key","modifiers":[],"keyCode":38},{"rendered":";","type":"key","modifiers":[],"keyCode":41},{"rendered":"k","type":"key","modifiers":[],"keyCode":40},{"rendered":"s","type":"key","modifiers":[],"keyCode":1},{"rendered":"d","type":"key","modifiers":[],"keyCode":2},{"rendered":"j","type":"key","modifiers":[],"keyCode":38},{"rendered":" ","type":"boundary","modifiers":[],"keyCode":49}],"initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"split":"holdout","languages":["en","uk"],"expected":{"sourceID":"com.apple.keylayout.Ukrainian","text":"можливо ","language":"uk","allowedAutomaticActions":["replaceSuffix","switchSource"],"checkpoints":[{"text":"можливо ","afterEvent":8,"language":"uk"}]},"groupID":"holdout-grp-007","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"repair"} +{"schemaVersion":1,"id":"holdout-repair-008","category":"single-word","events":[{"rendered":",","type":"key","modifiers":[],"keyCode":43},{"rendered":"f","type":"key","modifiers":[],"keyCode":3},{"rendered":"x","type":"key","modifiers":[],"keyCode":7},{"rendered":"s","type":"key","modifiers":[],"keyCode":1},{"rendered":"v","type":"key","modifiers":[],"keyCode":9},{"rendered":"j","type":"key","modifiers":[],"keyCode":38},{"rendered":" ","type":"boundary","modifiers":[],"keyCode":49}],"initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"split":"holdout","languages":["en","uk"],"expected":{"sourceID":"com.apple.keylayout.Ukrainian","text":"бачимо ","language":"uk","allowedAutomaticActions":["replaceSuffix","switchSource"],"checkpoints":[{"text":"бачимо ","language":"uk","afterEvent":7}]},"groupID":"holdout-grp-008","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"repair"} +{"schemaVersion":1,"id":"holdout-repair-009","category":"single-word","events":[{"rendered":"d","type":"key","modifiers":[],"keyCode":2},{"rendered":"t","type":"key","modifiers":[],"keyCode":17},{"rendered":"x","type":"key","modifiers":[],"keyCode":7},{"rendered":"b","type":"key","modifiers":[],"keyCode":11},{"rendered":"h","type":"key","modifiers":[],"keyCode":4},{"rendered":" ","type":"boundary","modifiers":[],"keyCode":49}],"initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"split":"holdout","languages":["en","uk"],"expected":{"sourceID":"com.apple.keylayout.Ukrainian","text":"вечір ","language":"uk","allowedAutomaticActions":["replaceSuffix","switchSource"],"checkpoints":[{"text":"вечір ","language":"uk","afterEvent":6}]},"groupID":"holdout-grp-009","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"repair"} +{"label":"repair","events":[{"keyCode":37,"modifiers":[],"type":"key","rendered":"l"},{"keyCode":17,"modifiers":[],"type":"key","rendered":"t"},{"keyCode":49,"modifiers":[],"type":"boundary","rendered":" "},{"keyCode":45,"modifiers":[],"type":"key","rendered":"n"},{"keyCode":1,"modifiers":[],"type":"key","rendered":"s"},{"keyCode":49,"modifiers":[],"type":"boundary","rendered":" "}],"id":"holdout-repair-010","split":"holdout","languages":["en","uk"],"category":"question","provenance":{"seed":0,"generator":"manual-v1","kind":"synthetic","review":"pending-human"},"initial":{"sourceID":"com.apple.keylayout.ABC","keyboardType":91,"preferredCyrillic":"uk","text":"","language":"en","caretUTF16":0},"expected":{"sourceID":"com.apple.keylayout.Ukrainian","checkpoints":[{"language":"uk","afterEvent":6,"text":"де ти "}],"text":"де ти ","language":"uk","allowedAutomaticActions":["replaceSuffix","switchSource"]},"groupID":"holdout-grp-010","schemaVersion":1} +{"label":"repair","events":[{"keyCode":0,"modifiers":[],"type":"key","rendered":"ф"},{"keyCode":35,"modifiers":[],"type":"key","rendered":"з"},{"keyCode":35,"modifiers":[],"type":"key","rendered":"з"},{"keyCode":37,"modifiers":[],"type":"key","rendered":"д"},{"keyCode":14,"modifiers":[],"type":"key","rendered":"у"},{"keyCode":49,"modifiers":[],"type":"boundary","rendered":" "}],"id":"holdout-repair-011","split":"holdout","languages":["en","uk"],"category":"single-word","provenance":{"seed":0,"generator":"manual-v1","kind":"synthetic","review":"pending-human"},"initial":{"sourceID":"com.apple.keylayout.Ukrainian","keyboardType":91,"preferredCyrillic":"uk","text":"","language":"uk","caretUTF16":0},"expected":{"sourceID":"com.apple.keylayout.ABC","checkpoints":[{"text":"apple ","language":"en","afterEvent":6}],"text":"apple ","language":"en","allowedAutomaticActions":["replaceSuffix","switchSource"]},"groupID":"holdout-grp-011","schemaVersion":1} +{"label":"repair","events":[{"keyCode":8,"modifiers":["shift"],"type":"key","rendered":"С"},{"keyCode":0,"modifiers":[],"type":"key","rendered":"ф"},{"keyCode":45,"modifiers":[],"type":"key","rendered":"т"},{"keyCode":0,"modifiers":[],"type":"key","rendered":"ф"},{"keyCode":2,"modifiers":[],"type":"key","rendered":"в"},{"keyCode":0,"modifiers":[],"type":"key","rendered":"ф"},{"type":"boundary","keyCode":49,"modifiers":[],"rendered":" "}],"id":"holdout-repair-012","split":"holdout","languages":["en","uk"],"category":"capitalized","provenance":{"seed":0,"generator":"manual-v1","kind":"synthetic","review":"pending-human"},"initial":{"sourceID":"com.apple.keylayout.Ukrainian","keyboardType":91,"preferredCyrillic":"uk","text":"","language":"uk","caretUTF16":0},"expected":{"sourceID":"com.apple.keylayout.ABC","checkpoints":[{"afterEvent":7,"text":"Canada ","language":"en"}],"text":"Canada ","language":"en","allowedAutomaticActions":["replaceSuffix","switchSource"]},"groupID":"holdout-grp-012","schemaVersion":1} +{"label":"repair","events":[{"keyCode":13,"modifiers":[],"type":"key","rendered":"ц"},{"keyCode":14,"modifiers":[],"type":"key","rendered":"у"},{"keyCode":37,"modifiers":[],"type":"key","rendered":"д"},{"keyCode":8,"modifiers":[],"type":"key","rendered":"с"},{"keyCode":31,"modifiers":[],"type":"key","rendered":"щ"},{"keyCode":46,"modifiers":[],"type":"key","rendered":"ь"},{"keyCode":14,"modifiers":[],"type":"key","rendered":"у"},{"keyCode":49,"modifiers":[],"type":"boundary","rendered":" "}],"id":"holdout-repair-013","split":"holdout","languages":["en","uk"],"category":"single-word","provenance":{"seed":0,"generator":"manual-v1","kind":"synthetic","review":"pending-human"},"initial":{"sourceID":"com.apple.keylayout.Ukrainian","keyboardType":91,"preferredCyrillic":"uk","text":"","language":"uk","caretUTF16":0},"expected":{"sourceID":"com.apple.keylayout.ABC","checkpoints":[{"language":"en","afterEvent":8,"text":"welcome "}],"text":"welcome ","language":"en","allowedAutomaticActions":["replaceSuffix","switchSource"]},"groupID":"holdout-grp-013","schemaVersion":1} +{"label":"repair","events":[{"keyCode":45,"modifiers":[],"type":"key","rendered":"т"},{"keyCode":14,"modifiers":[],"type":"key","rendered":"у"},{"keyCode":13,"modifiers":[],"type":"key","rendered":"ц"},{"keyCode":49,"modifiers":[],"type":"boundary","rendered":" "},{"keyCode":3,"modifiers":[],"type":"key","rendered":"а"},{"keyCode":34,"modifiers":[],"type":"key","rendered":"ш"},{"keyCode":37,"modifiers":[],"type":"key","rendered":"д"},{"keyCode":14,"modifiers":[],"type":"key","rendered":"у"},{"keyCode":49,"modifiers":[],"type":"boundary","rendered":" "}],"id":"holdout-repair-014","split":"holdout","languages":["en","uk"],"category":"back-to-back","provenance":{"seed":0,"generator":"manual-v1","kind":"synthetic","review":"pending-human"},"initial":{"sourceID":"com.apple.keylayout.Ukrainian","keyboardType":91,"preferredCyrillic":"uk","text":"","language":"uk","caretUTF16":0},"expected":{"sourceID":"com.apple.keylayout.ABC","checkpoints":[{"language":"en","afterEvent":9,"text":"new file "}],"text":"new file ","language":"en","allowedAutomaticActions":["replaceSuffix","switchSource"]},"groupID":"holdout-grp-014","schemaVersion":1} +{"label":"repair","events":[{"keyCode":11,"modifiers":[],"type":"key","rendered":"і"},{"keyCode":15,"modifiers":[],"type":"key","rendered":"к"},{"keyCode":31,"modifiers":[],"type":"key","rendered":"щ"},{"keyCode":13,"modifiers":[],"type":"key","rendered":"ц"},{"keyCode":1,"modifiers":[],"type":"key","rendered":"и"},{"keyCode":14,"modifiers":[],"type":"key","rendered":"у"},{"keyCode":15,"modifiers":[],"type":"key","rendered":"к"},{"keyCode":49,"modifiers":[],"type":"boundary","rendered":" "}],"id":"holdout-repair-015","split":"holdout","languages":["en","uk"],"category":"single-word","provenance":{"seed":0,"generator":"manual-v1","kind":"synthetic","review":"pending-human"},"initial":{"sourceID":"com.apple.keylayout.Ukrainian","keyboardType":91,"preferredCyrillic":"uk","text":"","language":"uk","caretUTF16":0},"expected":{"sourceID":"com.apple.keylayout.ABC","checkpoints":[{"language":"en","afterEvent":8,"text":"browser "}],"text":"browser ","language":"en","allowedAutomaticActions":["replaceSuffix","switchSource"]},"groupID":"holdout-grp-015","schemaVersion":1} +{"label":"repair","events":[{"keyCode":2,"modifiers":[],"type":"key","rendered":"в"},{"keyCode":31,"modifiers":[],"type":"key","rendered":"щ"},{"keyCode":8,"modifiers":[],"type":"key","rendered":"с"},{"keyCode":32,"modifiers":[],"type":"key","rendered":"г"},{"keyCode":46,"modifiers":[],"type":"key","rendered":"ь"},{"keyCode":14,"modifiers":[],"type":"key","rendered":"у"},{"keyCode":45,"modifiers":[],"type":"key","rendered":"т"},{"keyCode":17,"modifiers":[],"type":"key","rendered":"е"},{"keyCode":49,"modifiers":[],"type":"boundary","rendered":" "}],"id":"holdout-repair-016","split":"holdout","languages":["en","uk"],"category":"single-word","provenance":{"seed":0,"generator":"manual-v1","kind":"synthetic","review":"pending-human"},"initial":{"sourceID":"com.apple.keylayout.Ukrainian","keyboardType":91,"preferredCyrillic":"uk","text":"","language":"uk","caretUTF16":0},"expected":{"sourceID":"com.apple.keylayout.ABC","checkpoints":[{"language":"en","afterEvent":9,"text":"document "}],"text":"document ","language":"en","allowedAutomaticActions":["replaceSuffix","switchSource"]},"groupID":"holdout-grp-016","schemaVersion":1} +{"label":"repair","events":[{"keyCode":35,"modifiers":[],"type":"key","rendered":"з"},{"keyCode":14,"modifiers":[],"type":"key","rendered":"у"},{"keyCode":15,"modifiers":[],"type":"key","rendered":"к"},{"keyCode":4,"modifiers":[],"type":"key","rendered":"р"},{"keyCode":0,"modifiers":[],"type":"key","rendered":"ф"},{"keyCode":35,"modifiers":[],"type":"key","rendered":"з"},{"keyCode":1,"modifiers":[],"type":"key","rendered":"и"},{"keyCode":49,"modifiers":[],"type":"boundary","rendered":" "}],"id":"holdout-repair-017","split":"holdout","languages":["en","uk"],"category":"single-word","provenance":{"seed":0,"generator":"manual-v1","kind":"synthetic","review":"pending-human"},"initial":{"sourceID":"com.apple.keylayout.Ukrainian","keyboardType":91,"preferredCyrillic":"uk","text":"","language":"uk","caretUTF16":0},"expected":{"sourceID":"com.apple.keylayout.ABC","checkpoints":[{"language":"en","afterEvent":8,"text":"perhaps "}],"text":"perhaps ","language":"en","allowedAutomaticActions":["replaceSuffix","switchSource"]},"groupID":"holdout-grp-017","schemaVersion":1} +{"label":"repair","events":[{"rendered":"д","type":"key","keyCode":37,"modifiers":[]},{"rendered":"щ","type":"key","keyCode":31,"modifiers":[]},{"rendered":"щ","type":"key","keyCode":31,"modifiers":[]},{"rendered":"л","type":"key","keyCode":40,"modifiers":[]},{"rendered":"ш","type":"key","keyCode":34,"modifiers":[]},{"rendered":"т","type":"key","keyCode":45,"modifiers":[]},{"rendered":"п","type":"key","keyCode":5,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"id":"holdout-repair-018","split":"holdout","languages":["en","uk"],"category":"single-word","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"expected":{"language":"en","checkpoints":[{"language":"en","afterEvent":8,"text":"looking "}],"allowedAutomaticActions":["replaceSuffix","switchSource"],"sourceID":"com.apple.keylayout.ABC","text":"looking "},"groupID":"holdout-grp-018","schemaVersion":1} +{"label":"repair","events":[{"rendered":"у","type":"key","keyCode":14,"modifiers":[]},{"rendered":"м","type":"key","keyCode":9,"modifiers":[]},{"rendered":"у","type":"key","keyCode":14,"modifiers":[]},{"rendered":"т","type":"key","keyCode":45,"modifiers":[]},{"rendered":"ш","type":"key","keyCode":34,"modifiers":[]},{"rendered":"т","type":"key","keyCode":45,"modifiers":[]},{"rendered":"п","type":"key","keyCode":5,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"id":"holdout-repair-019","split":"holdout","languages":["en","uk"],"category":"single-word","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"expected":{"language":"en","checkpoints":[{"language":"en","afterEvent":8,"text":"evening "}],"allowedAutomaticActions":["replaceSuffix","switchSource"],"sourceID":"com.apple.keylayout.ABC","text":"evening "},"groupID":"holdout-grp-019","schemaVersion":1} +{"label":"repair","events":[{"rendered":"ц","type":"key","keyCode":13,"modifiers":[]},{"rendered":"р","type":"key","keyCode":4,"modifiers":[]},{"rendered":"у","type":"key","keyCode":14,"modifiers":[]},{"rendered":"к","type":"key","keyCode":15,"modifiers":[]},{"rendered":"у","type":"key","keyCode":14,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]},{"rendered":"ф","type":"key","keyCode":0,"modifiers":[]},{"rendered":"к","type":"key","keyCode":15,"modifiers":[]},{"rendered":"у","type":"key","keyCode":14,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]},{"rendered":"н","type":"key","keyCode":16,"modifiers":[]},{"rendered":"щ","type":"key","keyCode":31,"modifiers":[]},{"rendered":"г","type":"key","keyCode":32,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"id":"holdout-repair-020","split":"holdout","languages":["en","uk"],"category":"question","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"expected":{"allowedAutomaticActions":["replaceSuffix","switchSource"],"language":"en","text":"where are you ","checkpoints":[{"language":"en","text":"where are you ","afterEvent":14}],"sourceID":"com.apple.keylayout.ABC"},"groupID":"holdout-grp-020","schemaVersion":1} +{"label":"preserve","events":[{"rendered":"o","type":"key","keyCode":31,"modifiers":[]},{"rendered":"r","type":"key","keyCode":15,"modifiers":[]},{"rendered":"a","type":"key","keyCode":0,"modifiers":[]},{"rendered":"n","type":"key","keyCode":45,"modifiers":[]},{"rendered":"g","type":"key","keyCode":5,"modifiers":[]},{"rendered":"e","type":"key","keyCode":14,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"id":"holdout-preserve-021","split":"holdout","languages":["en","uk"],"category":"natural-word","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"expected":{"language":"en","checkpoints":[{"language":"en","text":"orange ","afterEvent":7}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.ABC","text":"orange "},"groupID":"holdout-grp-021","schemaVersion":1} +{"label":"preserve","events":[{"rendered":"p","type":"key","keyCode":35,"modifiers":[]},{"rendered":"y","type":"key","keyCode":16,"modifiers":[]},{"rendered":"t","type":"key","keyCode":17,"modifiers":[]},{"rendered":"h","type":"key","keyCode":4,"modifiers":[]},{"rendered":"o","type":"key","keyCode":31,"modifiers":[]},{"rendered":"n","type":"key","keyCode":45,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"id":"holdout-preserve-022","split":"holdout","languages":["en","uk"],"category":"natural-word","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"expected":{"language":"en","checkpoints":[{"language":"en","text":"python ","afterEvent":7}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.ABC","text":"python "},"groupID":"holdout-grp-022","schemaVersion":1} +{"label":"preserve","events":[{"rendered":"a","type":"key","keyCode":0,"modifiers":[]},{"rendered":"r","type":"key","keyCode":15,"modifiers":[]},{"rendered":"c","type":"key","keyCode":8,"modifiers":[]},{"rendered":"h","type":"key","keyCode":4,"modifiers":[]},{"rendered":"i","type":"key","keyCode":34,"modifiers":[]},{"rendered":"t","type":"key","keyCode":17,"modifiers":[]},{"rendered":"e","type":"key","keyCode":14,"modifiers":[]},{"rendered":"c","type":"key","keyCode":8,"modifiers":[]},{"rendered":"t","type":"key","keyCode":17,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"id":"holdout-preserve-023","split":"holdout","languages":["en","uk"],"category":"natural-word","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"expected":{"checkpoints":[{"language":"en","text":"architect ","afterEvent":10}],"language":"en","text":"architect ","allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.ABC"},"groupID":"holdout-grp-023","schemaVersion":1} +{"label":"preserve","events":[{"rendered":"c","type":"key","keyCode":8,"modifiers":[]},{"rendered":"o","type":"key","keyCode":31,"modifiers":[]},{"rendered":"n","type":"key","keyCode":45,"modifiers":[]},{"rendered":"s","type":"key","keyCode":1,"modifiers":[]},{"rendered":"o","type":"key","keyCode":31,"modifiers":[]},{"rendered":"l","type":"key","keyCode":37,"modifiers":[]},{"rendered":"e","type":"key","keyCode":14,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"id":"holdout-preserve-024","split":"holdout","languages":["en","uk"],"category":"natural-word","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"expected":{"language":"en","checkpoints":[{"language":"en","afterEvent":8,"text":"console "}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.ABC","text":"console "},"groupID":"holdout-grp-024","schemaVersion":1} +{"label":"preserve","events":[{"rendered":"u","type":"key","keyCode":32,"modifiers":[]},{"rendered":"p","type":"key","keyCode":35,"modifiers":[]},{"rendered":"d","type":"key","keyCode":2,"modifiers":[]},{"rendered":"a","type":"key","keyCode":0,"modifiers":[]},{"rendered":"t","type":"key","keyCode":17,"modifiers":[]},{"rendered":"e","type":"key","keyCode":14,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"id":"holdout-preserve-025","split":"holdout","languages":["en","uk"],"category":"natural-word","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"expected":{"language":"en","checkpoints":[{"language":"en","text":"update ","afterEvent":7}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.ABC","text":"update "},"groupID":"holdout-grp-025","schemaVersion":1} +{"label":"preserve","events":[{"rendered":"d","type":"key","keyCode":2,"modifiers":[]},{"rendered":"i","type":"key","keyCode":34,"modifiers":[]},{"rendered":"s","type":"key","keyCode":1,"modifiers":[]},{"rendered":"p","type":"key","keyCode":35,"modifiers":[]},{"rendered":"l","type":"key","keyCode":37,"modifiers":[]},{"rendered":"a","type":"key","keyCode":0,"modifiers":[]},{"rendered":"y","type":"key","keyCode":16,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"id":"holdout-preserve-026","split":"holdout","languages":["en","uk"],"category":"natural-word","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"expected":{"language":"en","checkpoints":[{"language":"en","afterEvent":8,"text":"display "}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.ABC","text":"display "},"groupID":"holdout-grp-026","schemaVersion":1} +{"label":"preserve","events":[{"rendered":"a","type":"key","keyCode":0,"modifiers":[]},{"rendered":"n","type":"key","keyCode":45,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"id":"holdout-preserve-027","split":"holdout","languages":["en","uk"],"category":"short-word","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"expected":{"language":"en","checkpoints":[{"afterEvent":3,"text":"an ","language":"en"}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.ABC","text":"an "},"groupID":"holdout-grp-027","schemaVersion":1} +{"label":"preserve","events":[{"rendered":"i","type":"key","keyCode":34,"modifiers":[]},{"rendered":"n","type":"key","keyCode":45,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"id":"holdout-preserve-028","split":"holdout","languages":["en","uk"],"category":"short-word","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"expected":{"language":"en","checkpoints":[{"text":"in ","language":"en","afterEvent":3}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.ABC","text":"in "},"groupID":"holdout-grp-028","schemaVersion":1} +{"label":"preserve","events":[{"rendered":"a","type":"key","keyCode":0,"modifiers":[]},{"rendered":"t","type":"key","keyCode":17,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"id":"holdout-preserve-029","split":"holdout","languages":["en","uk"],"category":"short-word","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"expected":{"language":"en","checkpoints":[{"language":"en","text":"at ","afterEvent":3}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.ABC","text":"at "},"groupID":"holdout-grp-029","schemaVersion":1} +{"label":"preserve","events":[{"rendered":"s","type":"key","keyCode":1,"modifiers":[]},{"rendered":"o","type":"key","keyCode":31,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"id":"holdout-preserve-030","split":"holdout","languages":["en","uk"],"category":"short-word","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"expected":{"language":"en","checkpoints":[{"language":"en","afterEvent":3,"text":"so "}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.ABC","text":"so "},"groupID":"holdout-grp-030","schemaVersion":1} +{"label":"preserve","events":[{"rendered":"i","type":"key","keyCode":34,"modifiers":[]},{"rendered":"f","type":"key","keyCode":3,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"id":"holdout-preserve-031","split":"holdout","languages":["en","uk"],"category":"short-word","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"expected":{"language":"en","checkpoints":[{"language":"en","text":"if ","afterEvent":3}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.ABC","text":"if "},"groupID":"holdout-grp-031","schemaVersion":1} +{"label":"preserve","events":[{"rendered":"u","type":"key","keyCode":32,"modifiers":[]},{"rendered":"p","type":"key","keyCode":35,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"id":"holdout-preserve-032","split":"holdout","languages":["en","uk"],"category":"short-word","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"expected":{"language":"en","checkpoints":[{"text":"up ","afterEvent":3,"language":"en"}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.ABC","text":"up "},"groupID":"holdout-grp-032","schemaVersion":1} +{"label":"preserve","events":[{"rendered":"G","type":"key","keyCode":5,"modifiers":["shift"]},{"rendered":"i","type":"key","keyCode":34,"modifiers":[]},{"rendered":"t","type":"key","keyCode":17,"modifiers":[]},{"rendered":"L","type":"key","keyCode":37,"modifiers":["shift"]},{"rendered":"a","type":"key","keyCode":0,"modifiers":[]},{"rendered":"b","type":"key","keyCode":11,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"id":"holdout-preserve-033","split":"holdout","languages":["en","uk"],"category":"proper-noun","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"expected":{"language":"en","checkpoints":[{"afterEvent":7,"text":"GitLab ","language":"en"}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.ABC","text":"GitLab "},"groupID":"holdout-grp-033","schemaVersion":1} +{"schemaVersion":1,"id":"holdout-preserve-034","category":"proper-noun","events":[{"rendered":"L","type":"key","modifiers":["shift"],"keyCode":37},{"rendered":"i","type":"key","modifiers":[],"keyCode":34},{"rendered":"n","type":"key","modifiers":[],"keyCode":45},{"rendered":"u","type":"key","modifiers":[],"keyCode":32},{"rendered":"x","type":"key","modifiers":[],"keyCode":7},{"rendered":" ","type":"boundary","modifiers":[],"keyCode":49}],"initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"split":"holdout","languages":["en","uk"],"expected":{"checkpoints":[{"text":"Linux ","afterEvent":6,"language":"en"}],"language":"en","text":"Linux ","allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.ABC"},"groupID":"holdout-grp-034","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"holdout-preserve-035","category":"proper-noun","events":[{"rendered":"V","type":"key","modifiers":["shift"],"keyCode":9},{"rendered":"i","type":"key","modifiers":[],"keyCode":34},{"rendered":"s","type":"key","modifiers":[],"keyCode":1},{"rendered":"u","type":"key","modifiers":[],"keyCode":32},{"rendered":"a","type":"key","modifiers":[],"keyCode":0},{"rendered":"l","type":"key","modifiers":[],"keyCode":37},{"rendered":"C","type":"key","modifiers":["shift"],"keyCode":8},{"rendered":"o","type":"key","modifiers":[],"keyCode":31},{"rendered":"d","type":"key","modifiers":[],"keyCode":2},{"rendered":"e","type":"key","modifiers":[],"keyCode":14},{"rendered":" ","type":"boundary","modifiers":[],"keyCode":49}],"initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"split":"holdout","languages":["en","uk"],"expected":{"checkpoints":[{"language":"en","afterEvent":11,"text":"VisualCode "}],"language":"en","text":"VisualCode ","allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.ABC"},"groupID":"holdout-grp-035","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"holdout-preserve-036","category":"proper-noun","events":[{"rendered":"i","type":"key","modifiers":[],"keyCode":34},{"rendered":"O","type":"key","modifiers":["shift"],"keyCode":31},{"rendered":"S","type":"key","modifiers":["shift"],"keyCode":1},{"rendered":" ","type":"boundary","modifiers":[],"keyCode":49}],"initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"split":"holdout","languages":["en","uk"],"expected":{"checkpoints":[{"text":"iOS ","language":"en","afterEvent":4}],"language":"en","text":"iOS ","allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.ABC"},"groupID":"holdout-grp-036","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"holdout-preserve-037","category":"code-token","events":[{"keyCode":35,"rendered":"p","type":"key","modifiers":[]},{"keyCode":0,"rendered":"a","type":"key","modifiers":[]},{"keyCode":1,"rendered":"s","type":"key","modifiers":[]},{"keyCode":8,"rendered":"c","type":"key","modifiers":[]},{"keyCode":0,"rendered":"a","type":"key","modifiers":[]},{"keyCode":37,"rendered":"l","type":"key","modifiers":[]},{"keyCode":8,"rendered":"C","type":"key","modifiers":["shift"]},{"keyCode":0,"rendered":"a","type":"key","modifiers":[]},{"keyCode":1,"rendered":"s","type":"key","modifiers":[]},{"keyCode":14,"rendered":"e","type":"key","modifiers":[]},{"keyCode":49,"rendered":" ","type":"boundary","modifiers":[]}],"initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"split":"holdout","languages":["en","uk"],"expected":{"language":"en","checkpoints":[{"text":"pascalCase ","language":"en","afterEvent":11}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.ABC","text":"pascalCase "},"groupID":"holdout-grp-037","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"holdout-preserve-038","category":"code-token","events":[{"keyCode":40,"rendered":"k","type":"key","modifiers":[]},{"keyCode":14,"rendered":"e","type":"key","modifiers":[]},{"keyCode":11,"rendered":"b","type":"key","modifiers":[]},{"keyCode":0,"rendered":"a","type":"key","modifiers":[]},{"keyCode":11,"rendered":"b","type":"key","modifiers":[]},{"keyCode":27,"rendered":"_","type":"key","modifiers":["shift"]},{"keyCode":8,"rendered":"c","type":"key","modifiers":[]},{"keyCode":0,"rendered":"a","type":"key","modifiers":[]},{"keyCode":1,"rendered":"s","type":"key","modifiers":[]},{"keyCode":14,"rendered":"e","type":"key","modifiers":[]},{"keyCode":49,"rendered":" ","type":"boundary","modifiers":[]}],"initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"split":"holdout","languages":["en","uk"],"expected":{"language":"en","checkpoints":[{"text":"kebab_case ","language":"en","afterEvent":11}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.ABC","text":"kebab_case "},"groupID":"holdout-grp-038","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"holdout-preserve-039","category":"code-token","events":[{"keyCode":8,"rendered":"C","type":"key","modifiers":["shift"]},{"keyCode":3,"rendered":"F","type":"key","modifiers":["shift"]},{"keyCode":15,"rendered":"R","type":"key","modifiers":["shift"]},{"keyCode":14,"rendered":"e","type":"key","modifiers":[]},{"keyCode":37,"rendered":"l","type":"key","modifiers":[]},{"keyCode":14,"rendered":"e","type":"key","modifiers":[]},{"keyCode":0,"rendered":"a","type":"key","modifiers":[]},{"keyCode":1,"rendered":"s","type":"key","modifiers":[]},{"keyCode":14,"rendered":"e","type":"key","modifiers":[]},{"keyCode":49,"rendered":" ","type":"boundary","modifiers":[]}],"initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"split":"holdout","languages":["en","uk"],"expected":{"language":"en","checkpoints":[{"language":"en","afterEvent":10,"text":"CFRelease "}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.ABC","text":"CFRelease "},"groupID":"holdout-grp-039","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"holdout-preserve-040","category":"code-token","events":[{"keyCode":2,"rendered":"d","type":"key","modifiers":[]},{"keyCode":14,"rendered":"e","type":"key","modifiers":[]},{"keyCode":34,"rendered":"i","type":"key","modifiers":[]},{"keyCode":45,"rendered":"n","type":"key","modifiers":[]},{"keyCode":34,"rendered":"i","type":"key","modifiers":[]},{"keyCode":17,"rendered":"t","type":"key","modifiers":[]},{"keyCode":25,"rendered":"(","type":"key","modifiers":["shift"]},{"keyCode":29,"rendered":")","type":"key","modifiers":["shift"]},{"keyCode":49,"rendered":" ","type":"boundary","modifiers":[]}],"initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"split":"holdout","languages":["en","uk"],"expected":{"checkpoints":[{"afterEvent":9,"language":"en","text":"deinit() "}],"sourceID":"com.apple.keylayout.ABC","allowedAutomaticActions":[],"language":"en","text":"deinit() "},"groupID":"holdout-grp-040","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"holdout-preserve-041","category":"path","events":[{"keyCode":44,"rendered":"\/","type":"key","modifiers":[]},{"keyCode":14,"rendered":"e","type":"key","modifiers":[]},{"keyCode":17,"rendered":"t","type":"key","modifiers":[]},{"keyCode":8,"rendered":"c","type":"key","modifiers":[]},{"keyCode":44,"rendered":"\/","type":"key","modifiers":[]},{"keyCode":4,"rendered":"h","type":"key","modifiers":[]},{"keyCode":31,"rendered":"o","type":"key","modifiers":[]},{"keyCode":1,"rendered":"s","type":"key","modifiers":[]},{"keyCode":17,"rendered":"t","type":"key","modifiers":[]},{"keyCode":1,"rendered":"s","type":"key","modifiers":[]},{"keyCode":49,"rendered":" ","type":"boundary","modifiers":[]}],"initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"split":"holdout","languages":["en","uk"],"expected":{"language":"en","checkpoints":[{"text":"\/etc\/hosts ","language":"en","afterEvent":11}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.ABC","text":"\/etc\/hosts "},"groupID":"holdout-grp-041","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"holdout-preserve-042","category":"filename","events":[{"keyCode":15,"rendered":"R","type":"key","modifiers":["shift"]},{"keyCode":14,"rendered":"E","type":"key","modifiers":["shift"]},{"keyCode":0,"rendered":"A","type":"key","modifiers":["shift"]},{"keyCode":2,"rendered":"D","type":"key","modifiers":["shift"]},{"keyCode":46,"rendered":"M","type":"key","modifiers":["shift"]},{"keyCode":14,"rendered":"E","type":"key","modifiers":["shift"]},{"keyCode":47,"rendered":".","type":"key","modifiers":[]},{"keyCode":46,"rendered":"m","type":"key","modifiers":[]},{"keyCode":2,"rendered":"d","type":"key","modifiers":[]},{"keyCode":49,"rendered":" ","type":"boundary","modifiers":[]}],"initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"split":"holdout","languages":["en","uk"],"expected":{"language":"en","checkpoints":[{"language":"en","afterEvent":10,"text":"README.md "}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.ABC","text":"README.md "},"groupID":"holdout-grp-042","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"holdout-preserve-043","category":"url","events":[{"keyCode":4,"rendered":"h","type":"key","modifiers":[]},{"keyCode":17,"rendered":"t","type":"key","modifiers":[]},{"keyCode":17,"rendered":"t","type":"key","modifiers":[]},{"keyCode":35,"rendered":"p","type":"key","modifiers":[]},{"keyCode":1,"rendered":"s","type":"key","modifiers":[]},{"keyCode":41,"rendered":":","type":"key","modifiers":["shift"]},{"keyCode":44,"rendered":"\/","type":"key","modifiers":[]},{"keyCode":44,"rendered":"\/","type":"key","modifiers":[]},{"keyCode":1,"rendered":"s","type":"key","modifiers":[]},{"keyCode":13,"rendered":"w","type":"key","modifiers":[]},{"keyCode":34,"rendered":"i","type":"key","modifiers":[]},{"keyCode":3,"rendered":"f","type":"key","modifiers":[]},{"keyCode":17,"rendered":"t","type":"key","modifiers":[]},{"keyCode":47,"rendered":".","type":"key","modifiers":[]},{"keyCode":31,"rendered":"o","type":"key","modifiers":[]},{"keyCode":15,"rendered":"r","type":"key","modifiers":[]},{"keyCode":5,"rendered":"g","type":"key","modifiers":[]},{"keyCode":49,"rendered":" ","type":"boundary","modifiers":[]}],"initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"split":"holdout","languages":["en","uk"],"expected":{"checkpoints":[{"text":"https:\/\/swift.org ","language":"en","afterEvent":18}],"sourceID":"com.apple.keylayout.ABC","allowedAutomaticActions":[],"language":"en","text":"https:\/\/swift.org "},"groupID":"holdout-grp-043","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"holdout-preserve-044","category":"email","events":[{"keyCode":34,"rendered":"i","type":"key","modifiers":[]},{"keyCode":45,"rendered":"n","type":"key","modifiers":[]},{"keyCode":3,"rendered":"f","type":"key","modifiers":[]},{"keyCode":31,"rendered":"o","type":"key","modifiers":[]},{"keyCode":19,"rendered":"@","type":"key","modifiers":["shift"]},{"keyCode":8,"rendered":"c","type":"key","modifiers":[]},{"keyCode":31,"rendered":"o","type":"key","modifiers":[]},{"keyCode":46,"rendered":"m","type":"key","modifiers":[]},{"keyCode":35,"rendered":"p","type":"key","modifiers":[]},{"keyCode":0,"rendered":"a","type":"key","modifiers":[]},{"keyCode":45,"rendered":"n","type":"key","modifiers":[]},{"keyCode":16,"rendered":"y","type":"key","modifiers":[]},{"keyCode":47,"rendered":".","type":"key","modifiers":[]},{"keyCode":8,"rendered":"c","type":"key","modifiers":[]},{"keyCode":31,"rendered":"o","type":"key","modifiers":[]},{"keyCode":46,"rendered":"m","type":"key","modifiers":[]},{"keyCode":49,"rendered":" ","type":"boundary","modifiers":[]}],"initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"split":"holdout","languages":["en","uk"],"expected":{"checkpoints":[{"language":"en","afterEvent":17,"text":"info@company.com "}],"sourceID":"com.apple.keylayout.ABC","allowedAutomaticActions":[],"language":"en","text":"info@company.com "},"groupID":"holdout-grp-044","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"holdout-preserve-045","category":"acronym","events":[{"keyCode":15,"rendered":"R","type":"key","modifiers":["shift"]},{"keyCode":14,"rendered":"E","type":"key","modifiers":["shift"]},{"keyCode":1,"rendered":"S","type":"key","modifiers":["shift"]},{"keyCode":17,"rendered":"T","type":"key","modifiers":["shift"]},{"keyCode":49,"rendered":" ","type":"boundary","modifiers":[]}],"initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"split":"holdout","languages":["en","uk"],"expected":{"checkpoints":[{"text":"REST ","language":"en","afterEvent":5}],"sourceID":"com.apple.keylayout.ABC","allowedAutomaticActions":[],"language":"en","text":"REST "},"groupID":"holdout-grp-045","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"holdout-preserve-046","category":"acronym","events":[{"type":"key","rendered":"H","modifiers":["shift"],"keyCode":4},{"type":"key","rendered":"T","modifiers":["shift"],"keyCode":17},{"type":"key","rendered":"M","modifiers":["shift"],"keyCode":46},{"type":"key","rendered":"L","modifiers":["shift"],"keyCode":37},{"type":"boundary","rendered":" ","modifiers":[],"keyCode":49}],"initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"split":"holdout","languages":["en","uk"],"expected":{"text":"HTML ","checkpoints":[{"language":"en","afterEvent":5,"text":"HTML "}],"language":"en","sourceID":"com.apple.keylayout.ABC","allowedAutomaticActions":[]},"groupID":"holdout-grp-046","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"holdout-preserve-047","category":"acronym","events":[{"keyCode":15,"rendered":"R","type":"key","modifiers":["shift"]},{"keyCode":0,"rendered":"A","type":"key","modifiers":["shift"]},{"keyCode":46,"rendered":"M","type":"key","modifiers":["shift"]},{"keyCode":49,"rendered":" ","type":"boundary","modifiers":[]}],"initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"split":"holdout","languages":["en","uk"],"expected":{"language":"en","sourceID":"com.apple.keylayout.ABC","text":"RAM ","allowedAutomaticActions":[],"checkpoints":[{"text":"RAM ","afterEvent":4,"language":"en"}]},"groupID":"holdout-grp-047","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"holdout-preserve-048","category":"contraction","events":[{"type":"key","modifiers":[],"keyCode":13,"rendered":"w"},{"type":"key","modifiers":[],"keyCode":31,"rendered":"o"},{"type":"key","modifiers":[],"keyCode":45,"rendered":"n"},{"type":"key","modifiers":[],"keyCode":39,"rendered":"'"},{"type":"key","modifiers":[],"keyCode":17,"rendered":"t"},{"type":"boundary","modifiers":[],"keyCode":49,"rendered":" "}],"initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"split":"holdout","languages":["en","uk"],"expected":{"checkpoints":[{"text":"won't ","language":"en","afterEvent":6}],"sourceID":"com.apple.keylayout.ABC","allowedAutomaticActions":[],"text":"won't ","language":"en"},"groupID":"holdout-grp-048","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"holdout-preserve-049","category":"contraction","events":[{"type":"key","modifiers":[],"keyCode":17,"rendered":"t"},{"type":"key","modifiers":[],"keyCode":4,"rendered":"h"},{"type":"key","modifiers":[],"keyCode":0,"rendered":"a"},{"type":"key","modifiers":[],"keyCode":17,"rendered":"t"},{"type":"key","modifiers":[],"keyCode":39,"rendered":"'"},{"type":"key","modifiers":[],"keyCode":1,"rendered":"s"},{"type":"boundary","modifiers":[],"keyCode":49,"rendered":" "}],"initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"split":"holdout","languages":["en","uk"],"expected":{"checkpoints":[{"afterEvent":7,"language":"en","text":"that's "}],"sourceID":"com.apple.keylayout.ABC","allowedAutomaticActions":[],"text":"that's ","language":"en"},"groupID":"holdout-grp-049","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"holdout-preserve-050","category":"punctuation","events":[{"type":"key","modifiers":[],"keyCode":1,"rendered":"s"},{"type":"key","modifiers":[],"keyCode":17,"rendered":"t"},{"type":"key","modifiers":[],"keyCode":0,"rendered":"a"},{"type":"key","modifiers":[],"keyCode":17,"rendered":"t"},{"type":"key","modifiers":[],"keyCode":32,"rendered":"u"},{"type":"key","modifiers":[],"keyCode":1,"rendered":"s"},{"type":"boundary","modifiers":[],"keyCode":49,"rendered":" "},{"type":"key","modifiers":[],"keyCode":24,"rendered":"="},{"type":"key","modifiers":[],"keyCode":24,"rendered":"="},{"type":"boundary","modifiers":[],"keyCode":49,"rendered":" "},{"type":"key","modifiers":[],"keyCode":29,"rendered":"0"},{"type":"key","modifiers":[],"keyCode":41,"rendered":";"},{"type":"boundary","modifiers":[],"keyCode":49,"rendered":" "}],"initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"split":"holdout","languages":["en","uk"],"expected":{"text":"status == 0; ","sourceID":"com.apple.keylayout.ABC","checkpoints":[{"text":"status == 0; ","afterEvent":13,"language":"en"}],"language":"en","allowedAutomaticActions":[]},"groupID":"holdout-grp-050","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"holdout-preserve-051","category":"natural-word","events":[{"type":"key","modifiers":[],"keyCode":3,"rendered":"а"},{"type":"key","modifiers":[],"keyCode":5,"rendered":"п"},{"type":"key","modifiers":[],"keyCode":17,"rendered":"е"},{"type":"key","modifiers":[],"keyCode":40,"rendered":"л"},{"type":"key","modifiers":[],"keyCode":46,"rendered":"ь"},{"type":"key","modifiers":[],"keyCode":8,"rendered":"с"},{"type":"key","modifiers":[],"keyCode":1,"rendered":"и"},{"type":"key","modifiers":[],"keyCode":16,"rendered":"н"},{"type":"boundary","modifiers":[],"keyCode":49,"rendered":" "}],"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"split":"holdout","languages":["en","uk"],"expected":{"checkpoints":[{"afterEvent":9,"text":"апельсин ","language":"uk"}],"sourceID":"com.apple.keylayout.Ukrainian","allowedAutomaticActions":[],"text":"апельсин ","language":"uk"},"groupID":"holdout-grp-051","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"holdout-preserve-052","category":"natural-word","events":[{"type":"key","modifiers":[],"keyCode":9,"rendered":"м"},{"type":"key","modifiers":[],"keyCode":38,"rendered":"о"},{"type":"key","modifiers":[],"keyCode":2,"rendered":"в"},{"type":"key","modifiers":[],"keyCode":3,"rendered":"а"},{"type":"boundary","modifiers":[],"keyCode":49,"rendered":" "}],"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"split":"holdout","languages":["en","uk"],"expected":{"checkpoints":[{"afterEvent":5,"text":"мова ","language":"uk"}],"sourceID":"com.apple.keylayout.Ukrainian","allowedAutomaticActions":[],"text":"мова ","language":"uk"},"groupID":"holdout-grp-052","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"holdout-preserve-053","category":"natural-word","events":[{"type":"key","modifiers":[],"keyCode":3,"rendered":"а"},{"type":"key","modifiers":[],"keyCode":4,"rendered":"р"},{"type":"key","modifiers":[],"keyCode":33,"rendered":"х"},{"type":"key","modifiers":[],"keyCode":11,"rendered":"і"},{"type":"key","modifiers":[],"keyCode":45,"rendered":"т"},{"type":"key","modifiers":[],"keyCode":17,"rendered":"е"},{"type":"key","modifiers":[],"keyCode":15,"rendered":"к"},{"type":"key","modifiers":[],"keyCode":45,"rendered":"т"},{"type":"key","modifiers":[],"keyCode":38,"rendered":"о"},{"type":"key","modifiers":[],"keyCode":4,"rendered":"р"},{"type":"boundary","modifiers":[],"keyCode":49,"rendered":" "}],"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"split":"holdout","languages":["en","uk"],"expected":{"language":"uk","checkpoints":[{"text":"архітектор ","afterEvent":11,"language":"uk"}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.Ukrainian","text":"архітектор "},"groupID":"holdout-grp-053","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"holdout-preserve-054","category":"natural-word","events":[{"type":"key","modifiers":[],"keyCode":15,"rendered":"к"},{"type":"key","modifiers":[],"keyCode":38,"rendered":"о"},{"type":"key","modifiers":[],"keyCode":16,"rendered":"н"},{"type":"key","modifiers":[],"keyCode":8,"rendered":"с"},{"type":"key","modifiers":[],"keyCode":38,"rendered":"о"},{"type":"key","modifiers":[],"keyCode":40,"rendered":"л"},{"type":"key","modifiers":[],"keyCode":46,"rendered":"ь"},{"type":"boundary","modifiers":[],"keyCode":49,"rendered":" "}],"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"split":"holdout","languages":["en","uk"],"expected":{"checkpoints":[{"afterEvent":8,"language":"uk","text":"консоль "}],"sourceID":"com.apple.keylayout.Ukrainian","allowedAutomaticActions":[],"text":"консоль ","language":"uk"},"groupID":"holdout-grp-054","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"holdout-preserve-055","category":"natural-word","events":[{"type":"key","modifiers":[],"keyCode":38,"rendered":"о"},{"type":"key","modifiers":[],"keyCode":16,"rendered":"н"},{"type":"key","modifiers":[],"keyCode":38,"rendered":"о"},{"type":"key","modifiers":[],"keyCode":2,"rendered":"в"},{"type":"key","modifiers":[],"keyCode":40,"rendered":"л"},{"type":"key","modifiers":[],"keyCode":17,"rendered":"е"},{"type":"key","modifiers":[],"keyCode":16,"rendered":"н"},{"type":"key","modifiers":[],"keyCode":16,"rendered":"н"},{"type":"key","modifiers":[],"keyCode":6,"rendered":"я"},{"type":"boundary","modifiers":[],"keyCode":49,"rendered":" "}],"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"split":"holdout","languages":["en","uk"],"expected":{"language":"uk","checkpoints":[{"language":"uk","text":"оновлення ","afterEvent":10}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.Ukrainian","text":"оновлення "},"groupID":"holdout-grp-055","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"holdout-preserve-056","category":"natural-word","events":[{"type":"key","modifiers":[],"keyCode":17,"rendered":"е"},{"type":"key","modifiers":[],"keyCode":15,"rendered":"к"},{"type":"key","modifiers":[],"keyCode":4,"rendered":"р"},{"type":"key","modifiers":[],"keyCode":3,"rendered":"а"},{"type":"key","modifiers":[],"keyCode":16,"rendered":"н"},{"type":"boundary","modifiers":[],"keyCode":49,"rendered":" "}],"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"split":"holdout","languages":["en","uk"],"expected":{"checkpoints":[{"afterEvent":6,"text":"екран ","language":"uk"}],"sourceID":"com.apple.keylayout.Ukrainian","allowedAutomaticActions":[],"text":"екран ","language":"uk"},"groupID":"holdout-grp-056","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"holdout-preserve-057","category":"short-word","events":[{"type":"key","modifiers":[],"keyCode":43,"rendered":"б"},{"type":"key","modifiers":[],"keyCode":38,"rendered":"о"},{"type":"boundary","modifiers":[],"keyCode":49,"rendered":" "}],"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"split":"holdout","languages":["en","uk"],"expected":{"checkpoints":[{"language":"uk","afterEvent":3,"text":"бо "}],"sourceID":"com.apple.keylayout.Ukrainian","allowedAutomaticActions":[],"text":"бо ","language":"uk"},"groupID":"holdout-grp-057","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"holdout-preserve-058","category":"short-word","events":[{"rendered":"ч","modifiers":[],"type":"key","keyCode":7},{"rendered":"и","modifiers":[],"type":"key","keyCode":1},{"rendered":" ","modifiers":[],"type":"boundary","keyCode":49}],"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"split":"holdout","languages":["en","uk"],"expected":{"sourceID":"com.apple.keylayout.Ukrainian","text":"чи ","allowedAutomaticActions":[],"language":"uk","checkpoints":[{"language":"uk","text":"чи ","afterEvent":3}]},"groupID":"holdout-grp-058","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"holdout-preserve-059","category":"short-word","events":[{"type":"key","keyCode":14,"rendered":"у","modifiers":[]},{"type":"boundary","keyCode":49,"rendered":" ","modifiers":[]}],"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"split":"holdout","languages":["en","uk"],"expected":{"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","checkpoints":[{"text":"у ","language":"uk","afterEvent":2}],"text":"у ","allowedAutomaticActions":[]},"groupID":"holdout-grp-059","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"holdout-preserve-060","category":"short-word","events":[{"modifiers":[],"type":"key","keyCode":2,"rendered":"в"},{"modifiers":[],"type":"boundary","keyCode":49,"rendered":" "}],"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"split":"holdout","languages":["en","uk"],"expected":{"language":"uk","checkpoints":[{"afterEvent":2,"text":"в ","language":"uk"}],"allowedAutomaticActions":[],"text":"в ","sourceID":"com.apple.keylayout.Ukrainian"},"groupID":"holdout-grp-060","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"holdout-preserve-061","category":"short-word","events":[{"rendered":"о","type":"key","keyCode":38,"modifiers":[]},{"rendered":"т","type":"key","keyCode":45,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"split":"holdout","languages":["en","uk"],"expected":{"text":"от ","language":"uk","allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.Ukrainian","checkpoints":[{"afterEvent":3,"text":"от ","language":"uk"}]},"groupID":"holdout-grp-061","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"holdout-preserve-062","category":"short-word","events":[{"rendered":"н","keyCode":16,"modifiers":[],"type":"key"},{"rendered":"у","keyCode":14,"modifiers":[],"type":"key"},{"rendered":" ","keyCode":49,"modifiers":[],"type":"boundary"}],"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"split":"holdout","languages":["en","uk"],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"language":"uk","afterEvent":3,"text":"ну "}],"text":"ну ","language":"uk","sourceID":"com.apple.keylayout.Ukrainian"},"groupID":"holdout-grp-062","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"holdout-preserve-063","category":"proper-noun","events":[{"rendered":"О","type":"key","keyCode":38,"modifiers":["shift"]},{"rendered":"д","type":"key","keyCode":37,"modifiers":[]},{"rendered":"е","type":"key","keyCode":17,"modifiers":[]},{"rendered":"с","type":"key","keyCode":8,"modifiers":[]},{"rendered":"а","type":"key","keyCode":3,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"split":"holdout","languages":["en","uk"],"expected":{"text":"Одеса ","sourceID":"com.apple.keylayout.Ukrainian","language":"uk","allowedAutomaticActions":[],"checkpoints":[{"text":"Одеса ","afterEvent":6,"language":"uk"}]},"groupID":"holdout-grp-063","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"holdout-preserve-064","category":"proper-noun","events":[{"modifiers":["shift"],"type":"key","keyCode":0,"rendered":"Ф"},{"modifiers":[],"type":"key","keyCode":4,"rendered":"р"},{"modifiers":[],"type":"key","keyCode":3,"rendered":"а"},{"modifiers":[],"type":"key","keyCode":16,"rendered":"н"},{"modifiers":[],"type":"key","keyCode":15,"rendered":"к"},{"modifiers":[],"type":"key","keyCode":38,"rendered":"о"},{"modifiers":[],"type":"boundary","keyCode":49,"rendered":" "}],"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"split":"holdout","languages":["en","uk"],"expected":{"sourceID":"com.apple.keylayout.Ukrainian","language":"uk","text":"Франко ","allowedAutomaticActions":[],"checkpoints":[{"text":"Франко ","language":"uk","afterEvent":7}]},"groupID":"holdout-grp-064","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"holdout-preserve-065","category":"proper-noun","events":[{"modifiers":["shift"],"type":"key","keyCode":40,"rendered":"Л"},{"modifiers":[],"type":"key","keyCode":17,"rendered":"е"},{"modifiers":[],"type":"key","keyCode":8,"rendered":"с"},{"modifiers":[],"type":"key","keyCode":6,"rendered":"я"},{"modifiers":[],"type":"boundary","keyCode":49,"rendered":" "}],"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"split":"holdout","languages":["en","uk"],"expected":{"sourceID":"com.apple.keylayout.Ukrainian","language":"uk","text":"Леся ","allowedAutomaticActions":[],"checkpoints":[{"afterEvent":5,"language":"uk","text":"Леся "}]},"groupID":"holdout-grp-065","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"holdout-preserve-066","category":"proper-noun","events":[{"rendered":"Х","type":"key","keyCode":33,"modifiers":["shift"]},{"rendered":"а","type":"key","keyCode":3,"modifiers":[]},{"rendered":"р","type":"key","keyCode":4,"modifiers":[]},{"rendered":"к","type":"key","keyCode":15,"modifiers":[]},{"rendered":"і","type":"key","keyCode":11,"modifiers":[]},{"rendered":"в","type":"key","keyCode":2,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"split":"holdout","languages":["en","uk"],"expected":{"text":"Харків ","language":"uk","sourceID":"com.apple.keylayout.Ukrainian","allowedAutomaticActions":[],"checkpoints":[{"text":"Харків ","language":"uk","afterEvent":7}]},"groupID":"holdout-grp-066","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"holdout-preserve-067","category":"apostrophe","events":[{"rendered":"м","type":"key","keyCode":9,"modifiers":[]},{"rendered":"'","type":"key","keyCode":50,"modifiers":[]},{"rendered":"я","type":"key","keyCode":6,"modifiers":[]},{"rendered":"к","type":"key","keyCode":15,"modifiers":[]},{"rendered":"и","type":"key","keyCode":1,"modifiers":[]},{"rendered":"й","type":"key","keyCode":12,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"split":"holdout","languages":["en","uk"],"expected":{"text":"м'який ","language":"uk","sourceID":"com.apple.keylayout.Ukrainian","allowedAutomaticActions":[],"checkpoints":[{"text":"м'який ","language":"uk","afterEvent":7}]},"groupID":"holdout-grp-067","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"holdout-preserve-068","category":"apostrophe","events":[{"rendered":"п","type":"key","keyCode":5,"modifiers":[]},{"rendered":"'","type":"key","keyCode":50,"modifiers":[]},{"rendered":"я","type":"key","keyCode":6,"modifiers":[]},{"rendered":"т","type":"key","keyCode":45,"modifiers":[]},{"rendered":"ь","type":"key","keyCode":46,"modifiers":[]},{"rendered":" ","type":"boundary","keyCode":49,"modifiers":[]}],"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"split":"holdout","languages":["en","uk"],"expected":{"text":"п'ять ","language":"uk","sourceID":"com.apple.keylayout.Ukrainian","allowedAutomaticActions":[],"checkpoints":[{"language":"uk","text":"п'ять ","afterEvent":6}]},"groupID":"holdout-grp-068","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"holdout-preserve-069","category":"apostrophe","events":[{"modifiers":[],"keyCode":8,"rendered":"с","type":"key"},{"modifiers":[],"keyCode":38,"rendered":"о","type":"key"},{"modifiers":[],"keyCode":40,"rendered":"л","type":"key"},{"modifiers":[],"keyCode":38,"rendered":"о","type":"key"},{"modifiers":[],"keyCode":2,"rendered":"в","type":"key"},{"modifiers":[],"keyCode":50,"rendered":"'","type":"key"},{"modifiers":[],"keyCode":30,"rendered":"ї","type":"key"},{"modifiers":[],"keyCode":16,"rendered":"н","type":"key"},{"modifiers":[],"keyCode":1,"rendered":"и","type":"key"},{"modifiers":[],"keyCode":12,"rendered":"й","type":"key"},{"modifiers":[],"keyCode":49,"rendered":" ","type":"boundary"}],"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"split":"holdout","languages":["en","uk"],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"language":"uk","afterEvent":11,"text":"солов'їний "}],"text":"солов'їний ","language":"uk","sourceID":"com.apple.keylayout.Ukrainian"},"groupID":"holdout-grp-069","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"holdout-preserve-070","category":"apostrophe","events":[{"modifiers":[],"keyCode":5,"rendered":"п","type":"key"},{"modifiers":[],"keyCode":11,"rendered":"і","type":"key"},{"modifiers":[],"keyCode":37,"rendered":"д","type":"key"},{"modifiers":[],"keyCode":50,"rendered":"'","type":"key"},{"modifiers":[],"keyCode":30,"rendered":"ї","type":"key"},{"modifiers":[],"keyCode":35,"rendered":"з","type":"key"},{"modifiers":[],"keyCode":37,"rendered":"д","type":"key"},{"modifiers":[],"keyCode":49,"rendered":" ","type":"boundary"}],"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"split":"holdout","languages":["en","uk"],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"text":"під'їзд ","language":"uk","afterEvent":8}],"text":"під'їзд ","language":"uk","sourceID":"com.apple.keylayout.Ukrainian"},"groupID":"holdout-grp-070","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"holdout-preserve-071","category":"acronym","events":[{"modifiers":["shift"],"keyCode":9,"rendered":"М","type":"key"},{"modifiers":["shift"],"keyCode":2,"rendered":"В","type":"key"},{"modifiers":["shift"],"keyCode":0,"rendered":"Ф","type":"key"},{"modifiers":[],"keyCode":49,"rendered":" ","type":"boundary"}],"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"split":"holdout","languages":["en","uk"],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":4,"language":"uk","text":"МВФ "}],"text":"МВФ ","language":"uk","sourceID":"com.apple.keylayout.Ukrainian"},"groupID":"holdout-grp-071","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"holdout-preserve-072","category":"acronym","events":[{"keyCode":47,"type":"key","rendered":"Ю","modifiers":["shift"]},{"keyCode":16,"type":"key","rendered":"Н","modifiers":["shift"]},{"keyCode":17,"type":"key","rendered":"Е","modifiers":["shift"]},{"keyCode":8,"type":"key","rendered":"С","modifiers":["shift"]},{"keyCode":15,"type":"key","rendered":"К","modifiers":["shift"]},{"keyCode":38,"type":"key","rendered":"О","modifiers":["shift"]},{"keyCode":49,"type":"boundary","rendered":" ","modifiers":[]}],"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"split":"holdout","languages":["en","uk"],"expected":{"sourceID":"com.apple.keylayout.Ukrainian","text":"ЮНЕСКО ","language":"uk","allowedAutomaticActions":[],"checkpoints":[{"text":"ЮНЕСКО ","language":"uk","afterEvent":7}]},"groupID":"holdout-grp-072","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"holdout-preserve-073","category":"acronym","events":[{"keyCode":39,"type":"key","rendered":"Є","modifiers":["shift"]},{"keyCode":8,"type":"key","rendered":"С","modifiers":["shift"]},{"keyCode":49,"type":"boundary","rendered":" ","modifiers":[]}],"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"split":"holdout","languages":["en","uk"],"expected":{"sourceID":"com.apple.keylayout.Ukrainian","text":"ЄС ","language":"uk","allowedAutomaticActions":[],"checkpoints":[{"afterEvent":3,"language":"uk","text":"ЄС "}]},"groupID":"holdout-grp-073","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"holdout-preserve-074","category":"acronym","events":[{"modifiers":["shift"],"rendered":"З","type":"key","keyCode":35},{"modifiers":["shift"],"rendered":"С","type":"key","keyCode":8},{"modifiers":["shift"],"rendered":"У","type":"key","keyCode":14},{"modifiers":[],"rendered":" ","type":"boundary","keyCode":49}],"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"split":"holdout","languages":["en","uk"],"expected":{"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","checkpoints":[{"afterEvent":4,"text":"ЗСУ ","language":"uk"}],"allowedAutomaticActions":[],"text":"ЗСУ "},"groupID":"holdout-grp-074","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"holdout-preserve-075","category":"punctuation","events":[{"keyCode":7,"type":"key","modifiers":[],"rendered":"ч"},{"keyCode":14,"type":"key","modifiers":[],"rendered":"у"},{"keyCode":37,"type":"key","modifiers":[],"rendered":"д"},{"keyCode":38,"type":"key","modifiers":[],"rendered":"о"},{"keyCode":2,"type":"key","modifiers":[],"rendered":"в"},{"keyCode":38,"type":"key","modifiers":[],"rendered":"о"},{"keyCode":18,"type":"key","modifiers":["shift"],"rendered":"!"},{"keyCode":49,"type":"boundary","modifiers":[],"rendered":" "}],"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"split":"holdout","languages":["en","uk"],"expected":{"language":"uk","allowedAutomaticActions":[],"checkpoints":[{"text":"чудово! ","afterEvent":8,"language":"uk"}],"text":"чудово! ","sourceID":"com.apple.keylayout.Ukrainian"},"groupID":"holdout-grp-075","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"holdout-preserve-076","category":"punctuation","events":[{"keyCode":25,"type":"key","modifiers":["shift"],"rendered":"("},{"keyCode":16,"type":"key","modifiers":[],"rendered":"н"},{"keyCode":11,"type":"key","modifiers":[],"rendered":"і"},{"keyCode":29,"type":"key","modifiers":["shift"],"rendered":")"},{"keyCode":49,"type":"boundary","modifiers":[],"rendered":" "}],"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"split":"holdout","languages":["en","uk"],"expected":{"language":"uk","allowedAutomaticActions":[],"checkpoints":[{"afterEvent":5,"text":"(ні) ","language":"uk"}],"text":"(ні) ","sourceID":"com.apple.keylayout.Ukrainian"},"groupID":"holdout-grp-076","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"holdout-preserve-077","category":"punctuation","events":[{"keyCode":2,"type":"key","modifiers":[],"rendered":"в"},{"keyCode":1,"type":"key","modifiers":[],"rendered":"и"},{"keyCode":8,"type":"key","modifiers":[],"rendered":"с"},{"keyCode":16,"type":"key","modifiers":[],"rendered":"н"},{"keyCode":38,"type":"key","modifiers":[],"rendered":"о"},{"keyCode":2,"type":"key","modifiers":[],"rendered":"в"},{"keyCode":38,"type":"key","modifiers":[],"rendered":"о"},{"keyCode":15,"type":"key","modifiers":[],"rendered":"к"},{"keyCode":23,"type":"key","modifiers":["shift"],"rendered":":"},{"keyCode":49,"type":"boundary","modifiers":[],"rendered":" "}],"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"split":"holdout","languages":["en","uk"],"expected":{"language":"uk","checkpoints":[{"text":"висновок: ","afterEvent":10,"language":"uk"}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.Ukrainian","text":"висновок: "},"groupID":"holdout-grp-077","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"holdout-preserve-078","category":"symbol","events":[{"keyCode":23,"type":"key","modifiers":[],"rendered":"5"},{"keyCode":29,"type":"key","modifiers":[],"rendered":"0"},{"keyCode":21,"type":"key","modifiers":["shift"],"rendered":"%"},{"keyCode":49,"type":"boundary","modifiers":[],"rendered":" "}],"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"split":"holdout","languages":["en","uk"],"expected":{"language":"uk","allowedAutomaticActions":[],"checkpoints":[{"text":"50% ","afterEvent":4,"language":"uk"}],"text":"50% ","sourceID":"com.apple.keylayout.Ukrainian"},"groupID":"holdout-grp-078","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"holdout-preserve-079","category":"phrase","events":[{"keyCode":37,"modifiers":[],"rendered":"д","type":"key"},{"keyCode":38,"modifiers":[],"rendered":"о","type":"key"},{"keyCode":43,"modifiers":[],"rendered":"б","type":"key"},{"keyCode":4,"modifiers":[],"rendered":"р","type":"key"},{"keyCode":1,"modifiers":[],"rendered":"и","type":"key"},{"keyCode":12,"modifiers":[],"rendered":"й","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"},{"keyCode":2,"modifiers":[],"rendered":"в","type":"key"},{"keyCode":17,"modifiers":[],"rendered":"е","type":"key"},{"keyCode":7,"modifiers":[],"rendered":"ч","type":"key"},{"keyCode":11,"modifiers":[],"rendered":"і","type":"key"},{"keyCode":4,"modifiers":[],"rendered":"р","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"split":"holdout","languages":["en","uk"],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":13,"text":"добрий вечір ","language":"uk"}],"language":"uk","text":"добрий вечір ","sourceID":"com.apple.keylayout.Ukrainian"},"groupID":"holdout-grp-079","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"holdout-preserve-080","category":"phrase","events":[{"keyCode":16,"modifiers":[],"rendered":"н","type":"key"},{"keyCode":3,"modifiers":[],"rendered":"а","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"},{"keyCode":2,"modifiers":[],"rendered":"в","type":"key"},{"keyCode":8,"modifiers":[],"rendered":"с","type":"key"},{"keyCode":17,"modifiers":[],"rendered":"е","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"},{"keyCode":37,"modifiers":[],"rendered":"д","type":"key"},{"keyCode":38,"modifiers":[],"rendered":"о","type":"key"},{"keyCode":43,"modifiers":[],"rendered":"б","type":"key"},{"keyCode":4,"modifiers":[],"rendered":"р","type":"key"},{"keyCode":17,"modifiers":[],"rendered":"е","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"split":"holdout","languages":["en","uk"],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"language":"uk","text":"на все добре ","afterEvent":13}],"language":"uk","text":"на все добре ","sourceID":"com.apple.keylayout.Ukrainian"},"groupID":"holdout-grp-080","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"preserve"} +{"schemaVersion":1,"id":"holdout-abstain-081","category":"ambiguous-short","events":[{"keyCode":0,"modifiers":[],"rendered":"a","type":"key"},{"keyCode":46,"modifiers":[],"rendered":"m","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"split":"holdout","languages":["en","uk"],"expected":{"text":"am ","language":"en","allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.ABC","checkpoints":[{"afterEvent":3,"text":"am ","language":"en"}]},"groupID":"holdout-grp-081","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"abstain"} +{"schemaVersion":1,"id":"holdout-abstain-082","category":"ambiguous-short","events":[{"keyCode":34,"type":"key","modifiers":[],"rendered":"i"},{"keyCode":1,"type":"key","modifiers":[],"rendered":"s"},{"keyCode":49,"type":"boundary","modifiers":[],"rendered":" "}],"initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"split":"holdout","languages":["en","uk"],"expected":{"allowedAutomaticActions":[],"language":"en","checkpoints":[{"afterEvent":3,"text":"is ","language":"en"}],"text":"is ","sourceID":"com.apple.keylayout.ABC"},"groupID":"holdout-grp-082","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"abstain"} +{"schemaVersion":1,"id":"holdout-abstain-083","category":"ambiguous-short","events":[{"keyCode":32,"modifiers":[],"rendered":"u","type":"key"},{"keyCode":1,"modifiers":[],"rendered":"s","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"split":"holdout","languages":["en","uk"],"expected":{"checkpoints":[{"text":"us ","language":"en","afterEvent":3}],"allowedAutomaticActions":[],"text":"us ","language":"en","sourceID":"com.apple.keylayout.ABC"},"groupID":"holdout-grp-083","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"abstain"} +{"schemaVersion":1,"id":"holdout-abstain-084","category":"ambiguous-short","events":[{"rendered":"b","modifiers":[],"type":"key","keyCode":11},{"rendered":"e","modifiers":[],"type":"key","keyCode":14},{"rendered":" ","modifiers":[],"type":"boundary","keyCode":49}],"initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"split":"holdout","languages":["en","uk"],"expected":{"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.ABC","text":"be ","language":"en","checkpoints":[{"text":"be ","language":"en","afterEvent":3}]},"groupID":"holdout-grp-084","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"abstain"} +{"schemaVersion":1,"id":"holdout-abstain-085","category":"ambiguous-short","events":[{"rendered":"h","keyCode":4,"type":"key","modifiers":[]},{"rendered":"e","keyCode":14,"type":"key","modifiers":[]},{"rendered":" ","keyCode":49,"type":"boundary","modifiers":[]}],"initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"split":"holdout","languages":["en","uk"],"expected":{"checkpoints":[{"language":"en","afterEvent":3,"text":"he "}],"text":"he ","sourceID":"com.apple.keylayout.ABC","language":"en","allowedAutomaticActions":[]},"groupID":"holdout-grp-085","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"abstain"} +{"schemaVersion":1,"id":"holdout-abstain-086","category":"gibberish","events":[{"rendered":"z","modifiers":[],"type":"key","keyCode":6},{"rendered":"x","modifiers":[],"type":"key","keyCode":7},{"rendered":"c","modifiers":[],"type":"key","keyCode":8},{"rendered":"v","modifiers":[],"type":"key","keyCode":9},{"rendered":"b","modifiers":[],"type":"key","keyCode":11},{"rendered":"n","modifiers":[],"type":"key","keyCode":45},{"rendered":"m","modifiers":[],"type":"key","keyCode":46},{"rendered":" ","modifiers":[],"type":"boundary","keyCode":49}],"initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"split":"holdout","languages":["en","uk"],"expected":{"text":"zxcvbnm ","language":"en","sourceID":"com.apple.keylayout.ABC","checkpoints":[{"text":"zxcvbnm ","language":"en","afterEvent":8}],"allowedAutomaticActions":[]},"groupID":"holdout-grp-086","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"abstain"} +{"schemaVersion":1,"id":"holdout-abstain-087","category":"gibberish","events":[{"rendered":"l","modifiers":[],"type":"key","keyCode":37},{"rendered":"k","modifiers":[],"type":"key","keyCode":40},{"rendered":"j","modifiers":[],"type":"key","keyCode":38},{"rendered":"h","modifiers":[],"type":"key","keyCode":4},{"rendered":"g","modifiers":[],"type":"key","keyCode":5},{"rendered":"f","modifiers":[],"type":"key","keyCode":3},{"rendered":"d","modifiers":[],"type":"key","keyCode":2},{"rendered":"s","modifiers":[],"type":"key","keyCode":1},{"rendered":"a","modifiers":[],"type":"key","keyCode":0},{"rendered":" ","modifiers":[],"type":"boundary","keyCode":49}],"initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"split":"holdout","languages":["en","uk"],"expected":{"language":"en","checkpoints":[{"afterEvent":10,"text":"lkjhgfdsa ","language":"en"}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.ABC","text":"lkjhgfdsa "},"groupID":"holdout-grp-087","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"abstain"} +{"schemaVersion":1,"id":"holdout-abstain-088","category":"isolated-char","events":[{"rendered":"b","modifiers":[],"type":"key","keyCode":11},{"rendered":" ","modifiers":[],"type":"boundary","keyCode":49}],"initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"split":"holdout","languages":["en","uk"],"expected":{"text":"b ","language":"en","sourceID":"com.apple.keylayout.ABC","checkpoints":[{"text":"b ","afterEvent":2,"language":"en"}],"allowedAutomaticActions":[]},"groupID":"holdout-grp-088","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"abstain"} +{"schemaVersion":1,"id":"holdout-abstain-089","category":"isolated-char","events":[{"keyCode":16,"type":"key","modifiers":[],"rendered":"y"},{"keyCode":49,"type":"boundary","modifiers":[],"rendered":" "}],"initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"split":"holdout","languages":["en","uk"],"expected":{"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.ABC","checkpoints":[{"language":"en","text":"y ","afterEvent":2}],"text":"y ","language":"en"},"groupID":"holdout-grp-089","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"abstain"} +{"schemaVersion":1,"id":"holdout-abstain-090","category":"isolated-symbol","events":[{"type":"key","keyCode":43,"modifiers":[],"rendered":","},{"type":"boundary","keyCode":49,"modifiers":[],"rendered":" "}],"initial":{"language":"en","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.ABC","text":""},"split":"holdout","languages":["en","uk"],"expected":{"checkpoints":[{"afterEvent":2,"language":"en","text":", "}],"sourceID":"com.apple.keylayout.ABC","language":"en","allowedAutomaticActions":[],"text":", "},"groupID":"holdout-grp-090","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"abstain"} +{"schemaVersion":1,"id":"holdout-abstain-091","category":"ambiguous-short","events":[{"modifiers":[],"rendered":"б","keyCode":43,"type":"key"},{"modifiers":[],"rendered":"и","keyCode":1,"type":"key"},{"modifiers":[],"rendered":" ","keyCode":49,"type":"boundary"}],"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"split":"holdout","languages":["en","uk"],"expected":{"checkpoints":[{"afterEvent":3,"language":"uk","text":"би "}],"sourceID":"com.apple.keylayout.Ukrainian","allowedAutomaticActions":[],"language":"uk","text":"би "},"groupID":"holdout-grp-091","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"abstain"} +{"schemaVersion":1,"id":"holdout-abstain-092","category":"ambiguous-short","events":[{"keyCode":41,"modifiers":[],"rendered":"ж","type":"key"},{"keyCode":17,"modifiers":[],"rendered":"е","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"split":"holdout","languages":["en","uk"],"expected":{"language":"uk","text":"же ","checkpoints":[{"afterEvent":3,"text":"же ","language":"uk"}],"allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.Ukrainian"},"groupID":"holdout-grp-092","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"abstain"} +{"schemaVersion":1,"id":"holdout-abstain-093","category":"ambiguous-short","events":[{"keyCode":38,"rendered":"о","modifiers":[],"type":"key"},{"keyCode":45,"rendered":"т","modifiers":[],"type":"key"},{"keyCode":49,"rendered":" ","modifiers":[],"type":"boundary"}],"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"split":"holdout","languages":["en","uk"],"expected":{"text":"от ","sourceID":"com.apple.keylayout.Ukrainian","language":"uk","allowedAutomaticActions":[],"checkpoints":[{"language":"uk","text":"от ","afterEvent":3}]},"groupID":"holdout-grp-093","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"abstain"} +{"schemaVersion":1,"id":"holdout-abstain-094","category":"ambiguous-short","events":[{"type":"key","keyCode":16,"rendered":"н","modifiers":[]},{"type":"key","keyCode":14,"rendered":"у","modifiers":[]},{"type":"boundary","keyCode":49,"rendered":" ","modifiers":[]}],"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"split":"holdout","languages":["en","uk"],"expected":{"sourceID":"com.apple.keylayout.Ukrainian","text":"ну ","language":"uk","allowedAutomaticActions":[],"checkpoints":[{"afterEvent":3,"language":"uk","text":"ну "}]},"groupID":"holdout-grp-094","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"abstain"} +{"schemaVersion":1,"id":"holdout-abstain-095","category":"ambiguous-short","events":[{"modifiers":[],"rendered":"щ","type":"key","keyCode":31},{"modifiers":[],"rendered":"о","type":"key","keyCode":38},{"modifiers":[],"rendered":" ","type":"boundary","keyCode":49}],"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"split":"holdout","languages":["en","uk"],"expected":{"checkpoints":[{"language":"uk","afterEvent":3,"text":"що "}],"text":"що ","sourceID":"com.apple.keylayout.Ukrainian","allowedAutomaticActions":[],"language":"uk"},"groupID":"holdout-grp-095","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"abstain"} +{"schemaVersion":1,"id":"holdout-abstain-096","category":"gibberish","events":[{"type":"key","modifiers":[],"rendered":"я","keyCode":6},{"type":"key","modifiers":[],"rendered":"ч","keyCode":7},{"type":"key","modifiers":[],"rendered":"с","keyCode":8},{"type":"key","modifiers":[],"rendered":"м","keyCode":9},{"type":"key","modifiers":[],"rendered":"и","keyCode":1},{"type":"key","modifiers":[],"rendered":"т","keyCode":45},{"type":"key","modifiers":[],"rendered":"ь","keyCode":46},{"type":"key","modifiers":[],"rendered":"б","keyCode":43},{"type":"boundary","modifiers":[],"rendered":" ","keyCode":49}],"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"split":"holdout","languages":["en","uk"],"expected":{"sourceID":"com.apple.keylayout.Ukrainian","language":"uk","checkpoints":[{"language":"uk","afterEvent":9,"text":"ячсмитьб "}],"allowedAutomaticActions":[],"text":"ячсмитьб "},"groupID":"holdout-grp-096","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"abstain"} +{"schemaVersion":1,"id":"holdout-abstain-097","category":"gibberish","events":[{"type":"key","modifiers":[],"rendered":"п","keyCode":5},{"type":"key","modifiers":[],"rendered":"о","keyCode":38},{"type":"key","modifiers":[],"rendered":"л","keyCode":40},{"type":"key","modifiers":[],"rendered":"д","keyCode":37},{"type":"key","modifiers":[],"rendered":"з","keyCode":35},{"type":"key","modifiers":[],"rendered":"я","keyCode":6},{"type":"key","modifiers":[],"rendered":"ч","keyCode":7},{"type":"key","modifiers":[],"rendered":"с","keyCode":8},{"type":"boundary","modifiers":[],"rendered":" ","keyCode":49}],"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"split":"holdout","languages":["en","uk"],"expected":{"sourceID":"com.apple.keylayout.Ukrainian","language":"uk","checkpoints":[{"language":"uk","afterEvent":9,"text":"полдзячс "}],"allowedAutomaticActions":[],"text":"полдзячс "},"groupID":"holdout-grp-097","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"abstain"} +{"schemaVersion":1,"id":"holdout-abstain-098","category":"isolated-char","events":[{"type":"key","modifiers":[],"rendered":"і","keyCode":11},{"type":"boundary","modifiers":[],"rendered":" ","keyCode":49}],"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"split":"holdout","languages":["en","uk"],"expected":{"sourceID":"com.apple.keylayout.Ukrainian","language":"uk","checkpoints":[{"afterEvent":2,"text":"і ","language":"uk"}],"allowedAutomaticActions":[],"text":"і "},"groupID":"holdout-grp-098","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"abstain"} +{"schemaVersion":1,"id":"holdout-abstain-099","category":"isolated-symbol","events":[{"keyCode":22,"modifiers":["shift"],"type":"key","rendered":","},{"keyCode":49,"modifiers":[],"type":"boundary","rendered":" "}],"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"split":"holdout","languages":["en","uk"],"expected":{"checkpoints":[{"language":"uk","text":", ","afterEvent":2}],"sourceID":"com.apple.keylayout.Ukrainian","text":", ","language":"uk","allowedAutomaticActions":[]},"groupID":"holdout-grp-099","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"abstain"} +{"schemaVersion":1,"id":"holdout-abstain-100","category":"symbol","events":[{"rendered":"=","type":"key","modifiers":[],"keyCode":24},{"rendered":"=","type":"key","modifiers":[],"keyCode":24},{"rendered":"=","type":"key","modifiers":[],"keyCode":24},{"rendered":" ","type":"boundary","modifiers":[],"keyCode":49}],"initial":{"language":"uk","preferredCyrillic":"uk","keyboardType":91,"caretUTF16":0,"sourceID":"com.apple.keylayout.Ukrainian","text":""},"split":"holdout","languages":["en","uk"],"expected":{"language":"uk","text":"=== ","allowedAutomaticActions":[],"sourceID":"com.apple.keylayout.Ukrainian","checkpoints":[{"text":"=== ","language":"uk","afterEvent":4}]},"groupID":"holdout-grp-100","provenance":{"review":"pending-human","kind":"synthetic","seed":0,"generator":"manual-v1"},"label":"abstain"} diff --git a/Benchmarks/layout-corpus/v1/manifest.json b/Benchmarks/layout-corpus/v1/manifest.json new file mode 100644 index 0000000..7465c60 --- /dev/null +++ b/Benchmarks/layout-corpus/v1/manifest.json @@ -0,0 +1,103 @@ +{ + "corpusVersion" : "v1", + "created" : "2026-10-19", + "files" : { + "layout-corpus\/v1\/README.md" : { + "sha256" : "aee9eaa02e7fd87cee28a2aa842318186a631b0c8800db5596aa1a6532b40d77" + }, + "layout-corpus\/v1\/development.jsonl" : { + "sha256" : "2b54ac2a26bf524ed412dcacbd1458f4115413fc886b624ae93b79bb524c4b16", + "trialsCount" : 100 + }, + "layout-corpus\/v1\/holdout.jsonl" : { + "sha256" : "ad9672a0544c2338a7546724fad423064dc2029999cec8fa3c9402405b8ddd0c", + "trialsCount" : 100 + }, + "layout-tables\/v1\/com.apple.keylayout.ABC@37.json" : { + "sha256" : "cf0bed17113a8f0d96738efeb7ca33449314c2b02e930ccc3b1021cbfdd57f46" + }, + "layout-tables\/v1\/com.apple.keylayout.ABC@91.json" : { + "sha256" : "4a135908d4e08e20bdab9e17c39f7731765d039b50c0819b88c847a1d8ecf5aa" + }, + "layout-tables\/v1\/com.apple.keylayout.British@37.json" : { + "sha256" : "62657c4714b1ba0567282bf4d2beb6f247ab12643acf8dd66e43fc5e0fb07e7b" + }, + "layout-tables\/v1\/com.apple.keylayout.British@91.json" : { + "sha256" : "b29622001b91eba73b291d9c8595f57394f3a64ddd4d700b1ff3a884ea3d82e2" + }, + "layout-tables\/v1\/com.apple.keylayout.Colemak@37.json" : { + "sha256" : "4faf9db8a1732999fc46357c7791fd6a29ec298b645a810f87b1c281a4c3d83b" + }, + "layout-tables\/v1\/com.apple.keylayout.Colemak@91.json" : { + "sha256" : "e5ab956b491545b4adc68f82801b67e45a5df016758199f9312a122948174197" + }, + "layout-tables\/v1\/com.apple.keylayout.Dvorak@37.json" : { + "sha256" : "6b31a65f097b6543cec92f2b87b4e6afc5177ba853de941ab0a62da389d18d0f" + }, + "layout-tables\/v1\/com.apple.keylayout.Dvorak@91.json" : { + "sha256" : "61befd74221873723822cdafa27f3300077393a55c4a0e4f24d3ca099fd7024f" + }, + "layout-tables\/v1\/com.apple.keylayout.Russian-Phonetic@37.json" : { + "sha256" : "1c2d031cbb0ef13b89b0e586d8acf64fb8c9d751364c0267b6ed00fbad94a00a" + }, + "layout-tables\/v1\/com.apple.keylayout.Russian-Phonetic@91.json" : { + "sha256" : "09e30609b90e9aab3184e99c69d689eda4073652dd69a085bde28406d26a593d" + }, + "layout-tables\/v1\/com.apple.keylayout.Russian@37.json" : { + "sha256" : "249b33745e1b4c8fe058dbfd55cbec18083d93089fe0bb6c9484dabc4e3bc30a" + }, + "layout-tables\/v1\/com.apple.keylayout.Russian@91.json" : { + "sha256" : "ccb648a0c68868e58e51388594d1632beaccd93f9758504a95706ce4c00e3bad" + }, + "layout-tables\/v1\/com.apple.keylayout.RussianWin@37.json" : { + "sha256" : "29898c681949ffe8d31b0a157e85f453d2b2f2c4437927e8c25d87a532d3060b" + }, + "layout-tables\/v1\/com.apple.keylayout.RussianWin@91.json" : { + "sha256" : "8585e4a27ee4cf81c204656e5c472aa5fd99c8208e507e1067579a576f5eec1e" + }, + "layout-tables\/v1\/com.apple.keylayout.US@37.json" : { + "sha256" : "5066da5b841c2ca0288defff297e9a28057d4c473be59a1bc77700302b23ce0b" + }, + "layout-tables\/v1\/com.apple.keylayout.US@91.json" : { + "sha256" : "b506d124fc61cc7cf54a4f88d13b07f3c8620197f50f62f9934585be6c069676" + }, + "layout-tables\/v1\/com.apple.keylayout.Ukrainian-PC@37.json" : { + "sha256" : "c5ab3998e352694b9b7662235e1aae0a2b74afee18447f72d8028804e49f45f0" + }, + "layout-tables\/v1\/com.apple.keylayout.Ukrainian-PC@91.json" : { + "sha256" : "65c6e763f08b5976a165cb26b57d9560aadeddfaf5c80b44bcf2e967fd25b82e" + }, + "layout-tables\/v1\/com.apple.keylayout.Ukrainian-QWERTY@37.json" : { + "sha256" : "9883b792cf3c7113c041c9342f4f18ef9d697ff9cb8f289a4004ff20f1a3242d" + }, + "layout-tables\/v1\/com.apple.keylayout.Ukrainian-QWERTY@91.json" : { + "sha256" : "8bc9476d6354ed537a9ab6a60b280104bc48cd9e8f542134f057643ea679bceb" + }, + "layout-tables\/v1\/com.apple.keylayout.Ukrainian@37.json" : { + "sha256" : "a8e9b561ac7404769e2021aa9557dc8e6ea768f5b10b70fa7a9c632f5700dc85" + }, + "layout-tables\/v1\/com.apple.keylayout.Ukrainian@91.json" : { + "sha256" : "4307413f155a6ea20503e583f84e6b3669943718ca0709939cd6591691f53bee" + } + }, + "keyboardType" : 91, + "osBuild" : "24G830", + "osVersion" : "15.7.9", + "schemaVersion" : 1, + "sourceCommit" : "b8471649300162827cc4053d553c14a895820e8b", + "splits" : { + "development" : { + "abstain" : 20, + "preserve" : 60, + "repair" : 20, + "total" : 100 + }, + "holdout" : { + "abstain" : 20, + "preserve" : 60, + "repair" : 20, + "total" : 100 + } + }, + "tablesVersion" : "v1" +} \ No newline at end of file diff --git a/Benchmarks/layout-corpus/v2/README.md b/Benchmarks/layout-corpus/v2/README.md new file mode 100644 index 0000000..e691bbe --- /dev/null +++ b/Benchmarks/layout-corpus/v2/README.md @@ -0,0 +1,27 @@ +# Layout Benchmark Corpus v2 + +Specification: `docs/plans/03-comparative-false-positive-benchmark.md` +Source commit: `5daf6533deb186b5fa85fbffc042fffcf34a807a` +Generator: `LayoutBenchmark generate-seed-corpus --version v2` (byte-deterministic; regenerating reproduces the manifest hashes) + +v2 supersedes v1 as the regression corpus. v1 stays frozen and unchanged for reproducing its baselines. + +## Changes from v1 + +1. **Leaks and contradictory labels removed.** In v1 the identical typed inputs `і` and `чи` appeared in both splits with opposite labels (preserve vs abstain), `apple` was a development preserve and a holdout reversed-mapping repair, and holdout labeled `от` and `ну` both preserve and abstain. Five holdout trials are replaced, keeping their stratum, under new IDs: `holdout-repair-011-v2` (`garden`), `holdout-preserve-058-v2` (`ще`), `holdout-abstain-093-v2` (`аж`), `holdout-abstain-094-v2` (`хоч`), `holdout-abstain-098-v2` (`є`). +2. **Hard negatives added.** v1 negatives were all typed on the correct layout and never collided with a word in the other language, so a zero false-positive rate said little. Category `cross-layout-collision` adds 10 `preserve` trials per split, 5 typed on ABC and 5 on Ukrainian, each an intended token whose rendering on the other layout is a real word (for example `vs` → `ми`, `руку` → `here`). Inflections of one lemma and reversed mappings share a group. +3. **Validation is stricter.** `validate` reports identical typed input with different labels anywhere, and typed input or base text shared across splits; `--strict` fails on any of them. v1 validates with 9 such warnings, v2 with none. Validation also checks manifest hashes, caret offsets and exact keyboard-type tables. + +## Split Structure + +- Development: 110 trials (20 repair, 70 preserve, 20 abstain) +- Holdout: 110 trials (20 repair, 70 preserve, 20 abstain) + +Every label is divided equally between trials typed on ABC and on Ukrainian. All trials are synthetic, rendered through the frozen `Benchmarks/layout-tables/v1` tables, and carry `"review": "pending-human"`; human review results become a new corpus version. + +## Known limits + +- The `abstain` stratum is short words, gibberish and symbols typed on the correct layout; it does not yet contain genuinely ambiguous wrong-layout input. +- Hard negatives are few (20 in total) and correlated within groups; treat their rate as a regression signal, not a false-positive claim. + +The trial schema is unchanged from v1 (see `Benchmarks/layout-corpus/v1/README.md`). diff --git a/Benchmarks/layout-corpus/v2/development.jsonl b/Benchmarks/layout-corpus/v2/development.jsonl new file mode 100644 index 0000000..656d28e --- /dev/null +++ b/Benchmarks/layout-corpus/v2/development.jsonl @@ -0,0 +1,110 @@ +{"category":"single-word","events":[{"keyCode":5,"modifiers":[],"rendered":"g","type":"key"},{"keyCode":4,"modifiers":[],"rendered":"h","type":"key"},{"keyCode":1,"modifiers":[],"rendered":"s","type":"key"},{"keyCode":2,"modifiers":[],"rendered":"d","type":"key"},{"keyCode":11,"modifiers":[],"rendered":"b","type":"key"},{"keyCode":45,"modifiers":[],"rendered":"n","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":["replaceSuffix","switchSource"],"checkpoints":[{"afterEvent":7,"language":"uk","text":"привіт "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"привіт "},"groupID":"dev-grp-001","id":"dev-repair-001","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"repair","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"single-word","events":[{"keyCode":4,"modifiers":[],"rendered":"h","type":"key"},{"keyCode":38,"modifiers":[],"rendered":"j","type":"key"},{"keyCode":43,"modifiers":[],"rendered":",","type":"key"},{"keyCode":38,"modifiers":[],"rendered":"j","type":"key"},{"keyCode":45,"modifiers":[],"rendered":"n","type":"key"},{"keyCode":3,"modifiers":[],"rendered":"f","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":["replaceSuffix","switchSource"],"checkpoints":[{"afterEvent":7,"language":"uk","text":"робота "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"робота "},"groupID":"dev-grp-002","id":"dev-repair-002","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"repair","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"single-word","events":[{"keyCode":40,"modifiers":[],"rendered":"k","type":"key"},{"keyCode":47,"modifiers":[],"rendered":".","type":"key"},{"keyCode":37,"modifiers":[],"rendered":"l","type":"key"},{"keyCode":1,"modifiers":[],"rendered":"s","type":"key"},{"keyCode":16,"modifiers":[],"rendered":"y","type":"key"},{"keyCode":3,"modifiers":[],"rendered":"f","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":["replaceSuffix","switchSource"],"checkpoints":[{"afterEvent":7,"language":"uk","text":"людина "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"людина "},"groupID":"dev-grp-003","id":"dev-repair-003","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"repair","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"single-word","events":[{"keyCode":37,"modifiers":[],"rendered":"l","type":"key"},{"keyCode":6,"modifiers":[],"rendered":"z","type":"key"},{"keyCode":15,"modifiers":[],"rendered":"r","type":"key"},{"keyCode":14,"modifiers":[],"rendered":"e","type":"key"},{"keyCode":47,"modifiers":[],"rendered":".","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":["replaceSuffix","switchSource"],"checkpoints":[{"afterEvent":6,"language":"uk","text":"дякую "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"дякую "},"groupID":"dev-grp-004","id":"dev-repair-004","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"repair","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"capitalized","events":[{"keyCode":15,"modifiers":["shift"],"rendered":"R","type":"key"},{"keyCode":1,"modifiers":[],"rendered":"s","type":"key"},{"keyCode":30,"modifiers":[],"rendered":"]","type":"key"},{"keyCode":2,"modifiers":[],"rendered":"d","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":["replaceSuffix","switchSource"],"checkpoints":[{"afterEvent":5,"language":"uk","text":"Київ "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"Київ "},"groupID":"dev-grp-005","id":"dev-repair-005","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"repair","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"back-to-back","events":[{"keyCode":37,"modifiers":[],"rendered":"l","type":"key"},{"keyCode":38,"modifiers":[],"rendered":"j","type":"key"},{"keyCode":43,"modifiers":[],"rendered":",","type":"key"},{"keyCode":4,"modifiers":[],"rendered":"h","type":"key"},{"keyCode":1,"modifiers":[],"rendered":"s","type":"key"},{"keyCode":12,"modifiers":[],"rendered":"q","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"},{"keyCode":37,"modifiers":[],"rendered":"l","type":"key"},{"keyCode":17,"modifiers":[],"rendered":"t","type":"key"},{"keyCode":16,"modifiers":[],"rendered":"y","type":"key"},{"keyCode":46,"modifiers":[],"rendered":"m","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":["replaceSuffix","switchSource"],"checkpoints":[{"afterEvent":12,"language":"uk","text":"добрий день "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"добрий день "},"groupID":"dev-grp-006","id":"dev-repair-006","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"repair","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"single-word","events":[{"keyCode":37,"modifiers":[],"rendered":"l","type":"key"},{"keyCode":1,"modifiers":[],"rendered":"s","type":"key"},{"keyCode":2,"modifiers":[],"rendered":"d","type":"key"},{"keyCode":1,"modifiers":[],"rendered":"s","type":"key"},{"keyCode":9,"modifiers":[],"rendered":"v","type":"key"},{"keyCode":38,"modifiers":[],"rendered":"j","type":"key"},{"keyCode":8,"modifiers":[],"rendered":"c","type":"key"},{"keyCode":46,"modifiers":[],"rendered":"m","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":["replaceSuffix","switchSource"],"checkpoints":[{"afterEvent":9,"language":"uk","text":"дивимось "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"дивимось "},"groupID":"dev-grp-007","id":"dev-repair-007","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"repair","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"single-word","events":[{"keyCode":7,"modifiers":[],"rendered":"x","type":"key"},{"keyCode":14,"modifiers":[],"rendered":"e","type":"key"},{"keyCode":37,"modifiers":[],"rendered":"l","type":"key"},{"keyCode":38,"modifiers":[],"rendered":"j","type":"key"},{"keyCode":2,"modifiers":[],"rendered":"d","type":"key"},{"keyCode":38,"modifiers":[],"rendered":"j","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":["replaceSuffix","switchSource"],"checkpoints":[{"afterEvent":7,"language":"uk","text":"чудово "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"чудово "},"groupID":"dev-grp-008","id":"dev-repair-008","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"repair","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"single-word","events":[{"keyCode":5,"modifiers":[],"rendered":"g","type":"key"},{"keyCode":4,"modifiers":[],"rendered":"h","type":"key"},{"keyCode":3,"modifiers":[],"rendered":"f","type":"key"},{"keyCode":2,"modifiers":[],"rendered":"d","type":"key"},{"keyCode":37,"modifiers":[],"rendered":"l","type":"key"},{"keyCode":3,"modifiers":[],"rendered":"f","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":["replaceSuffix","switchSource"],"checkpoints":[{"afterEvent":7,"language":"uk","text":"правда "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"правда "},"groupID":"dev-grp-009","id":"dev-repair-009","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"repair","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"question","events":[{"keyCode":6,"modifiers":[],"rendered":"z","type":"key"},{"keyCode":15,"modifiers":[],"rendered":"r","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"},{"keyCode":8,"modifiers":[],"rendered":"c","type":"key"},{"keyCode":5,"modifiers":[],"rendered":"g","type":"key"},{"keyCode":4,"modifiers":[],"rendered":"h","type":"key"},{"keyCode":3,"modifiers":[],"rendered":"f","type":"key"},{"keyCode":2,"modifiers":[],"rendered":"d","type":"key"},{"keyCode":1,"modifiers":[],"rendered":"s","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":["replaceSuffix","switchSource"],"checkpoints":[{"afterEvent":10,"language":"uk","text":"як справи "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"як справи "},"groupID":"dev-grp-010","id":"dev-repair-010","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"repair","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"single-word","events":[{"keyCode":4,"modifiers":[],"rendered":"р","type":"key"},{"keyCode":14,"modifiers":[],"rendered":"у","type":"key"},{"keyCode":37,"modifiers":[],"rendered":"д","type":"key"},{"keyCode":37,"modifiers":[],"rendered":"д","type":"key"},{"keyCode":31,"modifiers":[],"rendered":"щ","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":["replaceSuffix","switchSource"],"checkpoints":[{"afterEvent":6,"language":"en","text":"hello "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"hello "},"groupID":"dev-grp-011","id":"dev-repair-011","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"repair","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"single-word","events":[{"keyCode":13,"modifiers":[],"rendered":"ц","type":"key"},{"keyCode":31,"modifiers":[],"rendered":"щ","type":"key"},{"keyCode":15,"modifiers":[],"rendered":"к","type":"key"},{"keyCode":37,"modifiers":[],"rendered":"д","type":"key"},{"keyCode":2,"modifiers":[],"rendered":"в","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":["replaceSuffix","switchSource"],"checkpoints":[{"afterEvent":6,"language":"en","text":"world "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"world "},"groupID":"dev-grp-012","id":"dev-repair-012","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"repair","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"single-word","events":[{"keyCode":1,"modifiers":[],"rendered":"и","type":"key"},{"keyCode":16,"modifiers":[],"rendered":"н","type":"key"},{"keyCode":1,"modifiers":[],"rendered":"и","type":"key"},{"keyCode":17,"modifiers":[],"rendered":"е","type":"key"},{"keyCode":14,"modifiers":[],"rendered":"у","type":"key"},{"keyCode":46,"modifiers":[],"rendered":"ь","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":["replaceSuffix","switchSource"],"checkpoints":[{"afterEvent":7,"language":"en","text":"system "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"system "},"groupID":"dev-grp-013","id":"dev-repair-013","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"repair","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"single-word","events":[{"keyCode":13,"modifiers":[],"rendered":"ц","type":"key"},{"keyCode":34,"modifiers":[],"rendered":"ш","type":"key"},{"keyCode":45,"modifiers":[],"rendered":"т","type":"key"},{"keyCode":2,"modifiers":[],"rendered":"в","type":"key"},{"keyCode":31,"modifiers":[],"rendered":"щ","type":"key"},{"keyCode":13,"modifiers":[],"rendered":"ц","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":["replaceSuffix","switchSource"],"checkpoints":[{"afterEvent":7,"language":"en","text":"window "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"window "},"groupID":"dev-grp-014","id":"dev-repair-014","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"repair","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"capitalized","events":[{"keyCode":37,"modifiers":["shift"],"rendered":"Д","type":"key"},{"keyCode":31,"modifiers":[],"rendered":"щ","type":"key"},{"keyCode":45,"modifiers":[],"rendered":"т","type":"key"},{"keyCode":2,"modifiers":[],"rendered":"в","type":"key"},{"keyCode":31,"modifiers":[],"rendered":"щ","type":"key"},{"keyCode":45,"modifiers":[],"rendered":"т","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":["replaceSuffix","switchSource"],"checkpoints":[{"afterEvent":7,"language":"en","text":"London "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"London "},"groupID":"dev-grp-015","id":"dev-repair-015","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"repair","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"back-to-back","events":[{"keyCode":5,"modifiers":[],"rendered":"п","type":"key"},{"keyCode":31,"modifiers":[],"rendered":"щ","type":"key"},{"keyCode":31,"modifiers":[],"rendered":"щ","type":"key"},{"keyCode":2,"modifiers":[],"rendered":"в","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"},{"keyCode":46,"modifiers":[],"rendered":"ь","type":"key"},{"keyCode":31,"modifiers":[],"rendered":"щ","type":"key"},{"keyCode":15,"modifiers":[],"rendered":"к","type":"key"},{"keyCode":45,"modifiers":[],"rendered":"т","type":"key"},{"keyCode":34,"modifiers":[],"rendered":"ш","type":"key"},{"keyCode":45,"modifiers":[],"rendered":"т","type":"key"},{"keyCode":5,"modifiers":[],"rendered":"п","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":["replaceSuffix","switchSource"],"checkpoints":[{"afterEvent":13,"language":"en","text":"good morning "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"good morning "},"groupID":"dev-grp-016","id":"dev-repair-016","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"repair","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"single-word","events":[{"keyCode":14,"modifiers":[],"rendered":"у","type":"key"},{"keyCode":7,"modifiers":[],"rendered":"ч","type":"key"},{"keyCode":35,"modifiers":[],"rendered":"з","type":"key"},{"keyCode":14,"modifiers":[],"rendered":"у","type":"key"},{"keyCode":8,"modifiers":[],"rendered":"с","type":"key"},{"keyCode":17,"modifiers":[],"rendered":"е","type":"key"},{"keyCode":14,"modifiers":[],"rendered":"у","type":"key"},{"keyCode":2,"modifiers":[],"rendered":"в","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":["replaceSuffix","switchSource"],"checkpoints":[{"afterEvent":9,"language":"en","text":"expected "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"expected "},"groupID":"dev-grp-017","id":"dev-repair-017","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"repair","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"single-word","events":[{"keyCode":17,"modifiers":[],"rendered":"е","type":"key"},{"keyCode":4,"modifiers":[],"rendered":"р","type":"key"},{"keyCode":0,"modifiers":[],"rendered":"ф","type":"key"},{"keyCode":45,"modifiers":[],"rendered":"т","type":"key"},{"keyCode":40,"modifiers":[],"rendered":"л","type":"key"},{"keyCode":1,"modifiers":[],"rendered":"и","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":["replaceSuffix","switchSource"],"checkpoints":[{"afterEvent":7,"language":"en","text":"thanks "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"thanks "},"groupID":"dev-grp-018","id":"dev-repair-018","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"repair","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"single-word","events":[{"keyCode":35,"modifiers":[],"rendered":"з","type":"key"},{"keyCode":15,"modifiers":[],"rendered":"к","type":"key"},{"keyCode":31,"modifiers":[],"rendered":"щ","type":"key"},{"keyCode":38,"modifiers":[],"rendered":"о","type":"key"},{"keyCode":14,"modifiers":[],"rendered":"у","type":"key"},{"keyCode":8,"modifiers":[],"rendered":"с","type":"key"},{"keyCode":17,"modifiers":[],"rendered":"е","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":["replaceSuffix","switchSource"],"checkpoints":[{"afterEvent":8,"language":"en","text":"project "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"project "},"groupID":"dev-grp-019","id":"dev-repair-019","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"repair","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"question","events":[{"keyCode":4,"modifiers":[],"rendered":"р","type":"key"},{"keyCode":31,"modifiers":[],"rendered":"щ","type":"key"},{"keyCode":13,"modifiers":[],"rendered":"ц","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"},{"keyCode":0,"modifiers":[],"rendered":"ф","type":"key"},{"keyCode":15,"modifiers":[],"rendered":"к","type":"key"},{"keyCode":14,"modifiers":[],"rendered":"у","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"},{"keyCode":16,"modifiers":[],"rendered":"н","type":"key"},{"keyCode":31,"modifiers":[],"rendered":"щ","type":"key"},{"keyCode":32,"modifiers":[],"rendered":"г","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":["replaceSuffix","switchSource"],"checkpoints":[{"afterEvent":12,"language":"en","text":"how are you "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"how are you "},"groupID":"dev-grp-020","id":"dev-repair-020","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"repair","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"natural-word","events":[{"keyCode":0,"modifiers":[],"rendered":"a","type":"key"},{"keyCode":35,"modifiers":[],"rendered":"p","type":"key"},{"keyCode":35,"modifiers":[],"rendered":"p","type":"key"},{"keyCode":37,"modifiers":[],"rendered":"l","type":"key"},{"keyCode":14,"modifiers":[],"rendered":"e","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":6,"language":"en","text":"apple "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"apple "},"groupID":"dev-grp-021","id":"dev-preserve-021","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"natural-word","events":[{"keyCode":1,"modifiers":[],"rendered":"s","type":"key"},{"keyCode":13,"modifiers":[],"rendered":"w","type":"key"},{"keyCode":34,"modifiers":[],"rendered":"i","type":"key"},{"keyCode":3,"modifiers":[],"rendered":"f","type":"key"},{"keyCode":17,"modifiers":[],"rendered":"t","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":6,"language":"en","text":"swift "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"swift "},"groupID":"dev-grp-022","id":"dev-preserve-022","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"natural-word","events":[{"keyCode":2,"modifiers":[],"rendered":"d","type":"key"},{"keyCode":14,"modifiers":[],"rendered":"e","type":"key"},{"keyCode":9,"modifiers":[],"rendered":"v","type":"key"},{"keyCode":14,"modifiers":[],"rendered":"e","type":"key"},{"keyCode":37,"modifiers":[],"rendered":"l","type":"key"},{"keyCode":31,"modifiers":[],"rendered":"o","type":"key"},{"keyCode":35,"modifiers":[],"rendered":"p","type":"key"},{"keyCode":14,"modifiers":[],"rendered":"e","type":"key"},{"keyCode":15,"modifiers":[],"rendered":"r","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":10,"language":"en","text":"developer "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"developer "},"groupID":"dev-grp-023","id":"dev-preserve-023","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"natural-word","events":[{"keyCode":17,"modifiers":[],"rendered":"t","type":"key"},{"keyCode":14,"modifiers":[],"rendered":"e","type":"key"},{"keyCode":15,"modifiers":[],"rendered":"r","type":"key"},{"keyCode":46,"modifiers":[],"rendered":"m","type":"key"},{"keyCode":34,"modifiers":[],"rendered":"i","type":"key"},{"keyCode":45,"modifiers":[],"rendered":"n","type":"key"},{"keyCode":0,"modifiers":[],"rendered":"a","type":"key"},{"keyCode":37,"modifiers":[],"rendered":"l","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":9,"language":"en","text":"terminal "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"terminal "},"groupID":"dev-grp-024","id":"dev-preserve-024","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"natural-word","events":[{"keyCode":15,"modifiers":[],"rendered":"r","type":"key"},{"keyCode":14,"modifiers":[],"rendered":"e","type":"key"},{"keyCode":37,"modifiers":[],"rendered":"l","type":"key"},{"keyCode":14,"modifiers":[],"rendered":"e","type":"key"},{"keyCode":0,"modifiers":[],"rendered":"a","type":"key"},{"keyCode":1,"modifiers":[],"rendered":"s","type":"key"},{"keyCode":14,"modifiers":[],"rendered":"e","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":8,"language":"en","text":"release "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"release "},"groupID":"dev-grp-025","id":"dev-preserve-025","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"natural-word","events":[{"keyCode":40,"modifiers":[],"rendered":"k","type":"key"},{"keyCode":14,"modifiers":[],"rendered":"e","type":"key"},{"keyCode":16,"modifiers":[],"rendered":"y","type":"key"},{"keyCode":11,"modifiers":[],"rendered":"b","type":"key"},{"keyCode":31,"modifiers":[],"rendered":"o","type":"key"},{"keyCode":0,"modifiers":[],"rendered":"a","type":"key"},{"keyCode":15,"modifiers":[],"rendered":"r","type":"key"},{"keyCode":2,"modifiers":[],"rendered":"d","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":9,"language":"en","text":"keyboard "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"keyboard "},"groupID":"dev-grp-026","id":"dev-preserve-026","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"short-word","events":[{"keyCode":17,"modifiers":[],"rendered":"t","type":"key"},{"keyCode":4,"modifiers":[],"rendered":"h","type":"key"},{"keyCode":14,"modifiers":[],"rendered":"e","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":4,"language":"en","text":"the "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"the "},"groupID":"dev-grp-027","id":"dev-preserve-027","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"short-word","events":[{"keyCode":0,"modifiers":[],"rendered":"a","type":"key"},{"keyCode":45,"modifiers":[],"rendered":"n","type":"key"},{"keyCode":2,"modifiers":[],"rendered":"d","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":4,"language":"en","text":"and "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"and "},"groupID":"dev-grp-028","id":"dev-preserve-028","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"short-word","events":[{"keyCode":3,"modifiers":[],"rendered":"f","type":"key"},{"keyCode":31,"modifiers":[],"rendered":"o","type":"key"},{"keyCode":15,"modifiers":[],"rendered":"r","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":4,"language":"en","text":"for "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"for "},"groupID":"dev-grp-029","id":"dev-preserve-029","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"short-word","events":[{"keyCode":45,"modifiers":[],"rendered":"n","type":"key"},{"keyCode":31,"modifiers":[],"rendered":"o","type":"key"},{"keyCode":17,"modifiers":[],"rendered":"t","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":4,"language":"en","text":"not "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"not "},"groupID":"dev-grp-030","id":"dev-preserve-030","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"short-word","events":[{"keyCode":11,"modifiers":[],"rendered":"b","type":"key"},{"keyCode":32,"modifiers":[],"rendered":"u","type":"key"},{"keyCode":17,"modifiers":[],"rendered":"t","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":4,"language":"en","text":"but "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"but "},"groupID":"dev-grp-031","id":"dev-preserve-031","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"short-word","events":[{"keyCode":16,"modifiers":[],"rendered":"y","type":"key"},{"keyCode":14,"modifiers":[],"rendered":"e","type":"key"},{"keyCode":1,"modifiers":[],"rendered":"s","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":4,"language":"en","text":"yes "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"yes "},"groupID":"dev-grp-032","id":"dev-preserve-032","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"proper-noun","events":[{"keyCode":5,"modifiers":["shift"],"rendered":"G","type":"key"},{"keyCode":34,"modifiers":[],"rendered":"i","type":"key"},{"keyCode":17,"modifiers":[],"rendered":"t","type":"key"},{"keyCode":4,"modifiers":["shift"],"rendered":"H","type":"key"},{"keyCode":32,"modifiers":[],"rendered":"u","type":"key"},{"keyCode":11,"modifiers":[],"rendered":"b","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":7,"language":"en","text":"GitHub "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"GitHub "},"groupID":"dev-grp-033","id":"dev-preserve-033","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"proper-noun","events":[{"keyCode":32,"modifiers":["shift"],"rendered":"U","type":"key"},{"keyCode":45,"modifiers":[],"rendered":"n","type":"key"},{"keyCode":34,"modifiers":[],"rendered":"i","type":"key"},{"keyCode":7,"modifiers":[],"rendered":"x","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":5,"language":"en","text":"Unix "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"Unix "},"groupID":"dev-grp-034","id":"dev-preserve-034","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"proper-noun","events":[{"keyCode":7,"modifiers":["shift"],"rendered":"X","type":"key"},{"keyCode":8,"modifiers":[],"rendered":"c","type":"key"},{"keyCode":31,"modifiers":[],"rendered":"o","type":"key"},{"keyCode":2,"modifiers":[],"rendered":"d","type":"key"},{"keyCode":14,"modifiers":[],"rendered":"e","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":6,"language":"en","text":"Xcode "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"Xcode "},"groupID":"dev-grp-035","id":"dev-preserve-035","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"proper-noun","events":[{"keyCode":46,"modifiers":[],"rendered":"m","type":"key"},{"keyCode":0,"modifiers":[],"rendered":"a","type":"key"},{"keyCode":8,"modifiers":[],"rendered":"c","type":"key"},{"keyCode":31,"modifiers":["shift"],"rendered":"O","type":"key"},{"keyCode":1,"modifiers":["shift"],"rendered":"S","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":6,"language":"en","text":"macOS "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"macOS "},"groupID":"dev-grp-036","id":"dev-preserve-036","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"code-token","events":[{"keyCode":8,"modifiers":[],"rendered":"c","type":"key"},{"keyCode":0,"modifiers":[],"rendered":"a","type":"key"},{"keyCode":46,"modifiers":[],"rendered":"m","type":"key"},{"keyCode":14,"modifiers":[],"rendered":"e","type":"key"},{"keyCode":37,"modifiers":[],"rendered":"l","type":"key"},{"keyCode":8,"modifiers":["shift"],"rendered":"C","type":"key"},{"keyCode":0,"modifiers":[],"rendered":"a","type":"key"},{"keyCode":1,"modifiers":[],"rendered":"s","type":"key"},{"keyCode":14,"modifiers":[],"rendered":"e","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":10,"language":"en","text":"camelCase "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"camelCase "},"groupID":"dev-grp-037","id":"dev-preserve-037","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"code-token","events":[{"keyCode":1,"modifiers":[],"rendered":"s","type":"key"},{"keyCode":45,"modifiers":[],"rendered":"n","type":"key"},{"keyCode":0,"modifiers":[],"rendered":"a","type":"key"},{"keyCode":40,"modifiers":[],"rendered":"k","type":"key"},{"keyCode":14,"modifiers":[],"rendered":"e","type":"key"},{"keyCode":27,"modifiers":["shift"],"rendered":"_","type":"key"},{"keyCode":8,"modifiers":[],"rendered":"c","type":"key"},{"keyCode":0,"modifiers":[],"rendered":"a","type":"key"},{"keyCode":1,"modifiers":[],"rendered":"s","type":"key"},{"keyCode":14,"modifiers":[],"rendered":"e","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":11,"language":"en","text":"snake_case "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"snake_case "},"groupID":"dev-grp-038","id":"dev-preserve-038","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"code-token","events":[{"keyCode":40,"modifiers":[],"rendered":"k","type":"key"},{"keyCode":32,"modifiers":["shift"],"rendered":"U","type":"key"},{"keyCode":8,"modifiers":["shift"],"rendered":"C","type":"key"},{"keyCode":40,"modifiers":["shift"],"rendered":"K","type":"key"},{"keyCode":14,"modifiers":[],"rendered":"e","type":"key"},{"keyCode":16,"modifiers":[],"rendered":"y","type":"key"},{"keyCode":0,"modifiers":["shift"],"rendered":"A","type":"key"},{"keyCode":8,"modifiers":[],"rendered":"c","type":"key"},{"keyCode":17,"modifiers":[],"rendered":"t","type":"key"},{"keyCode":34,"modifiers":[],"rendered":"i","type":"key"},{"keyCode":31,"modifiers":[],"rendered":"o","type":"key"},{"keyCode":45,"modifiers":[],"rendered":"n","type":"key"},{"keyCode":2,"modifiers":["shift"],"rendered":"D","type":"key"},{"keyCode":31,"modifiers":[],"rendered":"o","type":"key"},{"keyCode":13,"modifiers":[],"rendered":"w","type":"key"},{"keyCode":45,"modifiers":[],"rendered":"n","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":17,"language":"en","text":"kUCKeyActionDown "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"kUCKeyActionDown "},"groupID":"dev-grp-039","id":"dev-preserve-039","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"code-token","events":[{"keyCode":34,"modifiers":[],"rendered":"i","type":"key"},{"keyCode":45,"modifiers":[],"rendered":"n","type":"key"},{"keyCode":34,"modifiers":[],"rendered":"i","type":"key"},{"keyCode":17,"modifiers":[],"rendered":"t","type":"key"},{"keyCode":25,"modifiers":["shift"],"rendered":"(","type":"key"},{"keyCode":29,"modifiers":["shift"],"rendered":")","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":7,"language":"en","text":"init() "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"init() "},"groupID":"dev-grp-040","id":"dev-preserve-040","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"path","events":[{"keyCode":44,"modifiers":[],"rendered":"/","type":"key"},{"keyCode":32,"modifiers":[],"rendered":"u","type":"key"},{"keyCode":1,"modifiers":[],"rendered":"s","type":"key"},{"keyCode":15,"modifiers":[],"rendered":"r","type":"key"},{"keyCode":44,"modifiers":[],"rendered":"/","type":"key"},{"keyCode":37,"modifiers":[],"rendered":"l","type":"key"},{"keyCode":31,"modifiers":[],"rendered":"o","type":"key"},{"keyCode":8,"modifiers":[],"rendered":"c","type":"key"},{"keyCode":0,"modifiers":[],"rendered":"a","type":"key"},{"keyCode":37,"modifiers":[],"rendered":"l","type":"key"},{"keyCode":44,"modifiers":[],"rendered":"/","type":"key"},{"keyCode":11,"modifiers":[],"rendered":"b","type":"key"},{"keyCode":34,"modifiers":[],"rendered":"i","type":"key"},{"keyCode":45,"modifiers":[],"rendered":"n","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":15,"language":"en","text":"/usr/local/bin "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"/usr/local/bin "},"groupID":"dev-grp-041","id":"dev-preserve-041","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"filename","events":[{"keyCode":35,"modifiers":["shift"],"rendered":"P","type":"key"},{"keyCode":0,"modifiers":[],"rendered":"a","type":"key"},{"keyCode":8,"modifiers":[],"rendered":"c","type":"key"},{"keyCode":40,"modifiers":[],"rendered":"k","type":"key"},{"keyCode":0,"modifiers":[],"rendered":"a","type":"key"},{"keyCode":5,"modifiers":[],"rendered":"g","type":"key"},{"keyCode":14,"modifiers":[],"rendered":"e","type":"key"},{"keyCode":47,"modifiers":[],"rendered":".","type":"key"},{"keyCode":1,"modifiers":[],"rendered":"s","type":"key"},{"keyCode":13,"modifiers":[],"rendered":"w","type":"key"},{"keyCode":34,"modifiers":[],"rendered":"i","type":"key"},{"keyCode":3,"modifiers":[],"rendered":"f","type":"key"},{"keyCode":17,"modifiers":[],"rendered":"t","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":14,"language":"en","text":"Package.swift "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"Package.swift "},"groupID":"dev-grp-042","id":"dev-preserve-042","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"url","events":[{"keyCode":4,"modifiers":[],"rendered":"h","type":"key"},{"keyCode":17,"modifiers":[],"rendered":"t","type":"key"},{"keyCode":17,"modifiers":[],"rendered":"t","type":"key"},{"keyCode":35,"modifiers":[],"rendered":"p","type":"key"},{"keyCode":1,"modifiers":[],"rendered":"s","type":"key"},{"keyCode":41,"modifiers":["shift"],"rendered":":","type":"key"},{"keyCode":44,"modifiers":[],"rendered":"/","type":"key"},{"keyCode":44,"modifiers":[],"rendered":"/","type":"key"},{"keyCode":0,"modifiers":[],"rendered":"a","type":"key"},{"keyCode":35,"modifiers":[],"rendered":"p","type":"key"},{"keyCode":35,"modifiers":[],"rendered":"p","type":"key"},{"keyCode":37,"modifiers":[],"rendered":"l","type":"key"},{"keyCode":14,"modifiers":[],"rendered":"e","type":"key"},{"keyCode":47,"modifiers":[],"rendered":".","type":"key"},{"keyCode":8,"modifiers":[],"rendered":"c","type":"key"},{"keyCode":31,"modifiers":[],"rendered":"o","type":"key"},{"keyCode":46,"modifiers":[],"rendered":"m","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":18,"language":"en","text":"https://apple.com "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"https://apple.com "},"groupID":"dev-grp-043","id":"dev-preserve-043","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"email","events":[{"keyCode":2,"modifiers":[],"rendered":"d","type":"key"},{"keyCode":14,"modifiers":[],"rendered":"e","type":"key"},{"keyCode":9,"modifiers":[],"rendered":"v","type":"key"},{"keyCode":19,"modifiers":["shift"],"rendered":"@","type":"key"},{"keyCode":14,"modifiers":[],"rendered":"e","type":"key"},{"keyCode":7,"modifiers":[],"rendered":"x","type":"key"},{"keyCode":0,"modifiers":[],"rendered":"a","type":"key"},{"keyCode":46,"modifiers":[],"rendered":"m","type":"key"},{"keyCode":35,"modifiers":[],"rendered":"p","type":"key"},{"keyCode":37,"modifiers":[],"rendered":"l","type":"key"},{"keyCode":14,"modifiers":[],"rendered":"e","type":"key"},{"keyCode":47,"modifiers":[],"rendered":".","type":"key"},{"keyCode":31,"modifiers":[],"rendered":"o","type":"key"},{"keyCode":15,"modifiers":[],"rendered":"r","type":"key"},{"keyCode":5,"modifiers":[],"rendered":"g","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":16,"language":"en","text":"dev@example.org "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"dev@example.org "},"groupID":"dev-grp-044","id":"dev-preserve-044","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"acronym","events":[{"keyCode":4,"modifiers":["shift"],"rendered":"H","type":"key"},{"keyCode":17,"modifiers":["shift"],"rendered":"T","type":"key"},{"keyCode":17,"modifiers":["shift"],"rendered":"T","type":"key"},{"keyCode":35,"modifiers":["shift"],"rendered":"P","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":5,"language":"en","text":"HTTP "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"HTTP "},"groupID":"dev-grp-045","id":"dev-preserve-045","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"acronym","events":[{"keyCode":38,"modifiers":["shift"],"rendered":"J","type":"key"},{"keyCode":1,"modifiers":["shift"],"rendered":"S","type":"key"},{"keyCode":31,"modifiers":["shift"],"rendered":"O","type":"key"},{"keyCode":45,"modifiers":["shift"],"rendered":"N","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":5,"language":"en","text":"JSON "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"JSON "},"groupID":"dev-grp-046","id":"dev-preserve-046","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"acronym","events":[{"keyCode":32,"modifiers":["shift"],"rendered":"U","type":"key"},{"keyCode":32,"modifiers":["shift"],"rendered":"U","type":"key"},{"keyCode":34,"modifiers":["shift"],"rendered":"I","type":"key"},{"keyCode":2,"modifiers":["shift"],"rendered":"D","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":5,"language":"en","text":"UUID "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"UUID "},"groupID":"dev-grp-047","id":"dev-preserve-047","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"contraction","events":[{"keyCode":2,"modifiers":[],"rendered":"d","type":"key"},{"keyCode":31,"modifiers":[],"rendered":"o","type":"key"},{"keyCode":45,"modifiers":[],"rendered":"n","type":"key"},{"keyCode":39,"modifiers":[],"rendered":"'","type":"key"},{"keyCode":17,"modifiers":[],"rendered":"t","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":6,"language":"en","text":"don't "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"don't "},"groupID":"dev-grp-048","id":"dev-preserve-048","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"contraction","events":[{"keyCode":34,"modifiers":[],"rendered":"i","type":"key"},{"keyCode":17,"modifiers":[],"rendered":"t","type":"key"},{"keyCode":39,"modifiers":[],"rendered":"'","type":"key"},{"keyCode":1,"modifiers":[],"rendered":"s","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":5,"language":"en","text":"it's "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"it's "},"groupID":"dev-grp-049","id":"dev-preserve-049","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"punctuation","events":[{"keyCode":15,"modifiers":[],"rendered":"r","type":"key"},{"keyCode":14,"modifiers":[],"rendered":"e","type":"key"},{"keyCode":1,"modifiers":[],"rendered":"s","type":"key"},{"keyCode":32,"modifiers":[],"rendered":"u","type":"key"},{"keyCode":37,"modifiers":[],"rendered":"l","type":"key"},{"keyCode":17,"modifiers":[],"rendered":"t","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"},{"keyCode":24,"modifiers":[],"rendered":"=","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"},{"keyCode":17,"modifiers":[],"rendered":"t","type":"key"},{"keyCode":15,"modifiers":[],"rendered":"r","type":"key"},{"keyCode":32,"modifiers":[],"rendered":"u","type":"key"},{"keyCode":14,"modifiers":[],"rendered":"e","type":"key"},{"keyCode":41,"modifiers":[],"rendered":";","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":15,"language":"en","text":"result = true; "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"result = true; "},"groupID":"dev-grp-050","id":"dev-preserve-050","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"natural-word","events":[{"keyCode":6,"modifiers":[],"rendered":"я","type":"key"},{"keyCode":43,"modifiers":[],"rendered":"б","type":"key"},{"keyCode":40,"modifiers":[],"rendered":"л","type":"key"},{"keyCode":14,"modifiers":[],"rendered":"у","type":"key"},{"keyCode":15,"modifiers":[],"rendered":"к","type":"key"},{"keyCode":38,"modifiers":[],"rendered":"о","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":7,"language":"uk","text":"яблуко "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"яблуко "},"groupID":"dev-grp-051","id":"dev-preserve-051","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"natural-word","events":[{"keyCode":4,"modifiers":[],"rendered":"р","type":"key"},{"keyCode":1,"modifiers":[],"rendered":"и","type":"key"},{"keyCode":16,"modifiers":[],"rendered":"н","type":"key"},{"keyCode":38,"modifiers":[],"rendered":"о","type":"key"},{"keyCode":15,"modifiers":[],"rendered":"к","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":6,"language":"uk","text":"ринок "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"ринок "},"groupID":"dev-grp-052","id":"dev-preserve-052","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"natural-word","events":[{"keyCode":4,"modifiers":[],"rendered":"р","type":"key"},{"keyCode":38,"modifiers":[],"rendered":"о","type":"key"},{"keyCode":35,"modifiers":[],"rendered":"з","type":"key"},{"keyCode":4,"modifiers":[],"rendered":"р","type":"key"},{"keyCode":38,"modifiers":[],"rendered":"о","type":"key"},{"keyCode":43,"modifiers":[],"rendered":"б","type":"key"},{"keyCode":16,"modifiers":[],"rendered":"н","type":"key"},{"keyCode":1,"modifiers":[],"rendered":"и","type":"key"},{"keyCode":15,"modifiers":[],"rendered":"к","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":10,"language":"uk","text":"розробник "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"розробник "},"groupID":"dev-grp-053","id":"dev-preserve-053","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"natural-word","events":[{"keyCode":45,"modifiers":[],"rendered":"т","type":"key"},{"keyCode":17,"modifiers":[],"rendered":"е","type":"key"},{"keyCode":4,"modifiers":[],"rendered":"р","type":"key"},{"keyCode":9,"modifiers":[],"rendered":"м","type":"key"},{"keyCode":11,"modifiers":[],"rendered":"і","type":"key"},{"keyCode":16,"modifiers":[],"rendered":"н","type":"key"},{"keyCode":3,"modifiers":[],"rendered":"а","type":"key"},{"keyCode":40,"modifiers":[],"rendered":"л","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":9,"language":"uk","text":"термінал "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"термінал "},"groupID":"dev-grp-054","id":"dev-preserve-054","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"natural-word","events":[{"keyCode":4,"modifiers":[],"rendered":"р","type":"key"},{"keyCode":17,"modifiers":[],"rendered":"е","type":"key"},{"keyCode":40,"modifiers":[],"rendered":"л","type":"key"},{"keyCode":11,"modifiers":[],"rendered":"і","type":"key"},{"keyCode":35,"modifiers":[],"rendered":"з","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":6,"language":"uk","text":"реліз "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"реліз "},"groupID":"dev-grp-055","id":"dev-preserve-055","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"natural-word","events":[{"keyCode":8,"modifiers":[],"rendered":"с","type":"key"},{"keyCode":1,"modifiers":[],"rendered":"и","type":"key"},{"keyCode":8,"modifiers":[],"rendered":"с","type":"key"},{"keyCode":45,"modifiers":[],"rendered":"т","type":"key"},{"keyCode":17,"modifiers":[],"rendered":"е","type":"key"},{"keyCode":9,"modifiers":[],"rendered":"м","type":"key"},{"keyCode":3,"modifiers":[],"rendered":"а","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":8,"language":"uk","text":"система "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"система "},"groupID":"dev-grp-056","id":"dev-preserve-056","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"short-word","events":[{"keyCode":13,"modifiers":[],"rendered":"ц","type":"key"},{"keyCode":17,"modifiers":[],"rendered":"е","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":3,"language":"uk","text":"це "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"це "},"groupID":"dev-grp-057","id":"dev-preserve-057","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"short-word","events":[{"keyCode":11,"modifiers":[],"rendered":"і","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":2,"language":"uk","text":"і "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"і "},"groupID":"dev-grp-058","id":"dev-preserve-058","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"short-word","events":[{"keyCode":16,"modifiers":[],"rendered":"н","type":"key"},{"keyCode":3,"modifiers":[],"rendered":"а","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":3,"language":"uk","text":"на "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"на "},"groupID":"dev-grp-059","id":"dev-preserve-059","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"short-word","events":[{"keyCode":37,"modifiers":[],"rendered":"д","type":"key"},{"keyCode":38,"modifiers":[],"rendered":"о","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":3,"language":"uk","text":"до "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"до "},"groupID":"dev-grp-060","id":"dev-preserve-060","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"short-word","events":[{"keyCode":45,"modifiers":[],"rendered":"т","type":"key"},{"keyCode":3,"modifiers":[],"rendered":"а","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":3,"language":"uk","text":"та "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"та "},"groupID":"dev-grp-061","id":"dev-preserve-061","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"short-word","events":[{"keyCode":9,"modifiers":[],"rendered":"м","type":"key"},{"keyCode":1,"modifiers":[],"rendered":"и","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":3,"language":"uk","text":"ми "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"ми "},"groupID":"dev-grp-062","id":"dev-preserve-062","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"proper-noun","events":[{"keyCode":40,"modifiers":["shift"],"rendered":"Л","type":"key"},{"keyCode":46,"modifiers":[],"rendered":"ь","type":"key"},{"keyCode":2,"modifiers":[],"rendered":"в","type":"key"},{"keyCode":11,"modifiers":[],"rendered":"і","type":"key"},{"keyCode":2,"modifiers":[],"rendered":"в","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":6,"language":"uk","text":"Львів "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"Львів "},"groupID":"dev-grp-063","id":"dev-preserve-063","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"proper-noun","events":[{"keyCode":45,"modifiers":["shift"],"rendered":"Т","type":"key"},{"keyCode":3,"modifiers":[],"rendered":"а","type":"key"},{"keyCode":4,"modifiers":[],"rendered":"р","type":"key"},{"keyCode":3,"modifiers":[],"rendered":"а","type":"key"},{"keyCode":8,"modifiers":[],"rendered":"с","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":6,"language":"uk","text":"Тарас "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"Тарас "},"groupID":"dev-grp-064","id":"dev-preserve-064","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"proper-noun","events":[{"keyCode":34,"modifiers":["shift"],"rendered":"Ш","type":"key"},{"keyCode":17,"modifiers":[],"rendered":"е","type":"key"},{"keyCode":2,"modifiers":[],"rendered":"в","type":"key"},{"keyCode":7,"modifiers":[],"rendered":"ч","type":"key"},{"keyCode":17,"modifiers":[],"rendered":"е","type":"key"},{"keyCode":16,"modifiers":[],"rendered":"н","type":"key"},{"keyCode":15,"modifiers":[],"rendered":"к","type":"key"},{"keyCode":38,"modifiers":[],"rendered":"о","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":9,"language":"uk","text":"Шевченко "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"Шевченко "},"groupID":"dev-grp-065","id":"dev-preserve-065","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"proper-noun","events":[{"keyCode":37,"modifiers":["shift"],"rendered":"Д","type":"key"},{"keyCode":16,"modifiers":[],"rendered":"н","type":"key"},{"keyCode":11,"modifiers":[],"rendered":"і","type":"key"},{"keyCode":5,"modifiers":[],"rendered":"п","type":"key"},{"keyCode":4,"modifiers":[],"rendered":"р","type":"key"},{"keyCode":38,"modifiers":[],"rendered":"о","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":7,"language":"uk","text":"Дніпро "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"Дніпро "},"groupID":"dev-grp-066","id":"dev-preserve-066","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"apostrophe","events":[{"keyCode":8,"modifiers":[],"rendered":"с","type":"key"},{"keyCode":11,"modifiers":[],"rendered":"і","type":"key"},{"keyCode":9,"modifiers":[],"rendered":"м","type":"key"},{"keyCode":50,"modifiers":[],"rendered":"'","type":"key"},{"keyCode":6,"modifiers":[],"rendered":"я","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":6,"language":"uk","text":"сім'я "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"сім'я "},"groupID":"dev-grp-067","id":"dev-preserve-067","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"apostrophe","events":[{"keyCode":9,"modifiers":[],"rendered":"м","type":"key"},{"keyCode":50,"modifiers":[],"rendered":"'","type":"key"},{"keyCode":6,"modifiers":[],"rendered":"я","type":"key"},{"keyCode":8,"modifiers":[],"rendered":"с","type":"key"},{"keyCode":38,"modifiers":[],"rendered":"о","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":6,"language":"uk","text":"м'ясо "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"м'ясо "},"groupID":"dev-grp-068","id":"dev-preserve-068","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"apostrophe","events":[{"keyCode":2,"modifiers":[],"rendered":"в","type":"key"},{"keyCode":50,"modifiers":[],"rendered":"'","type":"key"},{"keyCode":6,"modifiers":[],"rendered":"я","type":"key"},{"keyCode":35,"modifiers":[],"rendered":"з","type":"key"},{"keyCode":3,"modifiers":[],"rendered":"а","type":"key"},{"keyCode":45,"modifiers":[],"rendered":"т","type":"key"},{"keyCode":1,"modifiers":[],"rendered":"и","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":8,"language":"uk","text":"в'язати "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"в'язати "},"groupID":"dev-grp-069","id":"dev-preserve-069","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"apostrophe","events":[{"keyCode":15,"modifiers":[],"rendered":"к","type":"key"},{"keyCode":38,"modifiers":[],"rendered":"о","type":"key"},{"keyCode":9,"modifiers":[],"rendered":"м","type":"key"},{"keyCode":5,"modifiers":[],"rendered":"п","type":"key"},{"keyCode":50,"modifiers":[],"rendered":"'","type":"key"},{"keyCode":47,"modifiers":[],"rendered":"ю","type":"key"},{"keyCode":45,"modifiers":[],"rendered":"т","type":"key"},{"keyCode":17,"modifiers":[],"rendered":"е","type":"key"},{"keyCode":4,"modifiers":[],"rendered":"р","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":10,"language":"uk","text":"комп'ютер "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"комп'ютер "},"groupID":"dev-grp-070","id":"dev-preserve-070","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"acronym","events":[{"keyCode":16,"modifiers":["shift"],"rendered":"Н","type":"key"},{"keyCode":3,"modifiers":["shift"],"rendered":"А","type":"key"},{"keyCode":45,"modifiers":["shift"],"rendered":"Т","type":"key"},{"keyCode":38,"modifiers":["shift"],"rendered":"О","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":5,"language":"uk","text":"НАТО "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"НАТО "},"groupID":"dev-grp-071","id":"dev-preserve-071","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"acronym","events":[{"keyCode":38,"modifiers":["shift"],"rendered":"О","type":"key"},{"keyCode":38,"modifiers":["shift"],"rendered":"О","type":"key"},{"keyCode":16,"modifiers":["shift"],"rendered":"Н","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":4,"language":"uk","text":"ООН "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"ООН "},"groupID":"dev-grp-072","id":"dev-preserve-072","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"acronym","events":[{"keyCode":8,"modifiers":["shift"],"rendered":"С","type":"key"},{"keyCode":34,"modifiers":["shift"],"rendered":"Ш","type":"key"},{"keyCode":3,"modifiers":["shift"],"rendered":"А","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":4,"language":"uk","text":"США "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"США "},"groupID":"dev-grp-073","id":"dev-preserve-073","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"acronym","events":[{"keyCode":16,"modifiers":["shift"],"rendered":"Н","type":"key"},{"keyCode":43,"modifiers":["shift"],"rendered":"Б","type":"key"},{"keyCode":14,"modifiers":["shift"],"rendered":"У","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":4,"language":"uk","text":"НБУ "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"НБУ "},"groupID":"dev-grp-074","id":"dev-preserve-074","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"punctuation","events":[{"keyCode":37,"modifiers":[],"rendered":"д","type":"key"},{"keyCode":38,"modifiers":[],"rendered":"о","type":"key"},{"keyCode":43,"modifiers":[],"rendered":"б","type":"key"},{"keyCode":4,"modifiers":[],"rendered":"р","type":"key"},{"keyCode":17,"modifiers":[],"rendered":"е","type":"key"},{"keyCode":18,"modifiers":["shift"],"rendered":"!","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":7,"language":"uk","text":"добре! "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"добре! "},"groupID":"dev-grp-075","id":"dev-preserve-075","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"punctuation","events":[{"keyCode":25,"modifiers":["shift"],"rendered":"(","type":"key"},{"keyCode":45,"modifiers":[],"rendered":"т","type":"key"},{"keyCode":3,"modifiers":[],"rendered":"а","type":"key"},{"keyCode":15,"modifiers":[],"rendered":"к","type":"key"},{"keyCode":29,"modifiers":["shift"],"rendered":")","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":6,"language":"uk","text":"(так) "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"(так) "},"groupID":"dev-grp-076","id":"dev-preserve-076","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"punctuation","events":[{"keyCode":8,"modifiers":[],"rendered":"с","type":"key"},{"keyCode":40,"modifiers":[],"rendered":"л","type":"key"},{"keyCode":38,"modifiers":[],"rendered":"о","type":"key"},{"keyCode":2,"modifiers":[],"rendered":"в","type":"key"},{"keyCode":38,"modifiers":[],"rendered":"о","type":"key"},{"keyCode":23,"modifiers":["shift"],"rendered":":","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":7,"language":"uk","text":"слово: "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"слово: "},"groupID":"dev-grp-077","id":"dev-preserve-077","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"symbol","events":[{"keyCode":18,"modifiers":[],"rendered":"1","type":"key"},{"keyCode":29,"modifiers":[],"rendered":"0","type":"key"},{"keyCode":29,"modifiers":[],"rendered":"0","type":"key"},{"keyCode":21,"modifiers":["shift"],"rendered":"%","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":5,"language":"uk","text":"100% "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"100% "},"groupID":"dev-grp-078","id":"dev-preserve-078","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"phrase","events":[{"keyCode":37,"modifiers":[],"rendered":"д","type":"key"},{"keyCode":38,"modifiers":[],"rendered":"о","type":"key"},{"keyCode":43,"modifiers":[],"rendered":"б","type":"key"},{"keyCode":4,"modifiers":[],"rendered":"р","type":"key"},{"keyCode":1,"modifiers":[],"rendered":"и","type":"key"},{"keyCode":12,"modifiers":[],"rendered":"й","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"},{"keyCode":4,"modifiers":[],"rendered":"р","type":"key"},{"keyCode":3,"modifiers":[],"rendered":"а","type":"key"},{"keyCode":16,"modifiers":[],"rendered":"н","type":"key"},{"keyCode":38,"modifiers":[],"rendered":"о","type":"key"},{"keyCode":15,"modifiers":[],"rendered":"к","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":13,"language":"uk","text":"добрий ранок "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"добрий ранок "},"groupID":"dev-grp-079","id":"dev-preserve-079","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"phrase","events":[{"keyCode":32,"modifiers":[],"rendered":"г","type":"key"},{"keyCode":3,"modifiers":[],"rendered":"а","type":"key"},{"keyCode":4,"modifiers":[],"rendered":"р","type":"key"},{"keyCode":16,"modifiers":[],"rendered":"н","type":"key"},{"keyCode":38,"modifiers":[],"rendered":"о","type":"key"},{"keyCode":32,"modifiers":[],"rendered":"г","type":"key"},{"keyCode":38,"modifiers":[],"rendered":"о","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"},{"keyCode":37,"modifiers":[],"rendered":"д","type":"key"},{"keyCode":16,"modifiers":[],"rendered":"н","type":"key"},{"keyCode":6,"modifiers":[],"rendered":"я","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":12,"language":"uk","text":"гарного дня "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"гарного дня "},"groupID":"dev-grp-080","id":"dev-preserve-080","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"ambiguous-short","events":[{"keyCode":17,"modifiers":[],"rendered":"t","type":"key"},{"keyCode":31,"modifiers":[],"rendered":"o","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":3,"language":"en","text":"to "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"to "},"groupID":"dev-grp-081","id":"dev-abstain-081","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"abstain","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"ambiguous-short","events":[{"keyCode":46,"modifiers":[],"rendered":"m","type":"key"},{"keyCode":14,"modifiers":[],"rendered":"e","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":3,"language":"en","text":"me "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"me "},"groupID":"dev-grp-082","id":"dev-abstain-082","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"abstain","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"ambiguous-short","events":[{"keyCode":2,"modifiers":[],"rendered":"d","type":"key"},{"keyCode":31,"modifiers":[],"rendered":"o","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":3,"language":"en","text":"do "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"do "},"groupID":"dev-grp-083","id":"dev-abstain-083","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"abstain","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"ambiguous-short","events":[{"keyCode":45,"modifiers":[],"rendered":"n","type":"key"},{"keyCode":31,"modifiers":[],"rendered":"o","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":3,"language":"en","text":"no "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"no "},"groupID":"dev-grp-084","id":"dev-abstain-084","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"abstain","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"ambiguous-short","events":[{"keyCode":31,"modifiers":[],"rendered":"o","type":"key"},{"keyCode":15,"modifiers":[],"rendered":"r","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":3,"language":"en","text":"or "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"or "},"groupID":"dev-grp-085","id":"dev-abstain-085","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"abstain","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"gibberish","events":[{"keyCode":0,"modifiers":[],"rendered":"a","type":"key"},{"keyCode":1,"modifiers":[],"rendered":"s","type":"key"},{"keyCode":2,"modifiers":[],"rendered":"d","type":"key"},{"keyCode":3,"modifiers":[],"rendered":"f","type":"key"},{"keyCode":5,"modifiers":[],"rendered":"g","type":"key"},{"keyCode":4,"modifiers":[],"rendered":"h","type":"key"},{"keyCode":38,"modifiers":[],"rendered":"j","type":"key"},{"keyCode":40,"modifiers":[],"rendered":"k","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":9,"language":"en","text":"asdfghjk "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"asdfghjk "},"groupID":"dev-grp-086","id":"dev-abstain-086","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"abstain","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"gibberish","events":[{"keyCode":12,"modifiers":[],"rendered":"q","type":"key"},{"keyCode":13,"modifiers":[],"rendered":"w","type":"key"},{"keyCode":14,"modifiers":[],"rendered":"e","type":"key"},{"keyCode":15,"modifiers":[],"rendered":"r","type":"key"},{"keyCode":17,"modifiers":[],"rendered":"t","type":"key"},{"keyCode":16,"modifiers":[],"rendered":"y","type":"key"},{"keyCode":32,"modifiers":[],"rendered":"u","type":"key"},{"keyCode":34,"modifiers":[],"rendered":"i","type":"key"},{"keyCode":31,"modifiers":[],"rendered":"o","type":"key"},{"keyCode":35,"modifiers":[],"rendered":"p","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":11,"language":"en","text":"qwertyuiop "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"qwertyuiop "},"groupID":"dev-grp-087","id":"dev-abstain-087","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"abstain","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"isolated-char","events":[{"keyCode":0,"modifiers":[],"rendered":"a","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":2,"language":"en","text":"a "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"a "},"groupID":"dev-grp-088","id":"dev-abstain-088","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"abstain","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"isolated-char","events":[{"keyCode":7,"modifiers":[],"rendered":"x","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":2,"language":"en","text":"x "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"x "},"groupID":"dev-grp-089","id":"dev-abstain-089","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"abstain","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"isolated-symbol","events":[{"keyCode":41,"modifiers":["shift"],"rendered":":","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":2,"language":"en","text":": "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":": "},"groupID":"dev-grp-090","id":"dev-abstain-090","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"abstain","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"ambiguous-short","events":[{"keyCode":45,"modifiers":[],"rendered":"т","type":"key"},{"keyCode":38,"modifiers":[],"rendered":"о","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":3,"language":"uk","text":"то "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"то "},"groupID":"dev-grp-091","id":"dev-abstain-091","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"abstain","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"ambiguous-short","events":[{"keyCode":16,"modifiers":[],"rendered":"н","type":"key"},{"keyCode":17,"modifiers":[],"rendered":"е","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":3,"language":"uk","text":"не "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"не "},"groupID":"dev-grp-092","id":"dev-abstain-092","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"abstain","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"ambiguous-short","events":[{"keyCode":7,"modifiers":[],"rendered":"ч","type":"key"},{"keyCode":1,"modifiers":[],"rendered":"и","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":3,"language":"uk","text":"чи "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"чи "},"groupID":"dev-grp-093","id":"dev-abstain-093","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"abstain","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"ambiguous-short","events":[{"keyCode":35,"modifiers":[],"rendered":"з","type":"key"},{"keyCode":3,"modifiers":[],"rendered":"а","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":3,"language":"uk","text":"за "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"за "},"groupID":"dev-grp-094","id":"dev-abstain-094","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"abstain","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"ambiguous-short","events":[{"keyCode":6,"modifiers":[],"rendered":"я","type":"key"},{"keyCode":15,"modifiers":[],"rendered":"к","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":3,"language":"uk","text":"як "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"як "},"groupID":"dev-grp-095","id":"dev-abstain-095","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"abstain","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"gibberish","events":[{"keyCode":12,"modifiers":[],"rendered":"й","type":"key"},{"keyCode":13,"modifiers":[],"rendered":"ц","type":"key"},{"keyCode":14,"modifiers":[],"rendered":"у","type":"key"},{"keyCode":15,"modifiers":[],"rendered":"к","type":"key"},{"keyCode":17,"modifiers":[],"rendered":"е","type":"key"},{"keyCode":16,"modifiers":[],"rendered":"н","type":"key"},{"keyCode":32,"modifiers":[],"rendered":"г","type":"key"},{"keyCode":34,"modifiers":[],"rendered":"ш","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":9,"language":"uk","text":"йцукенгш "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"йцукенгш "},"groupID":"dev-grp-096","id":"dev-abstain-096","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"abstain","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"gibberish","events":[{"keyCode":0,"modifiers":[],"rendered":"ф","type":"key"},{"keyCode":11,"modifiers":[],"rendered":"і","type":"key"},{"keyCode":2,"modifiers":[],"rendered":"в","type":"key"},{"keyCode":3,"modifiers":[],"rendered":"а","type":"key"},{"keyCode":5,"modifiers":[],"rendered":"п","type":"key"},{"keyCode":4,"modifiers":[],"rendered":"р","type":"key"},{"keyCode":38,"modifiers":[],"rendered":"о","type":"key"},{"keyCode":40,"modifiers":[],"rendered":"л","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":9,"language":"uk","text":"фівапрол "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"фівапрол "},"groupID":"dev-grp-097","id":"dev-abstain-097","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"abstain","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"isolated-char","events":[{"keyCode":6,"modifiers":[],"rendered":"я","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":2,"language":"uk","text":"я "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"я "},"groupID":"dev-grp-098","id":"dev-abstain-098","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"abstain","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"isolated-symbol","events":[{"keyCode":28,"modifiers":["shift"],"rendered":";","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":2,"language":"uk","text":"; "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"; "},"groupID":"dev-grp-099","id":"dev-abstain-099","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"abstain","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"symbol","events":[{"keyCode":27,"modifiers":[],"rendered":"-","type":"key"},{"keyCode":27,"modifiers":[],"rendered":"-","type":"key"},{"keyCode":27,"modifiers":[],"rendered":"-","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":4,"language":"uk","text":"--- "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"--- "},"groupID":"dev-grp-100","id":"dev-abstain-100","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"abstain","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"cross-layout-collision","events":[{"keyCode":9,"modifiers":[],"rendered":"v","type":"key"},{"keyCode":1,"modifiers":[],"rendered":"s","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":3,"language":"en","text":"vs "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"vs "},"groupID":"dev-grp-101","id":"dev-preserve-101","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"cross-layout-collision","events":[{"keyCode":16,"modifiers":[],"rendered":"y","type":"key"},{"keyCode":17,"modifiers":[],"rendered":"t","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":3,"language":"en","text":"yt "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"yt "},"groupID":"dev-grp-102","id":"dev-preserve-102","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"cross-layout-collision","events":[{"keyCode":45,"modifiers":[],"rendered":"n","type":"key"},{"keyCode":3,"modifiers":[],"rendered":"f","type":"key"},{"keyCode":15,"modifiers":[],"rendered":"r","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":4,"language":"en","text":"nfr "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"nfr "},"groupID":"dev-grp-103","id":"dev-preserve-103","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"cross-layout-collision","events":[{"keyCode":32,"modifiers":[],"rendered":"u","type":"key"},{"keyCode":4,"modifiers":[],"rendered":"h","type":"key"},{"keyCode":3,"modifiers":[],"rendered":"f","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":4,"language":"en","text":"uhf "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"uhf "},"groupID":"dev-grp-104","id":"dev-preserve-104","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"cross-layout-collision","events":[{"keyCode":4,"modifiers":[],"rendered":"h","type":"key"},{"keyCode":11,"modifiers":[],"rendered":"b","type":"key"},{"keyCode":15,"modifiers":[],"rendered":"r","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":4,"language":"en","text":"hbr "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"hbr "},"groupID":"dev-grp-105","id":"dev-preserve-105","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"cross-layout-collision","events":[{"keyCode":4,"modifiers":[],"rendered":"р","type":"key"},{"keyCode":14,"modifiers":[],"rendered":"у","type":"key"},{"keyCode":15,"modifiers":[],"rendered":"к","type":"key"},{"keyCode":14,"modifiers":[],"rendered":"у","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":5,"language":"uk","text":"руку "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"руку "},"groupID":"dev-grp-106","id":"dev-preserve-106","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"cross-layout-collision","events":[{"keyCode":4,"modifiers":[],"rendered":"р","type":"key"},{"keyCode":14,"modifiers":[],"rendered":"у","type":"key"},{"keyCode":15,"modifiers":[],"rendered":"к","type":"key"},{"keyCode":1,"modifiers":[],"rendered":"и","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":5,"language":"uk","text":"руки "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"руки "},"groupID":"dev-grp-106","id":"dev-preserve-107","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"cross-layout-collision","events":[{"keyCode":4,"modifiers":[],"rendered":"р","type":"key"},{"keyCode":14,"modifiers":[],"rendered":"у","type":"key"},{"keyCode":15,"modifiers":[],"rendered":"к","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":4,"language":"uk","text":"рук "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"рук "},"groupID":"dev-grp-106","id":"dev-preserve-108","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"cross-layout-collision","events":[{"keyCode":15,"modifiers":[],"rendered":"к","type":"key"},{"keyCode":14,"modifiers":[],"rendered":"у","type":"key"},{"keyCode":9,"modifiers":[],"rendered":"м","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":4,"language":"uk","text":"кум "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"кум "},"groupID":"dev-grp-107","id":"dev-preserve-109","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} +{"category":"cross-layout-collision","events":[{"keyCode":16,"modifiers":[],"rendered":"н","type":"key"},{"keyCode":14,"modifiers":[],"rendered":"у","type":"key"},{"keyCode":45,"modifiers":[],"rendered":"т","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":4,"language":"uk","text":"нут "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"нут "},"groupID":"dev-grp-108","id":"dev-preserve-110","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"development"} diff --git a/Benchmarks/layout-corpus/v2/holdout.jsonl b/Benchmarks/layout-corpus/v2/holdout.jsonl new file mode 100644 index 0000000..094b6f3 --- /dev/null +++ b/Benchmarks/layout-corpus/v2/holdout.jsonl @@ -0,0 +1,110 @@ +{"category":"single-word","events":[{"keyCode":9,"modifiers":[],"rendered":"v","type":"key"},{"keyCode":38,"modifiers":[],"rendered":"j","type":"key"},{"keyCode":2,"modifiers":[],"rendered":"d","type":"key"},{"keyCode":3,"modifiers":[],"rendered":"f","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":["replaceSuffix","switchSource"],"checkpoints":[{"afterEvent":5,"language":"uk","text":"мова "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"мова "},"groupID":"holdout-grp-001","id":"holdout-repair-001","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"repair","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"capitalized","events":[{"keyCode":14,"modifiers":["shift"],"rendered":"E","type":"key"},{"keyCode":15,"modifiers":[],"rendered":"r","type":"key"},{"keyCode":4,"modifiers":[],"rendered":"h","type":"key"},{"keyCode":3,"modifiers":[],"rendered":"f","type":"key"},{"keyCode":30,"modifiers":[],"rendered":"]","type":"key"},{"keyCode":16,"modifiers":[],"rendered":"y","type":"key"},{"keyCode":3,"modifiers":[],"rendered":"f","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":["replaceSuffix","switchSource"],"checkpoints":[{"afterEvent":8,"language":"uk","text":"Україна "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"Україна "},"groupID":"holdout-grp-002","id":"holdout-repair-002","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"repair","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"single-word","events":[{"keyCode":37,"modifiers":[],"rendered":"l","type":"key"},{"keyCode":6,"modifiers":[],"rendered":"z","type":"key"},{"keyCode":15,"modifiers":[],"rendered":"r","type":"key"},{"keyCode":14,"modifiers":[],"rendered":"e","type":"key"},{"keyCode":39,"modifiers":[],"rendered":"'","type":"key"},{"keyCode":9,"modifiers":[],"rendered":"v","type":"key"},{"keyCode":38,"modifiers":[],"rendered":"j","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":["replaceSuffix","switchSource"],"checkpoints":[{"afterEvent":8,"language":"uk","text":"дякуємо "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"дякуємо "},"groupID":"holdout-grp-003","id":"holdout-repair-003","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"repair","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"back-to-back","events":[{"keyCode":16,"modifiers":[],"rendered":"y","type":"key"},{"keyCode":38,"modifiers":[],"rendered":"j","type":"key"},{"keyCode":2,"modifiers":[],"rendered":"d","type":"key"},{"keyCode":1,"modifiers":[],"rendered":"s","type":"key"},{"keyCode":12,"modifiers":[],"rendered":"q","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"},{"keyCode":37,"modifiers":[],"rendered":"l","type":"key"},{"keyCode":17,"modifiers":[],"rendered":"t","type":"key"},{"keyCode":16,"modifiers":[],"rendered":"y","type":"key"},{"keyCode":46,"modifiers":[],"rendered":"m","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":["replaceSuffix","switchSource"],"checkpoints":[{"afterEvent":11,"language":"uk","text":"новий день "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"новий день "},"groupID":"holdout-grp-004","id":"holdout-repair-004","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"repair","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"single-word","events":[{"keyCode":15,"modifiers":[],"rendered":"r","type":"key"},{"keyCode":38,"modifiers":[],"rendered":"j","type":"key"},{"keyCode":4,"modifiers":[],"rendered":"h","type":"key"},{"keyCode":1,"modifiers":[],"rendered":"s","type":"key"},{"keyCode":8,"modifiers":[],"rendered":"c","type":"key"},{"keyCode":45,"modifiers":[],"rendered":"n","type":"key"},{"keyCode":14,"modifiers":[],"rendered":"e","type":"key"},{"keyCode":2,"modifiers":[],"rendered":"d","type":"key"},{"keyCode":3,"modifiers":[],"rendered":"f","type":"key"},{"keyCode":7,"modifiers":[],"rendered":"x","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":["replaceSuffix","switchSource"],"checkpoints":[{"afterEvent":11,"language":"uk","text":"користувач "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"користувач "},"groupID":"holdout-grp-005","id":"holdout-repair-005","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"repair","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"single-word","events":[{"keyCode":37,"modifiers":[],"rendered":"l","type":"key"},{"keyCode":38,"modifiers":[],"rendered":"j","type":"key"},{"keyCode":15,"modifiers":[],"rendered":"r","type":"key"},{"keyCode":14,"modifiers":[],"rendered":"e","type":"key"},{"keyCode":9,"modifiers":[],"rendered":"v","type":"key"},{"keyCode":17,"modifiers":[],"rendered":"t","type":"key"},{"keyCode":16,"modifiers":[],"rendered":"y","type":"key"},{"keyCode":45,"modifiers":[],"rendered":"n","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":["replaceSuffix","switchSource"],"checkpoints":[{"afterEvent":9,"language":"uk","text":"документ "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"документ "},"groupID":"holdout-grp-006","id":"holdout-repair-006","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"repair","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"single-word","events":[{"keyCode":9,"modifiers":[],"rendered":"v","type":"key"},{"keyCode":38,"modifiers":[],"rendered":"j","type":"key"},{"keyCode":41,"modifiers":[],"rendered":";","type":"key"},{"keyCode":40,"modifiers":[],"rendered":"k","type":"key"},{"keyCode":1,"modifiers":[],"rendered":"s","type":"key"},{"keyCode":2,"modifiers":[],"rendered":"d","type":"key"},{"keyCode":38,"modifiers":[],"rendered":"j","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":["replaceSuffix","switchSource"],"checkpoints":[{"afterEvent":8,"language":"uk","text":"можливо "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"можливо "},"groupID":"holdout-grp-007","id":"holdout-repair-007","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"repair","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"single-word","events":[{"keyCode":43,"modifiers":[],"rendered":",","type":"key"},{"keyCode":3,"modifiers":[],"rendered":"f","type":"key"},{"keyCode":7,"modifiers":[],"rendered":"x","type":"key"},{"keyCode":1,"modifiers":[],"rendered":"s","type":"key"},{"keyCode":9,"modifiers":[],"rendered":"v","type":"key"},{"keyCode":38,"modifiers":[],"rendered":"j","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":["replaceSuffix","switchSource"],"checkpoints":[{"afterEvent":7,"language":"uk","text":"бачимо "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"бачимо "},"groupID":"holdout-grp-008","id":"holdout-repair-008","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"repair","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"single-word","events":[{"keyCode":2,"modifiers":[],"rendered":"d","type":"key"},{"keyCode":17,"modifiers":[],"rendered":"t","type":"key"},{"keyCode":7,"modifiers":[],"rendered":"x","type":"key"},{"keyCode":11,"modifiers":[],"rendered":"b","type":"key"},{"keyCode":4,"modifiers":[],"rendered":"h","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":["replaceSuffix","switchSource"],"checkpoints":[{"afterEvent":6,"language":"uk","text":"вечір "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"вечір "},"groupID":"holdout-grp-009","id":"holdout-repair-009","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"repair","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"question","events":[{"keyCode":37,"modifiers":[],"rendered":"l","type":"key"},{"keyCode":17,"modifiers":[],"rendered":"t","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"},{"keyCode":45,"modifiers":[],"rendered":"n","type":"key"},{"keyCode":1,"modifiers":[],"rendered":"s","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":["replaceSuffix","switchSource"],"checkpoints":[{"afterEvent":6,"language":"uk","text":"де ти "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"де ти "},"groupID":"holdout-grp-010","id":"holdout-repair-010","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"repair","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"single-word","events":[{"keyCode":5,"modifiers":[],"rendered":"п","type":"key"},{"keyCode":0,"modifiers":[],"rendered":"ф","type":"key"},{"keyCode":15,"modifiers":[],"rendered":"к","type":"key"},{"keyCode":2,"modifiers":[],"rendered":"в","type":"key"},{"keyCode":14,"modifiers":[],"rendered":"у","type":"key"},{"keyCode":45,"modifiers":[],"rendered":"т","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":["replaceSuffix","switchSource"],"checkpoints":[{"afterEvent":7,"language":"en","text":"garden "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"garden "},"groupID":"holdout-grp-011-v2","id":"holdout-repair-011-v2","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"repair","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"capitalized","events":[{"keyCode":8,"modifiers":["shift"],"rendered":"С","type":"key"},{"keyCode":0,"modifiers":[],"rendered":"ф","type":"key"},{"keyCode":45,"modifiers":[],"rendered":"т","type":"key"},{"keyCode":0,"modifiers":[],"rendered":"ф","type":"key"},{"keyCode":2,"modifiers":[],"rendered":"в","type":"key"},{"keyCode":0,"modifiers":[],"rendered":"ф","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":["replaceSuffix","switchSource"],"checkpoints":[{"afterEvent":7,"language":"en","text":"Canada "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"Canada "},"groupID":"holdout-grp-012","id":"holdout-repair-012","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"repair","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"single-word","events":[{"keyCode":13,"modifiers":[],"rendered":"ц","type":"key"},{"keyCode":14,"modifiers":[],"rendered":"у","type":"key"},{"keyCode":37,"modifiers":[],"rendered":"д","type":"key"},{"keyCode":8,"modifiers":[],"rendered":"с","type":"key"},{"keyCode":31,"modifiers":[],"rendered":"щ","type":"key"},{"keyCode":46,"modifiers":[],"rendered":"ь","type":"key"},{"keyCode":14,"modifiers":[],"rendered":"у","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":["replaceSuffix","switchSource"],"checkpoints":[{"afterEvent":8,"language":"en","text":"welcome "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"welcome "},"groupID":"holdout-grp-013","id":"holdout-repair-013","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"repair","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"back-to-back","events":[{"keyCode":45,"modifiers":[],"rendered":"т","type":"key"},{"keyCode":14,"modifiers":[],"rendered":"у","type":"key"},{"keyCode":13,"modifiers":[],"rendered":"ц","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"},{"keyCode":3,"modifiers":[],"rendered":"а","type":"key"},{"keyCode":34,"modifiers":[],"rendered":"ш","type":"key"},{"keyCode":37,"modifiers":[],"rendered":"д","type":"key"},{"keyCode":14,"modifiers":[],"rendered":"у","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":["replaceSuffix","switchSource"],"checkpoints":[{"afterEvent":9,"language":"en","text":"new file "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"new file "},"groupID":"holdout-grp-014","id":"holdout-repair-014","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"repair","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"single-word","events":[{"keyCode":11,"modifiers":[],"rendered":"і","type":"key"},{"keyCode":15,"modifiers":[],"rendered":"к","type":"key"},{"keyCode":31,"modifiers":[],"rendered":"щ","type":"key"},{"keyCode":13,"modifiers":[],"rendered":"ц","type":"key"},{"keyCode":1,"modifiers":[],"rendered":"и","type":"key"},{"keyCode":14,"modifiers":[],"rendered":"у","type":"key"},{"keyCode":15,"modifiers":[],"rendered":"к","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":["replaceSuffix","switchSource"],"checkpoints":[{"afterEvent":8,"language":"en","text":"browser "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"browser "},"groupID":"holdout-grp-015","id":"holdout-repair-015","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"repair","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"single-word","events":[{"keyCode":2,"modifiers":[],"rendered":"в","type":"key"},{"keyCode":31,"modifiers":[],"rendered":"щ","type":"key"},{"keyCode":8,"modifiers":[],"rendered":"с","type":"key"},{"keyCode":32,"modifiers":[],"rendered":"г","type":"key"},{"keyCode":46,"modifiers":[],"rendered":"ь","type":"key"},{"keyCode":14,"modifiers":[],"rendered":"у","type":"key"},{"keyCode":45,"modifiers":[],"rendered":"т","type":"key"},{"keyCode":17,"modifiers":[],"rendered":"е","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":["replaceSuffix","switchSource"],"checkpoints":[{"afterEvent":9,"language":"en","text":"document "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"document "},"groupID":"holdout-grp-016","id":"holdout-repair-016","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"repair","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"single-word","events":[{"keyCode":35,"modifiers":[],"rendered":"з","type":"key"},{"keyCode":14,"modifiers":[],"rendered":"у","type":"key"},{"keyCode":15,"modifiers":[],"rendered":"к","type":"key"},{"keyCode":4,"modifiers":[],"rendered":"р","type":"key"},{"keyCode":0,"modifiers":[],"rendered":"ф","type":"key"},{"keyCode":35,"modifiers":[],"rendered":"з","type":"key"},{"keyCode":1,"modifiers":[],"rendered":"и","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":["replaceSuffix","switchSource"],"checkpoints":[{"afterEvent":8,"language":"en","text":"perhaps "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"perhaps "},"groupID":"holdout-grp-017","id":"holdout-repair-017","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"repair","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"single-word","events":[{"keyCode":37,"modifiers":[],"rendered":"д","type":"key"},{"keyCode":31,"modifiers":[],"rendered":"щ","type":"key"},{"keyCode":31,"modifiers":[],"rendered":"щ","type":"key"},{"keyCode":40,"modifiers":[],"rendered":"л","type":"key"},{"keyCode":34,"modifiers":[],"rendered":"ш","type":"key"},{"keyCode":45,"modifiers":[],"rendered":"т","type":"key"},{"keyCode":5,"modifiers":[],"rendered":"п","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":["replaceSuffix","switchSource"],"checkpoints":[{"afterEvent":8,"language":"en","text":"looking "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"looking "},"groupID":"holdout-grp-018","id":"holdout-repair-018","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"repair","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"single-word","events":[{"keyCode":14,"modifiers":[],"rendered":"у","type":"key"},{"keyCode":9,"modifiers":[],"rendered":"м","type":"key"},{"keyCode":14,"modifiers":[],"rendered":"у","type":"key"},{"keyCode":45,"modifiers":[],"rendered":"т","type":"key"},{"keyCode":34,"modifiers":[],"rendered":"ш","type":"key"},{"keyCode":45,"modifiers":[],"rendered":"т","type":"key"},{"keyCode":5,"modifiers":[],"rendered":"п","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":["replaceSuffix","switchSource"],"checkpoints":[{"afterEvent":8,"language":"en","text":"evening "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"evening "},"groupID":"holdout-grp-019","id":"holdout-repair-019","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"repair","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"question","events":[{"keyCode":13,"modifiers":[],"rendered":"ц","type":"key"},{"keyCode":4,"modifiers":[],"rendered":"р","type":"key"},{"keyCode":14,"modifiers":[],"rendered":"у","type":"key"},{"keyCode":15,"modifiers":[],"rendered":"к","type":"key"},{"keyCode":14,"modifiers":[],"rendered":"у","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"},{"keyCode":0,"modifiers":[],"rendered":"ф","type":"key"},{"keyCode":15,"modifiers":[],"rendered":"к","type":"key"},{"keyCode":14,"modifiers":[],"rendered":"у","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"},{"keyCode":16,"modifiers":[],"rendered":"н","type":"key"},{"keyCode":31,"modifiers":[],"rendered":"щ","type":"key"},{"keyCode":32,"modifiers":[],"rendered":"г","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":["replaceSuffix","switchSource"],"checkpoints":[{"afterEvent":14,"language":"en","text":"where are you "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"where are you "},"groupID":"holdout-grp-020","id":"holdout-repair-020","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"repair","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"natural-word","events":[{"keyCode":31,"modifiers":[],"rendered":"o","type":"key"},{"keyCode":15,"modifiers":[],"rendered":"r","type":"key"},{"keyCode":0,"modifiers":[],"rendered":"a","type":"key"},{"keyCode":45,"modifiers":[],"rendered":"n","type":"key"},{"keyCode":5,"modifiers":[],"rendered":"g","type":"key"},{"keyCode":14,"modifiers":[],"rendered":"e","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":7,"language":"en","text":"orange "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"orange "},"groupID":"holdout-grp-021","id":"holdout-preserve-021","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"natural-word","events":[{"keyCode":35,"modifiers":[],"rendered":"p","type":"key"},{"keyCode":16,"modifiers":[],"rendered":"y","type":"key"},{"keyCode":17,"modifiers":[],"rendered":"t","type":"key"},{"keyCode":4,"modifiers":[],"rendered":"h","type":"key"},{"keyCode":31,"modifiers":[],"rendered":"o","type":"key"},{"keyCode":45,"modifiers":[],"rendered":"n","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":7,"language":"en","text":"python "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"python "},"groupID":"holdout-grp-022","id":"holdout-preserve-022","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"natural-word","events":[{"keyCode":0,"modifiers":[],"rendered":"a","type":"key"},{"keyCode":15,"modifiers":[],"rendered":"r","type":"key"},{"keyCode":8,"modifiers":[],"rendered":"c","type":"key"},{"keyCode":4,"modifiers":[],"rendered":"h","type":"key"},{"keyCode":34,"modifiers":[],"rendered":"i","type":"key"},{"keyCode":17,"modifiers":[],"rendered":"t","type":"key"},{"keyCode":14,"modifiers":[],"rendered":"e","type":"key"},{"keyCode":8,"modifiers":[],"rendered":"c","type":"key"},{"keyCode":17,"modifiers":[],"rendered":"t","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":10,"language":"en","text":"architect "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"architect "},"groupID":"holdout-grp-023","id":"holdout-preserve-023","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"natural-word","events":[{"keyCode":8,"modifiers":[],"rendered":"c","type":"key"},{"keyCode":31,"modifiers":[],"rendered":"o","type":"key"},{"keyCode":45,"modifiers":[],"rendered":"n","type":"key"},{"keyCode":1,"modifiers":[],"rendered":"s","type":"key"},{"keyCode":31,"modifiers":[],"rendered":"o","type":"key"},{"keyCode":37,"modifiers":[],"rendered":"l","type":"key"},{"keyCode":14,"modifiers":[],"rendered":"e","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":8,"language":"en","text":"console "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"console "},"groupID":"holdout-grp-024","id":"holdout-preserve-024","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"natural-word","events":[{"keyCode":32,"modifiers":[],"rendered":"u","type":"key"},{"keyCode":35,"modifiers":[],"rendered":"p","type":"key"},{"keyCode":2,"modifiers":[],"rendered":"d","type":"key"},{"keyCode":0,"modifiers":[],"rendered":"a","type":"key"},{"keyCode":17,"modifiers":[],"rendered":"t","type":"key"},{"keyCode":14,"modifiers":[],"rendered":"e","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":7,"language":"en","text":"update "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"update "},"groupID":"holdout-grp-025","id":"holdout-preserve-025","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"natural-word","events":[{"keyCode":2,"modifiers":[],"rendered":"d","type":"key"},{"keyCode":34,"modifiers":[],"rendered":"i","type":"key"},{"keyCode":1,"modifiers":[],"rendered":"s","type":"key"},{"keyCode":35,"modifiers":[],"rendered":"p","type":"key"},{"keyCode":37,"modifiers":[],"rendered":"l","type":"key"},{"keyCode":0,"modifiers":[],"rendered":"a","type":"key"},{"keyCode":16,"modifiers":[],"rendered":"y","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":8,"language":"en","text":"display "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"display "},"groupID":"holdout-grp-026","id":"holdout-preserve-026","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"short-word","events":[{"keyCode":0,"modifiers":[],"rendered":"a","type":"key"},{"keyCode":45,"modifiers":[],"rendered":"n","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":3,"language":"en","text":"an "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"an "},"groupID":"holdout-grp-027","id":"holdout-preserve-027","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"short-word","events":[{"keyCode":34,"modifiers":[],"rendered":"i","type":"key"},{"keyCode":45,"modifiers":[],"rendered":"n","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":3,"language":"en","text":"in "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"in "},"groupID":"holdout-grp-028","id":"holdout-preserve-028","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"short-word","events":[{"keyCode":0,"modifiers":[],"rendered":"a","type":"key"},{"keyCode":17,"modifiers":[],"rendered":"t","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":3,"language":"en","text":"at "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"at "},"groupID":"holdout-grp-029","id":"holdout-preserve-029","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"short-word","events":[{"keyCode":1,"modifiers":[],"rendered":"s","type":"key"},{"keyCode":31,"modifiers":[],"rendered":"o","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":3,"language":"en","text":"so "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"so "},"groupID":"holdout-grp-030","id":"holdout-preserve-030","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"short-word","events":[{"keyCode":34,"modifiers":[],"rendered":"i","type":"key"},{"keyCode":3,"modifiers":[],"rendered":"f","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":3,"language":"en","text":"if "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"if "},"groupID":"holdout-grp-031","id":"holdout-preserve-031","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"short-word","events":[{"keyCode":32,"modifiers":[],"rendered":"u","type":"key"},{"keyCode":35,"modifiers":[],"rendered":"p","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":3,"language":"en","text":"up "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"up "},"groupID":"holdout-grp-032","id":"holdout-preserve-032","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"proper-noun","events":[{"keyCode":5,"modifiers":["shift"],"rendered":"G","type":"key"},{"keyCode":34,"modifiers":[],"rendered":"i","type":"key"},{"keyCode":17,"modifiers":[],"rendered":"t","type":"key"},{"keyCode":37,"modifiers":["shift"],"rendered":"L","type":"key"},{"keyCode":0,"modifiers":[],"rendered":"a","type":"key"},{"keyCode":11,"modifiers":[],"rendered":"b","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":7,"language":"en","text":"GitLab "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"GitLab "},"groupID":"holdout-grp-033","id":"holdout-preserve-033","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"proper-noun","events":[{"keyCode":37,"modifiers":["shift"],"rendered":"L","type":"key"},{"keyCode":34,"modifiers":[],"rendered":"i","type":"key"},{"keyCode":45,"modifiers":[],"rendered":"n","type":"key"},{"keyCode":32,"modifiers":[],"rendered":"u","type":"key"},{"keyCode":7,"modifiers":[],"rendered":"x","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":6,"language":"en","text":"Linux "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"Linux "},"groupID":"holdout-grp-034","id":"holdout-preserve-034","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"proper-noun","events":[{"keyCode":9,"modifiers":["shift"],"rendered":"V","type":"key"},{"keyCode":34,"modifiers":[],"rendered":"i","type":"key"},{"keyCode":1,"modifiers":[],"rendered":"s","type":"key"},{"keyCode":32,"modifiers":[],"rendered":"u","type":"key"},{"keyCode":0,"modifiers":[],"rendered":"a","type":"key"},{"keyCode":37,"modifiers":[],"rendered":"l","type":"key"},{"keyCode":8,"modifiers":["shift"],"rendered":"C","type":"key"},{"keyCode":31,"modifiers":[],"rendered":"o","type":"key"},{"keyCode":2,"modifiers":[],"rendered":"d","type":"key"},{"keyCode":14,"modifiers":[],"rendered":"e","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":11,"language":"en","text":"VisualCode "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"VisualCode "},"groupID":"holdout-grp-035","id":"holdout-preserve-035","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"proper-noun","events":[{"keyCode":34,"modifiers":[],"rendered":"i","type":"key"},{"keyCode":31,"modifiers":["shift"],"rendered":"O","type":"key"},{"keyCode":1,"modifiers":["shift"],"rendered":"S","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":4,"language":"en","text":"iOS "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"iOS "},"groupID":"holdout-grp-036","id":"holdout-preserve-036","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"code-token","events":[{"keyCode":35,"modifiers":[],"rendered":"p","type":"key"},{"keyCode":0,"modifiers":[],"rendered":"a","type":"key"},{"keyCode":1,"modifiers":[],"rendered":"s","type":"key"},{"keyCode":8,"modifiers":[],"rendered":"c","type":"key"},{"keyCode":0,"modifiers":[],"rendered":"a","type":"key"},{"keyCode":37,"modifiers":[],"rendered":"l","type":"key"},{"keyCode":8,"modifiers":["shift"],"rendered":"C","type":"key"},{"keyCode":0,"modifiers":[],"rendered":"a","type":"key"},{"keyCode":1,"modifiers":[],"rendered":"s","type":"key"},{"keyCode":14,"modifiers":[],"rendered":"e","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":11,"language":"en","text":"pascalCase "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"pascalCase "},"groupID":"holdout-grp-037","id":"holdout-preserve-037","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"code-token","events":[{"keyCode":40,"modifiers":[],"rendered":"k","type":"key"},{"keyCode":14,"modifiers":[],"rendered":"e","type":"key"},{"keyCode":11,"modifiers":[],"rendered":"b","type":"key"},{"keyCode":0,"modifiers":[],"rendered":"a","type":"key"},{"keyCode":11,"modifiers":[],"rendered":"b","type":"key"},{"keyCode":27,"modifiers":["shift"],"rendered":"_","type":"key"},{"keyCode":8,"modifiers":[],"rendered":"c","type":"key"},{"keyCode":0,"modifiers":[],"rendered":"a","type":"key"},{"keyCode":1,"modifiers":[],"rendered":"s","type":"key"},{"keyCode":14,"modifiers":[],"rendered":"e","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":11,"language":"en","text":"kebab_case "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"kebab_case "},"groupID":"holdout-grp-038","id":"holdout-preserve-038","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"code-token","events":[{"keyCode":8,"modifiers":["shift"],"rendered":"C","type":"key"},{"keyCode":3,"modifiers":["shift"],"rendered":"F","type":"key"},{"keyCode":15,"modifiers":["shift"],"rendered":"R","type":"key"},{"keyCode":14,"modifiers":[],"rendered":"e","type":"key"},{"keyCode":37,"modifiers":[],"rendered":"l","type":"key"},{"keyCode":14,"modifiers":[],"rendered":"e","type":"key"},{"keyCode":0,"modifiers":[],"rendered":"a","type":"key"},{"keyCode":1,"modifiers":[],"rendered":"s","type":"key"},{"keyCode":14,"modifiers":[],"rendered":"e","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":10,"language":"en","text":"CFRelease "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"CFRelease "},"groupID":"holdout-grp-039","id":"holdout-preserve-039","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"code-token","events":[{"keyCode":2,"modifiers":[],"rendered":"d","type":"key"},{"keyCode":14,"modifiers":[],"rendered":"e","type":"key"},{"keyCode":34,"modifiers":[],"rendered":"i","type":"key"},{"keyCode":45,"modifiers":[],"rendered":"n","type":"key"},{"keyCode":34,"modifiers":[],"rendered":"i","type":"key"},{"keyCode":17,"modifiers":[],"rendered":"t","type":"key"},{"keyCode":25,"modifiers":["shift"],"rendered":"(","type":"key"},{"keyCode":29,"modifiers":["shift"],"rendered":")","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":9,"language":"en","text":"deinit() "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"deinit() "},"groupID":"holdout-grp-040","id":"holdout-preserve-040","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"path","events":[{"keyCode":44,"modifiers":[],"rendered":"/","type":"key"},{"keyCode":14,"modifiers":[],"rendered":"e","type":"key"},{"keyCode":17,"modifiers":[],"rendered":"t","type":"key"},{"keyCode":8,"modifiers":[],"rendered":"c","type":"key"},{"keyCode":44,"modifiers":[],"rendered":"/","type":"key"},{"keyCode":4,"modifiers":[],"rendered":"h","type":"key"},{"keyCode":31,"modifiers":[],"rendered":"o","type":"key"},{"keyCode":1,"modifiers":[],"rendered":"s","type":"key"},{"keyCode":17,"modifiers":[],"rendered":"t","type":"key"},{"keyCode":1,"modifiers":[],"rendered":"s","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":11,"language":"en","text":"/etc/hosts "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"/etc/hosts "},"groupID":"holdout-grp-041","id":"holdout-preserve-041","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"filename","events":[{"keyCode":15,"modifiers":["shift"],"rendered":"R","type":"key"},{"keyCode":14,"modifiers":["shift"],"rendered":"E","type":"key"},{"keyCode":0,"modifiers":["shift"],"rendered":"A","type":"key"},{"keyCode":2,"modifiers":["shift"],"rendered":"D","type":"key"},{"keyCode":46,"modifiers":["shift"],"rendered":"M","type":"key"},{"keyCode":14,"modifiers":["shift"],"rendered":"E","type":"key"},{"keyCode":47,"modifiers":[],"rendered":".","type":"key"},{"keyCode":46,"modifiers":[],"rendered":"m","type":"key"},{"keyCode":2,"modifiers":[],"rendered":"d","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":10,"language":"en","text":"README.md "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"README.md "},"groupID":"holdout-grp-042","id":"holdout-preserve-042","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"url","events":[{"keyCode":4,"modifiers":[],"rendered":"h","type":"key"},{"keyCode":17,"modifiers":[],"rendered":"t","type":"key"},{"keyCode":17,"modifiers":[],"rendered":"t","type":"key"},{"keyCode":35,"modifiers":[],"rendered":"p","type":"key"},{"keyCode":1,"modifiers":[],"rendered":"s","type":"key"},{"keyCode":41,"modifiers":["shift"],"rendered":":","type":"key"},{"keyCode":44,"modifiers":[],"rendered":"/","type":"key"},{"keyCode":44,"modifiers":[],"rendered":"/","type":"key"},{"keyCode":1,"modifiers":[],"rendered":"s","type":"key"},{"keyCode":13,"modifiers":[],"rendered":"w","type":"key"},{"keyCode":34,"modifiers":[],"rendered":"i","type":"key"},{"keyCode":3,"modifiers":[],"rendered":"f","type":"key"},{"keyCode":17,"modifiers":[],"rendered":"t","type":"key"},{"keyCode":47,"modifiers":[],"rendered":".","type":"key"},{"keyCode":31,"modifiers":[],"rendered":"o","type":"key"},{"keyCode":15,"modifiers":[],"rendered":"r","type":"key"},{"keyCode":5,"modifiers":[],"rendered":"g","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":18,"language":"en","text":"https://swift.org "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"https://swift.org "},"groupID":"holdout-grp-043","id":"holdout-preserve-043","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"email","events":[{"keyCode":34,"modifiers":[],"rendered":"i","type":"key"},{"keyCode":45,"modifiers":[],"rendered":"n","type":"key"},{"keyCode":3,"modifiers":[],"rendered":"f","type":"key"},{"keyCode":31,"modifiers":[],"rendered":"o","type":"key"},{"keyCode":19,"modifiers":["shift"],"rendered":"@","type":"key"},{"keyCode":8,"modifiers":[],"rendered":"c","type":"key"},{"keyCode":31,"modifiers":[],"rendered":"o","type":"key"},{"keyCode":46,"modifiers":[],"rendered":"m","type":"key"},{"keyCode":35,"modifiers":[],"rendered":"p","type":"key"},{"keyCode":0,"modifiers":[],"rendered":"a","type":"key"},{"keyCode":45,"modifiers":[],"rendered":"n","type":"key"},{"keyCode":16,"modifiers":[],"rendered":"y","type":"key"},{"keyCode":47,"modifiers":[],"rendered":".","type":"key"},{"keyCode":8,"modifiers":[],"rendered":"c","type":"key"},{"keyCode":31,"modifiers":[],"rendered":"o","type":"key"},{"keyCode":46,"modifiers":[],"rendered":"m","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":17,"language":"en","text":"info@company.com "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"info@company.com "},"groupID":"holdout-grp-044","id":"holdout-preserve-044","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"acronym","events":[{"keyCode":15,"modifiers":["shift"],"rendered":"R","type":"key"},{"keyCode":14,"modifiers":["shift"],"rendered":"E","type":"key"},{"keyCode":1,"modifiers":["shift"],"rendered":"S","type":"key"},{"keyCode":17,"modifiers":["shift"],"rendered":"T","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":5,"language":"en","text":"REST "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"REST "},"groupID":"holdout-grp-045","id":"holdout-preserve-045","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"acronym","events":[{"keyCode":4,"modifiers":["shift"],"rendered":"H","type":"key"},{"keyCode":17,"modifiers":["shift"],"rendered":"T","type":"key"},{"keyCode":46,"modifiers":["shift"],"rendered":"M","type":"key"},{"keyCode":37,"modifiers":["shift"],"rendered":"L","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":5,"language":"en","text":"HTML "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"HTML "},"groupID":"holdout-grp-046","id":"holdout-preserve-046","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"acronym","events":[{"keyCode":15,"modifiers":["shift"],"rendered":"R","type":"key"},{"keyCode":0,"modifiers":["shift"],"rendered":"A","type":"key"},{"keyCode":46,"modifiers":["shift"],"rendered":"M","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":4,"language":"en","text":"RAM "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"RAM "},"groupID":"holdout-grp-047","id":"holdout-preserve-047","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"contraction","events":[{"keyCode":13,"modifiers":[],"rendered":"w","type":"key"},{"keyCode":31,"modifiers":[],"rendered":"o","type":"key"},{"keyCode":45,"modifiers":[],"rendered":"n","type":"key"},{"keyCode":39,"modifiers":[],"rendered":"'","type":"key"},{"keyCode":17,"modifiers":[],"rendered":"t","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":6,"language":"en","text":"won't "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"won't "},"groupID":"holdout-grp-048","id":"holdout-preserve-048","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"contraction","events":[{"keyCode":17,"modifiers":[],"rendered":"t","type":"key"},{"keyCode":4,"modifiers":[],"rendered":"h","type":"key"},{"keyCode":0,"modifiers":[],"rendered":"a","type":"key"},{"keyCode":17,"modifiers":[],"rendered":"t","type":"key"},{"keyCode":39,"modifiers":[],"rendered":"'","type":"key"},{"keyCode":1,"modifiers":[],"rendered":"s","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":7,"language":"en","text":"that's "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"that's "},"groupID":"holdout-grp-049","id":"holdout-preserve-049","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"punctuation","events":[{"keyCode":1,"modifiers":[],"rendered":"s","type":"key"},{"keyCode":17,"modifiers":[],"rendered":"t","type":"key"},{"keyCode":0,"modifiers":[],"rendered":"a","type":"key"},{"keyCode":17,"modifiers":[],"rendered":"t","type":"key"},{"keyCode":32,"modifiers":[],"rendered":"u","type":"key"},{"keyCode":1,"modifiers":[],"rendered":"s","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"},{"keyCode":24,"modifiers":[],"rendered":"=","type":"key"},{"keyCode":24,"modifiers":[],"rendered":"=","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"},{"keyCode":29,"modifiers":[],"rendered":"0","type":"key"},{"keyCode":41,"modifiers":[],"rendered":";","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":13,"language":"en","text":"status == 0; "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"status == 0; "},"groupID":"holdout-grp-050","id":"holdout-preserve-050","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"natural-word","events":[{"keyCode":3,"modifiers":[],"rendered":"а","type":"key"},{"keyCode":5,"modifiers":[],"rendered":"п","type":"key"},{"keyCode":17,"modifiers":[],"rendered":"е","type":"key"},{"keyCode":40,"modifiers":[],"rendered":"л","type":"key"},{"keyCode":46,"modifiers":[],"rendered":"ь","type":"key"},{"keyCode":8,"modifiers":[],"rendered":"с","type":"key"},{"keyCode":1,"modifiers":[],"rendered":"и","type":"key"},{"keyCode":16,"modifiers":[],"rendered":"н","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":9,"language":"uk","text":"апельсин "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"апельсин "},"groupID":"holdout-grp-051","id":"holdout-preserve-051","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"natural-word","events":[{"keyCode":9,"modifiers":[],"rendered":"м","type":"key"},{"keyCode":38,"modifiers":[],"rendered":"о","type":"key"},{"keyCode":2,"modifiers":[],"rendered":"в","type":"key"},{"keyCode":3,"modifiers":[],"rendered":"а","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":5,"language":"uk","text":"мова "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"мова "},"groupID":"holdout-grp-052","id":"holdout-preserve-052","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"natural-word","events":[{"keyCode":3,"modifiers":[],"rendered":"а","type":"key"},{"keyCode":4,"modifiers":[],"rendered":"р","type":"key"},{"keyCode":33,"modifiers":[],"rendered":"х","type":"key"},{"keyCode":11,"modifiers":[],"rendered":"і","type":"key"},{"keyCode":45,"modifiers":[],"rendered":"т","type":"key"},{"keyCode":17,"modifiers":[],"rendered":"е","type":"key"},{"keyCode":15,"modifiers":[],"rendered":"к","type":"key"},{"keyCode":45,"modifiers":[],"rendered":"т","type":"key"},{"keyCode":38,"modifiers":[],"rendered":"о","type":"key"},{"keyCode":4,"modifiers":[],"rendered":"р","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":11,"language":"uk","text":"архітектор "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"архітектор "},"groupID":"holdout-grp-053","id":"holdout-preserve-053","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"natural-word","events":[{"keyCode":15,"modifiers":[],"rendered":"к","type":"key"},{"keyCode":38,"modifiers":[],"rendered":"о","type":"key"},{"keyCode":16,"modifiers":[],"rendered":"н","type":"key"},{"keyCode":8,"modifiers":[],"rendered":"с","type":"key"},{"keyCode":38,"modifiers":[],"rendered":"о","type":"key"},{"keyCode":40,"modifiers":[],"rendered":"л","type":"key"},{"keyCode":46,"modifiers":[],"rendered":"ь","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":8,"language":"uk","text":"консоль "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"консоль "},"groupID":"holdout-grp-054","id":"holdout-preserve-054","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"natural-word","events":[{"keyCode":38,"modifiers":[],"rendered":"о","type":"key"},{"keyCode":16,"modifiers":[],"rendered":"н","type":"key"},{"keyCode":38,"modifiers":[],"rendered":"о","type":"key"},{"keyCode":2,"modifiers":[],"rendered":"в","type":"key"},{"keyCode":40,"modifiers":[],"rendered":"л","type":"key"},{"keyCode":17,"modifiers":[],"rendered":"е","type":"key"},{"keyCode":16,"modifiers":[],"rendered":"н","type":"key"},{"keyCode":16,"modifiers":[],"rendered":"н","type":"key"},{"keyCode":6,"modifiers":[],"rendered":"я","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":10,"language":"uk","text":"оновлення "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"оновлення "},"groupID":"holdout-grp-055","id":"holdout-preserve-055","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"natural-word","events":[{"keyCode":17,"modifiers":[],"rendered":"е","type":"key"},{"keyCode":15,"modifiers":[],"rendered":"к","type":"key"},{"keyCode":4,"modifiers":[],"rendered":"р","type":"key"},{"keyCode":3,"modifiers":[],"rendered":"а","type":"key"},{"keyCode":16,"modifiers":[],"rendered":"н","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":6,"language":"uk","text":"екран "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"екран "},"groupID":"holdout-grp-056","id":"holdout-preserve-056","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"short-word","events":[{"keyCode":43,"modifiers":[],"rendered":"б","type":"key"},{"keyCode":38,"modifiers":[],"rendered":"о","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":3,"language":"uk","text":"бо "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"бо "},"groupID":"holdout-grp-057","id":"holdout-preserve-057","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"short-word","events":[{"keyCode":31,"modifiers":[],"rendered":"щ","type":"key"},{"keyCode":17,"modifiers":[],"rendered":"е","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":3,"language":"uk","text":"ще "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"ще "},"groupID":"holdout-grp-058-v2","id":"holdout-preserve-058-v2","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"short-word","events":[{"keyCode":14,"modifiers":[],"rendered":"у","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":2,"language":"uk","text":"у "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"у "},"groupID":"holdout-grp-059","id":"holdout-preserve-059","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"short-word","events":[{"keyCode":2,"modifiers":[],"rendered":"в","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":2,"language":"uk","text":"в "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"в "},"groupID":"holdout-grp-060","id":"holdout-preserve-060","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"short-word","events":[{"keyCode":38,"modifiers":[],"rendered":"о","type":"key"},{"keyCode":45,"modifiers":[],"rendered":"т","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":3,"language":"uk","text":"от "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"от "},"groupID":"holdout-grp-061","id":"holdout-preserve-061","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"short-word","events":[{"keyCode":16,"modifiers":[],"rendered":"н","type":"key"},{"keyCode":14,"modifiers":[],"rendered":"у","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":3,"language":"uk","text":"ну "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"ну "},"groupID":"holdout-grp-062","id":"holdout-preserve-062","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"proper-noun","events":[{"keyCode":38,"modifiers":["shift"],"rendered":"О","type":"key"},{"keyCode":37,"modifiers":[],"rendered":"д","type":"key"},{"keyCode":17,"modifiers":[],"rendered":"е","type":"key"},{"keyCode":8,"modifiers":[],"rendered":"с","type":"key"},{"keyCode":3,"modifiers":[],"rendered":"а","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":6,"language":"uk","text":"Одеса "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"Одеса "},"groupID":"holdout-grp-063","id":"holdout-preserve-063","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"proper-noun","events":[{"keyCode":0,"modifiers":["shift"],"rendered":"Ф","type":"key"},{"keyCode":4,"modifiers":[],"rendered":"р","type":"key"},{"keyCode":3,"modifiers":[],"rendered":"а","type":"key"},{"keyCode":16,"modifiers":[],"rendered":"н","type":"key"},{"keyCode":15,"modifiers":[],"rendered":"к","type":"key"},{"keyCode":38,"modifiers":[],"rendered":"о","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":7,"language":"uk","text":"Франко "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"Франко "},"groupID":"holdout-grp-064","id":"holdout-preserve-064","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"proper-noun","events":[{"keyCode":40,"modifiers":["shift"],"rendered":"Л","type":"key"},{"keyCode":17,"modifiers":[],"rendered":"е","type":"key"},{"keyCode":8,"modifiers":[],"rendered":"с","type":"key"},{"keyCode":6,"modifiers":[],"rendered":"я","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":5,"language":"uk","text":"Леся "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"Леся "},"groupID":"holdout-grp-065","id":"holdout-preserve-065","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"proper-noun","events":[{"keyCode":33,"modifiers":["shift"],"rendered":"Х","type":"key"},{"keyCode":3,"modifiers":[],"rendered":"а","type":"key"},{"keyCode":4,"modifiers":[],"rendered":"р","type":"key"},{"keyCode":15,"modifiers":[],"rendered":"к","type":"key"},{"keyCode":11,"modifiers":[],"rendered":"і","type":"key"},{"keyCode":2,"modifiers":[],"rendered":"в","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":7,"language":"uk","text":"Харків "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"Харків "},"groupID":"holdout-grp-066","id":"holdout-preserve-066","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"apostrophe","events":[{"keyCode":9,"modifiers":[],"rendered":"м","type":"key"},{"keyCode":50,"modifiers":[],"rendered":"'","type":"key"},{"keyCode":6,"modifiers":[],"rendered":"я","type":"key"},{"keyCode":15,"modifiers":[],"rendered":"к","type":"key"},{"keyCode":1,"modifiers":[],"rendered":"и","type":"key"},{"keyCode":12,"modifiers":[],"rendered":"й","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":7,"language":"uk","text":"м'який "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"м'який "},"groupID":"holdout-grp-067","id":"holdout-preserve-067","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"apostrophe","events":[{"keyCode":5,"modifiers":[],"rendered":"п","type":"key"},{"keyCode":50,"modifiers":[],"rendered":"'","type":"key"},{"keyCode":6,"modifiers":[],"rendered":"я","type":"key"},{"keyCode":45,"modifiers":[],"rendered":"т","type":"key"},{"keyCode":46,"modifiers":[],"rendered":"ь","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":6,"language":"uk","text":"п'ять "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"п'ять "},"groupID":"holdout-grp-068","id":"holdout-preserve-068","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"apostrophe","events":[{"keyCode":8,"modifiers":[],"rendered":"с","type":"key"},{"keyCode":38,"modifiers":[],"rendered":"о","type":"key"},{"keyCode":40,"modifiers":[],"rendered":"л","type":"key"},{"keyCode":38,"modifiers":[],"rendered":"о","type":"key"},{"keyCode":2,"modifiers":[],"rendered":"в","type":"key"},{"keyCode":50,"modifiers":[],"rendered":"'","type":"key"},{"keyCode":30,"modifiers":[],"rendered":"ї","type":"key"},{"keyCode":16,"modifiers":[],"rendered":"н","type":"key"},{"keyCode":1,"modifiers":[],"rendered":"и","type":"key"},{"keyCode":12,"modifiers":[],"rendered":"й","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":11,"language":"uk","text":"солов'їний "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"солов'їний "},"groupID":"holdout-grp-069","id":"holdout-preserve-069","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"apostrophe","events":[{"keyCode":5,"modifiers":[],"rendered":"п","type":"key"},{"keyCode":11,"modifiers":[],"rendered":"і","type":"key"},{"keyCode":37,"modifiers":[],"rendered":"д","type":"key"},{"keyCode":50,"modifiers":[],"rendered":"'","type":"key"},{"keyCode":30,"modifiers":[],"rendered":"ї","type":"key"},{"keyCode":35,"modifiers":[],"rendered":"з","type":"key"},{"keyCode":37,"modifiers":[],"rendered":"д","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":8,"language":"uk","text":"під'їзд "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"під'їзд "},"groupID":"holdout-grp-070","id":"holdout-preserve-070","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"acronym","events":[{"keyCode":9,"modifiers":["shift"],"rendered":"М","type":"key"},{"keyCode":2,"modifiers":["shift"],"rendered":"В","type":"key"},{"keyCode":0,"modifiers":["shift"],"rendered":"Ф","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":4,"language":"uk","text":"МВФ "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"МВФ "},"groupID":"holdout-grp-071","id":"holdout-preserve-071","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"acronym","events":[{"keyCode":47,"modifiers":["shift"],"rendered":"Ю","type":"key"},{"keyCode":16,"modifiers":["shift"],"rendered":"Н","type":"key"},{"keyCode":17,"modifiers":["shift"],"rendered":"Е","type":"key"},{"keyCode":8,"modifiers":["shift"],"rendered":"С","type":"key"},{"keyCode":15,"modifiers":["shift"],"rendered":"К","type":"key"},{"keyCode":38,"modifiers":["shift"],"rendered":"О","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":7,"language":"uk","text":"ЮНЕСКО "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"ЮНЕСКО "},"groupID":"holdout-grp-072","id":"holdout-preserve-072","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"acronym","events":[{"keyCode":39,"modifiers":["shift"],"rendered":"Є","type":"key"},{"keyCode":8,"modifiers":["shift"],"rendered":"С","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":3,"language":"uk","text":"ЄС "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"ЄС "},"groupID":"holdout-grp-073","id":"holdout-preserve-073","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"acronym","events":[{"keyCode":35,"modifiers":["shift"],"rendered":"З","type":"key"},{"keyCode":8,"modifiers":["shift"],"rendered":"С","type":"key"},{"keyCode":14,"modifiers":["shift"],"rendered":"У","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":4,"language":"uk","text":"ЗСУ "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"ЗСУ "},"groupID":"holdout-grp-074","id":"holdout-preserve-074","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"punctuation","events":[{"keyCode":7,"modifiers":[],"rendered":"ч","type":"key"},{"keyCode":14,"modifiers":[],"rendered":"у","type":"key"},{"keyCode":37,"modifiers":[],"rendered":"д","type":"key"},{"keyCode":38,"modifiers":[],"rendered":"о","type":"key"},{"keyCode":2,"modifiers":[],"rendered":"в","type":"key"},{"keyCode":38,"modifiers":[],"rendered":"о","type":"key"},{"keyCode":18,"modifiers":["shift"],"rendered":"!","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":8,"language":"uk","text":"чудово! "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"чудово! "},"groupID":"holdout-grp-075","id":"holdout-preserve-075","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"punctuation","events":[{"keyCode":25,"modifiers":["shift"],"rendered":"(","type":"key"},{"keyCode":16,"modifiers":[],"rendered":"н","type":"key"},{"keyCode":11,"modifiers":[],"rendered":"і","type":"key"},{"keyCode":29,"modifiers":["shift"],"rendered":")","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":5,"language":"uk","text":"(ні) "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"(ні) "},"groupID":"holdout-grp-076","id":"holdout-preserve-076","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"punctuation","events":[{"keyCode":2,"modifiers":[],"rendered":"в","type":"key"},{"keyCode":1,"modifiers":[],"rendered":"и","type":"key"},{"keyCode":8,"modifiers":[],"rendered":"с","type":"key"},{"keyCode":16,"modifiers":[],"rendered":"н","type":"key"},{"keyCode":38,"modifiers":[],"rendered":"о","type":"key"},{"keyCode":2,"modifiers":[],"rendered":"в","type":"key"},{"keyCode":38,"modifiers":[],"rendered":"о","type":"key"},{"keyCode":15,"modifiers":[],"rendered":"к","type":"key"},{"keyCode":23,"modifiers":["shift"],"rendered":":","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":10,"language":"uk","text":"висновок: "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"висновок: "},"groupID":"holdout-grp-077","id":"holdout-preserve-077","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"symbol","events":[{"keyCode":23,"modifiers":[],"rendered":"5","type":"key"},{"keyCode":29,"modifiers":[],"rendered":"0","type":"key"},{"keyCode":21,"modifiers":["shift"],"rendered":"%","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":4,"language":"uk","text":"50% "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"50% "},"groupID":"holdout-grp-078","id":"holdout-preserve-078","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"phrase","events":[{"keyCode":37,"modifiers":[],"rendered":"д","type":"key"},{"keyCode":38,"modifiers":[],"rendered":"о","type":"key"},{"keyCode":43,"modifiers":[],"rendered":"б","type":"key"},{"keyCode":4,"modifiers":[],"rendered":"р","type":"key"},{"keyCode":1,"modifiers":[],"rendered":"и","type":"key"},{"keyCode":12,"modifiers":[],"rendered":"й","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"},{"keyCode":2,"modifiers":[],"rendered":"в","type":"key"},{"keyCode":17,"modifiers":[],"rendered":"е","type":"key"},{"keyCode":7,"modifiers":[],"rendered":"ч","type":"key"},{"keyCode":11,"modifiers":[],"rendered":"і","type":"key"},{"keyCode":4,"modifiers":[],"rendered":"р","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":13,"language":"uk","text":"добрий вечір "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"добрий вечір "},"groupID":"holdout-grp-079","id":"holdout-preserve-079","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"phrase","events":[{"keyCode":16,"modifiers":[],"rendered":"н","type":"key"},{"keyCode":3,"modifiers":[],"rendered":"а","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"},{"keyCode":2,"modifiers":[],"rendered":"в","type":"key"},{"keyCode":8,"modifiers":[],"rendered":"с","type":"key"},{"keyCode":17,"modifiers":[],"rendered":"е","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"},{"keyCode":37,"modifiers":[],"rendered":"д","type":"key"},{"keyCode":38,"modifiers":[],"rendered":"о","type":"key"},{"keyCode":43,"modifiers":[],"rendered":"б","type":"key"},{"keyCode":4,"modifiers":[],"rendered":"р","type":"key"},{"keyCode":17,"modifiers":[],"rendered":"е","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":13,"language":"uk","text":"на все добре "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"на все добре "},"groupID":"holdout-grp-080","id":"holdout-preserve-080","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"ambiguous-short","events":[{"keyCode":0,"modifiers":[],"rendered":"a","type":"key"},{"keyCode":46,"modifiers":[],"rendered":"m","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":3,"language":"en","text":"am "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"am "},"groupID":"holdout-grp-081","id":"holdout-abstain-081","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"abstain","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"ambiguous-short","events":[{"keyCode":34,"modifiers":[],"rendered":"i","type":"key"},{"keyCode":1,"modifiers":[],"rendered":"s","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":3,"language":"en","text":"is "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"is "},"groupID":"holdout-grp-082","id":"holdout-abstain-082","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"abstain","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"ambiguous-short","events":[{"keyCode":32,"modifiers":[],"rendered":"u","type":"key"},{"keyCode":1,"modifiers":[],"rendered":"s","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":3,"language":"en","text":"us "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"us "},"groupID":"holdout-grp-083","id":"holdout-abstain-083","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"abstain","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"ambiguous-short","events":[{"keyCode":11,"modifiers":[],"rendered":"b","type":"key"},{"keyCode":14,"modifiers":[],"rendered":"e","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":3,"language":"en","text":"be "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"be "},"groupID":"holdout-grp-084","id":"holdout-abstain-084","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"abstain","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"ambiguous-short","events":[{"keyCode":4,"modifiers":[],"rendered":"h","type":"key"},{"keyCode":14,"modifiers":[],"rendered":"e","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":3,"language":"en","text":"he "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"he "},"groupID":"holdout-grp-085","id":"holdout-abstain-085","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"abstain","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"gibberish","events":[{"keyCode":6,"modifiers":[],"rendered":"z","type":"key"},{"keyCode":7,"modifiers":[],"rendered":"x","type":"key"},{"keyCode":8,"modifiers":[],"rendered":"c","type":"key"},{"keyCode":9,"modifiers":[],"rendered":"v","type":"key"},{"keyCode":11,"modifiers":[],"rendered":"b","type":"key"},{"keyCode":45,"modifiers":[],"rendered":"n","type":"key"},{"keyCode":46,"modifiers":[],"rendered":"m","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":8,"language":"en","text":"zxcvbnm "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"zxcvbnm "},"groupID":"holdout-grp-086","id":"holdout-abstain-086","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"abstain","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"gibberish","events":[{"keyCode":37,"modifiers":[],"rendered":"l","type":"key"},{"keyCode":40,"modifiers":[],"rendered":"k","type":"key"},{"keyCode":38,"modifiers":[],"rendered":"j","type":"key"},{"keyCode":4,"modifiers":[],"rendered":"h","type":"key"},{"keyCode":5,"modifiers":[],"rendered":"g","type":"key"},{"keyCode":3,"modifiers":[],"rendered":"f","type":"key"},{"keyCode":2,"modifiers":[],"rendered":"d","type":"key"},{"keyCode":1,"modifiers":[],"rendered":"s","type":"key"},{"keyCode":0,"modifiers":[],"rendered":"a","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":10,"language":"en","text":"lkjhgfdsa "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"lkjhgfdsa "},"groupID":"holdout-grp-087","id":"holdout-abstain-087","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"abstain","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"isolated-char","events":[{"keyCode":11,"modifiers":[],"rendered":"b","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":2,"language":"en","text":"b "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"b "},"groupID":"holdout-grp-088","id":"holdout-abstain-088","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"abstain","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"isolated-char","events":[{"keyCode":16,"modifiers":[],"rendered":"y","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":2,"language":"en","text":"y "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"y "},"groupID":"holdout-grp-089","id":"holdout-abstain-089","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"abstain","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"isolated-symbol","events":[{"keyCode":43,"modifiers":[],"rendered":",","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":2,"language":"en","text":", "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":", "},"groupID":"holdout-grp-090","id":"holdout-abstain-090","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"abstain","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"ambiguous-short","events":[{"keyCode":43,"modifiers":[],"rendered":"б","type":"key"},{"keyCode":1,"modifiers":[],"rendered":"и","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":3,"language":"uk","text":"би "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"би "},"groupID":"holdout-grp-091","id":"holdout-abstain-091","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"abstain","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"ambiguous-short","events":[{"keyCode":41,"modifiers":[],"rendered":"ж","type":"key"},{"keyCode":17,"modifiers":[],"rendered":"е","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":3,"language":"uk","text":"же "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"же "},"groupID":"holdout-grp-092","id":"holdout-abstain-092","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"abstain","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"ambiguous-short","events":[{"keyCode":3,"modifiers":[],"rendered":"а","type":"key"},{"keyCode":41,"modifiers":[],"rendered":"ж","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":3,"language":"uk","text":"аж "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"аж "},"groupID":"holdout-grp-093-v2","id":"holdout-abstain-093-v2","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"abstain","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"ambiguous-short","events":[{"keyCode":33,"modifiers":[],"rendered":"х","type":"key"},{"keyCode":38,"modifiers":[],"rendered":"о","type":"key"},{"keyCode":7,"modifiers":[],"rendered":"ч","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":4,"language":"uk","text":"хоч "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"хоч "},"groupID":"holdout-grp-094-v2","id":"holdout-abstain-094-v2","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"abstain","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"ambiguous-short","events":[{"keyCode":31,"modifiers":[],"rendered":"щ","type":"key"},{"keyCode":38,"modifiers":[],"rendered":"о","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":3,"language":"uk","text":"що "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"що "},"groupID":"holdout-grp-095","id":"holdout-abstain-095","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"abstain","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"gibberish","events":[{"keyCode":6,"modifiers":[],"rendered":"я","type":"key"},{"keyCode":7,"modifiers":[],"rendered":"ч","type":"key"},{"keyCode":8,"modifiers":[],"rendered":"с","type":"key"},{"keyCode":9,"modifiers":[],"rendered":"м","type":"key"},{"keyCode":1,"modifiers":[],"rendered":"и","type":"key"},{"keyCode":45,"modifiers":[],"rendered":"т","type":"key"},{"keyCode":46,"modifiers":[],"rendered":"ь","type":"key"},{"keyCode":43,"modifiers":[],"rendered":"б","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":9,"language":"uk","text":"ячсмитьб "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"ячсмитьб "},"groupID":"holdout-grp-096","id":"holdout-abstain-096","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"abstain","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"gibberish","events":[{"keyCode":5,"modifiers":[],"rendered":"п","type":"key"},{"keyCode":38,"modifiers":[],"rendered":"о","type":"key"},{"keyCode":40,"modifiers":[],"rendered":"л","type":"key"},{"keyCode":37,"modifiers":[],"rendered":"д","type":"key"},{"keyCode":35,"modifiers":[],"rendered":"з","type":"key"},{"keyCode":6,"modifiers":[],"rendered":"я","type":"key"},{"keyCode":7,"modifiers":[],"rendered":"ч","type":"key"},{"keyCode":8,"modifiers":[],"rendered":"с","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":9,"language":"uk","text":"полдзячс "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"полдзячс "},"groupID":"holdout-grp-097","id":"holdout-abstain-097","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"abstain","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"isolated-char","events":[{"keyCode":39,"modifiers":[],"rendered":"є","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":2,"language":"uk","text":"є "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"є "},"groupID":"holdout-grp-098-v2","id":"holdout-abstain-098-v2","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"abstain","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"isolated-symbol","events":[{"keyCode":22,"modifiers":["shift"],"rendered":",","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":2,"language":"uk","text":", "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":", "},"groupID":"holdout-grp-099","id":"holdout-abstain-099","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"abstain","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"symbol","events":[{"keyCode":24,"modifiers":[],"rendered":"=","type":"key"},{"keyCode":24,"modifiers":[],"rendered":"=","type":"key"},{"keyCode":24,"modifiers":[],"rendered":"=","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":4,"language":"uk","text":"=== "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"=== "},"groupID":"holdout-grp-100","id":"holdout-abstain-100","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"abstain","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"cross-layout-collision","events":[{"keyCode":37,"modifiers":[],"rendered":"l","type":"key"},{"keyCode":14,"modifiers":[],"rendered":"e","type":"key"},{"keyCode":9,"modifiers":[],"rendered":"v","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":4,"language":"en","text":"lev "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"lev "},"groupID":"holdout-grp-101","id":"holdout-preserve-101","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"cross-layout-collision","events":[{"keyCode":45,"modifiers":[],"rendered":"n","type":"key"},{"keyCode":1,"modifiers":[],"rendered":"s","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":3,"language":"en","text":"ns "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"ns "},"groupID":"holdout-grp-102","id":"holdout-preserve-102","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"cross-layout-collision","events":[{"keyCode":5,"modifiers":[],"rendered":"g","type":"key"},{"keyCode":0,"modifiers":[],"rendered":"a","type":"key"},{"keyCode":35,"modifiers":[],"rendered":"p","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":4,"language":"en","text":"gap "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"gap "},"groupID":"holdout-grp-103","id":"holdout-preserve-103","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"cross-layout-collision","events":[{"keyCode":32,"modifiers":[],"rendered":"u","type":"key"},{"keyCode":3,"modifiers":[],"rendered":"f","type":"key"},{"keyCode":8,"modifiers":[],"rendered":"c","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":4,"language":"en","text":"ufc "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"ufc "},"groupID":"holdout-grp-104","id":"holdout-preserve-104","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"cross-layout-collision","events":[{"keyCode":2,"modifiers":[],"rendered":"d","type":"key"},{"keyCode":1,"modifiers":[],"rendered":"s","type":"key"},{"keyCode":37,"modifiers":[],"rendered":"l","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":4,"language":"en","text":"dsl "}],"language":"en","sourceID":"com.apple.keylayout.ABC","text":"dsl "},"groupID":"holdout-grp-105","id":"holdout-preserve-105","initial":{"caretUTF16":0,"keyboardType":91,"language":"en","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.ABC","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"cross-layout-collision","events":[{"keyCode":37,"modifiers":[],"rendered":"д","type":"key"},{"keyCode":14,"modifiers":[],"rendered":"у","type":"key"},{"keyCode":9,"modifiers":[],"rendered":"м","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":4,"language":"uk","text":"дум "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"дум "},"groupID":"holdout-grp-101","id":"holdout-preserve-106","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"cross-layout-collision","events":[{"keyCode":37,"modifiers":[],"rendered":"д","type":"key"},{"keyCode":14,"modifiers":[],"rendered":"у","type":"key"},{"keyCode":45,"modifiers":[],"rendered":"т","type":"key"},{"keyCode":1,"modifiers":[],"rendered":"и","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":5,"language":"uk","text":"дути "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"дути "},"groupID":"holdout-grp-106","id":"holdout-preserve-107","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"cross-layout-collision","events":[{"keyCode":37,"modifiers":[],"rendered":"д","type":"key"},{"keyCode":14,"modifiers":[],"rendered":"у","type":"key"},{"keyCode":2,"modifiers":[],"rendered":"в","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":4,"language":"uk","text":"дув "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"дув "},"groupID":"holdout-grp-106","id":"holdout-preserve-108","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"cross-layout-collision","events":[{"keyCode":5,"modifiers":[],"rendered":"п","type":"key"},{"keyCode":14,"modifiers":[],"rendered":"у","type":"key"},{"keyCode":37,"modifiers":[],"rendered":"д","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":4,"language":"uk","text":"пуд "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"пуд "},"groupID":"holdout-grp-107","id":"holdout-preserve-109","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} +{"category":"cross-layout-collision","events":[{"keyCode":37,"modifiers":[],"rendered":"д","type":"key"},{"keyCode":16,"modifiers":[],"rendered":"н","type":"key"},{"keyCode":14,"modifiers":[],"rendered":"у","type":"key"},{"keyCode":49,"modifiers":[],"rendered":" ","type":"boundary"}],"expected":{"allowedAutomaticActions":[],"checkpoints":[{"afterEvent":4,"language":"uk","text":"дну "}],"language":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":"дну "},"groupID":"holdout-grp-108","id":"holdout-preserve-110","initial":{"caretUTF16":0,"keyboardType":91,"language":"uk","preferredCyrillic":"uk","sourceID":"com.apple.keylayout.Ukrainian","text":""},"label":"preserve","languages":["en","uk"],"provenance":{"generator":"manual-v2","kind":"synthetic","review":"pending-human","seed":0},"schemaVersion":1,"split":"holdout"} diff --git a/Benchmarks/layout-corpus/v2/manifest.json b/Benchmarks/layout-corpus/v2/manifest.json new file mode 100644 index 0000000..187b49f --- /dev/null +++ b/Benchmarks/layout-corpus/v2/manifest.json @@ -0,0 +1,103 @@ +{ + "corpusVersion" : "v2", + "created" : "2026-10-19", + "files" : { + "layout-corpus\/v2\/README.md" : { + "sha256" : "dba97b6a7ab720ed9b7d453a5a38e79ae70996dc04f1c9b1cd4cbe7e1fd899aa" + }, + "layout-corpus\/v2\/development.jsonl" : { + "sha256" : "4e8dcffbef2efb84c0767a2a088efba013b1bd212f743dfc95b8acfbffbeb28d", + "trialsCount" : 110 + }, + "layout-corpus\/v2\/holdout.jsonl" : { + "sha256" : "78e401db3d6039a4d229d8877d5cf6b0c336f2c276a059a52f22213ae3e8d654", + "trialsCount" : 110 + }, + "layout-tables\/v1\/com.apple.keylayout.ABC@37.json" : { + "sha256" : "cf0bed17113a8f0d96738efeb7ca33449314c2b02e930ccc3b1021cbfdd57f46" + }, + "layout-tables\/v1\/com.apple.keylayout.ABC@91.json" : { + "sha256" : "4a135908d4e08e20bdab9e17c39f7731765d039b50c0819b88c847a1d8ecf5aa" + }, + "layout-tables\/v1\/com.apple.keylayout.British@37.json" : { + "sha256" : "62657c4714b1ba0567282bf4d2beb6f247ab12643acf8dd66e43fc5e0fb07e7b" + }, + "layout-tables\/v1\/com.apple.keylayout.British@91.json" : { + "sha256" : "b29622001b91eba73b291d9c8595f57394f3a64ddd4d700b1ff3a884ea3d82e2" + }, + "layout-tables\/v1\/com.apple.keylayout.Colemak@37.json" : { + "sha256" : "4faf9db8a1732999fc46357c7791fd6a29ec298b645a810f87b1c281a4c3d83b" + }, + "layout-tables\/v1\/com.apple.keylayout.Colemak@91.json" : { + "sha256" : "e5ab956b491545b4adc68f82801b67e45a5df016758199f9312a122948174197" + }, + "layout-tables\/v1\/com.apple.keylayout.Dvorak@37.json" : { + "sha256" : "6b31a65f097b6543cec92f2b87b4e6afc5177ba853de941ab0a62da389d18d0f" + }, + "layout-tables\/v1\/com.apple.keylayout.Dvorak@91.json" : { + "sha256" : "61befd74221873723822cdafa27f3300077393a55c4a0e4f24d3ca099fd7024f" + }, + "layout-tables\/v1\/com.apple.keylayout.Russian-Phonetic@37.json" : { + "sha256" : "1c2d031cbb0ef13b89b0e586d8acf64fb8c9d751364c0267b6ed00fbad94a00a" + }, + "layout-tables\/v1\/com.apple.keylayout.Russian-Phonetic@91.json" : { + "sha256" : "09e30609b90e9aab3184e99c69d689eda4073652dd69a085bde28406d26a593d" + }, + "layout-tables\/v1\/com.apple.keylayout.Russian@37.json" : { + "sha256" : "249b33745e1b4c8fe058dbfd55cbec18083d93089fe0bb6c9484dabc4e3bc30a" + }, + "layout-tables\/v1\/com.apple.keylayout.Russian@91.json" : { + "sha256" : "ccb648a0c68868e58e51388594d1632beaccd93f9758504a95706ce4c00e3bad" + }, + "layout-tables\/v1\/com.apple.keylayout.RussianWin@37.json" : { + "sha256" : "29898c681949ffe8d31b0a157e85f453d2b2f2c4437927e8c25d87a532d3060b" + }, + "layout-tables\/v1\/com.apple.keylayout.RussianWin@91.json" : { + "sha256" : "8585e4a27ee4cf81c204656e5c472aa5fd99c8208e507e1067579a576f5eec1e" + }, + "layout-tables\/v1\/com.apple.keylayout.US@37.json" : { + "sha256" : "5066da5b841c2ca0288defff297e9a28057d4c473be59a1bc77700302b23ce0b" + }, + "layout-tables\/v1\/com.apple.keylayout.US@91.json" : { + "sha256" : "b506d124fc61cc7cf54a4f88d13b07f3c8620197f50f62f9934585be6c069676" + }, + "layout-tables\/v1\/com.apple.keylayout.Ukrainian-PC@37.json" : { + "sha256" : "c5ab3998e352694b9b7662235e1aae0a2b74afee18447f72d8028804e49f45f0" + }, + "layout-tables\/v1\/com.apple.keylayout.Ukrainian-PC@91.json" : { + "sha256" : "65c6e763f08b5976a165cb26b57d9560aadeddfaf5c80b44bcf2e967fd25b82e" + }, + "layout-tables\/v1\/com.apple.keylayout.Ukrainian-QWERTY@37.json" : { + "sha256" : "9883b792cf3c7113c041c9342f4f18ef9d697ff9cb8f289a4004ff20f1a3242d" + }, + "layout-tables\/v1\/com.apple.keylayout.Ukrainian-QWERTY@91.json" : { + "sha256" : "8bc9476d6354ed537a9ab6a60b280104bc48cd9e8f542134f057643ea679bceb" + }, + "layout-tables\/v1\/com.apple.keylayout.Ukrainian@37.json" : { + "sha256" : "a8e9b561ac7404769e2021aa9557dc8e6ea768f5b10b70fa7a9c632f5700dc85" + }, + "layout-tables\/v1\/com.apple.keylayout.Ukrainian@91.json" : { + "sha256" : "4307413f155a6ea20503e583f84e6b3669943718ca0709939cd6591691f53bee" + } + }, + "keyboardType" : 91, + "osBuild" : "24G830", + "osVersion" : "15.7.9", + "schemaVersion" : 1, + "sourceCommit" : "5daf6533deb186b5fa85fbffc042fffcf34a807a", + "splits" : { + "development" : { + "abstain" : 20, + "preserve" : 70, + "repair" : 20, + "total" : 110 + }, + "holdout" : { + "abstain" : 20, + "preserve" : 70, + "repair" : 20, + "total" : 110 + } + }, + "tablesVersion" : "v1" +} \ No newline at end of file diff --git a/Benchmarks/layout-tables/v1/com.apple.keylayout.ABC@37.json b/Benchmarks/layout-tables/v1/com.apple.keylayout.ABC@37.json new file mode 100644 index 0000000..0591092 --- /dev/null +++ b/Benchmarks/layout-tables/v1/com.apple.keylayout.ABC@37.json @@ -0,0 +1,3465 @@ +{ + "keyOutputs" : { + "0" : { + "caps" : { + "output" : "A" + }, + "capsOption" : { + "output" : "Å" + }, + "keyCode" : 0, + "option" : { + "output" : "å" + }, + "shift" : { + "output" : "A" + }, + "shiftCaps" : { + "output" : "A" + }, + "shiftCapsOption" : { + "output" : "Å" + }, + "shiftOption" : { + "output" : "Å" + }, + "unmodified" : { + "output" : "a" + } + }, + "1" : { + "caps" : { + "output" : "S" + }, + "capsOption" : { + "output" : "Í" + }, + "keyCode" : 1, + "option" : { + "output" : "ß" + }, + "shift" : { + "output" : "S" + }, + "shiftCaps" : { + "output" : "S" + }, + "shiftCapsOption" : { + "output" : "Í" + }, + "shiftOption" : { + "output" : "Í" + }, + "unmodified" : { + "output" : "s" + } + }, + "10" : { + "caps" : { + "output" : "§" + }, + "capsOption" : { + "output" : "§" + }, + "keyCode" : 10, + "option" : { + "output" : "§" + }, + "shift" : { + "output" : "±" + }, + "shiftCaps" : { + "output" : "±" + }, + "shiftCapsOption" : { + "output" : "±" + }, + "shiftOption" : { + "output" : "±" + }, + "unmodified" : { + "output" : "§" + } + }, + "100" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 100, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "101" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 101, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "102" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 102, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "103" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 103, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "104" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 104, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "105" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 105, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "106" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 106, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "107" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 107, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "108" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 108, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "109" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 109, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "11" : { + "caps" : { + "output" : "B" + }, + "capsOption" : { + "output" : "ı" + }, + "keyCode" : 11, + "option" : { + "output" : "∫" + }, + "shift" : { + "output" : "B" + }, + "shiftCaps" : { + "output" : "B" + }, + "shiftCapsOption" : { + "output" : "ı" + }, + "shiftOption" : { + "output" : "ı" + }, + "unmodified" : { + "output" : "b" + } + }, + "110" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 110, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "111" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 111, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "112" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 112, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "113" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 113, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "114" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 114, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "115" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 115, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "116" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 116, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "117" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 117, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "118" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 118, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "119" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 119, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "12" : { + "caps" : { + "output" : "Q" + }, + "capsOption" : { + "output" : "Œ" + }, + "keyCode" : 12, + "option" : { + "output" : "œ" + }, + "shift" : { + "output" : "Q" + }, + "shiftCaps" : { + "output" : "Q" + }, + "shiftCapsOption" : { + "output" : "Œ" + }, + "shiftOption" : { + "output" : "Œ" + }, + "unmodified" : { + "output" : "q" + } + }, + "120" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 120, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "121" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 121, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "122" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 122, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "123" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 123, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "124" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 124, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "125" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 125, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "126" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 126, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "127" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 127, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "13" : { + "caps" : { + "output" : "W" + }, + "capsOption" : { + "output" : "∑" + }, + "keyCode" : 13, + "option" : { + "output" : "∑" + }, + "shift" : { + "output" : "W" + }, + "shiftCaps" : { + "output" : "W" + }, + "shiftCapsOption" : { + "output" : "„" + }, + "shiftOption" : { + "output" : "„" + }, + "unmodified" : { + "output" : "w" + } + }, + "14" : { + "caps" : { + "output" : "E" + }, + "capsOption" : { + "output" : "´" + }, + "keyCode" : 14, + "option" : { + "rejection" : "deadKey" + }, + "shift" : { + "output" : "E" + }, + "shiftCaps" : { + "output" : "E" + }, + "shiftCapsOption" : { + "output" : "´" + }, + "shiftOption" : { + "output" : "´" + }, + "unmodified" : { + "output" : "e" + } + }, + "15" : { + "caps" : { + "output" : "R" + }, + "capsOption" : { + "output" : "®" + }, + "keyCode" : 15, + "option" : { + "output" : "®" + }, + "shift" : { + "output" : "R" + }, + "shiftCaps" : { + "output" : "R" + }, + "shiftCapsOption" : { + "output" : "‰" + }, + "shiftOption" : { + "output" : "‰" + }, + "unmodified" : { + "output" : "r" + } + }, + "16" : { + "caps" : { + "output" : "Y" + }, + "capsOption" : { + "output" : "Á" + }, + "keyCode" : 16, + "option" : { + "output" : "¥" + }, + "shift" : { + "output" : "Y" + }, + "shiftCaps" : { + "output" : "Y" + }, + "shiftCapsOption" : { + "output" : "Á" + }, + "shiftOption" : { + "output" : "Á" + }, + "unmodified" : { + "output" : "y" + } + }, + "17" : { + "caps" : { + "output" : "T" + }, + "capsOption" : { + "output" : "†" + }, + "keyCode" : 17, + "option" : { + "output" : "†" + }, + "shift" : { + "output" : "T" + }, + "shiftCaps" : { + "output" : "T" + }, + "shiftCapsOption" : { + "output" : "ˇ" + }, + "shiftOption" : { + "output" : "ˇ" + }, + "unmodified" : { + "output" : "t" + } + }, + "18" : { + "caps" : { + "output" : "1" + }, + "capsOption" : { + "output" : "¡" + }, + "keyCode" : 18, + "option" : { + "output" : "¡" + }, + "shift" : { + "output" : "!" + }, + "shiftCaps" : { + "output" : "!" + }, + "shiftCapsOption" : { + "output" : "⁄" + }, + "shiftOption" : { + "output" : "⁄" + }, + "unmodified" : { + "output" : "1" + } + }, + "19" : { + "caps" : { + "output" : "2" + }, + "capsOption" : { + "output" : "™" + }, + "keyCode" : 19, + "option" : { + "output" : "™" + }, + "shift" : { + "output" : "@" + }, + "shiftCaps" : { + "output" : "@" + }, + "shiftCapsOption" : { + "output" : "€" + }, + "shiftOption" : { + "output" : "€" + }, + "unmodified" : { + "output" : "2" + } + }, + "2" : { + "caps" : { + "output" : "D" + }, + "capsOption" : { + "output" : "Î" + }, + "keyCode" : 2, + "option" : { + "output" : "∂" + }, + "shift" : { + "output" : "D" + }, + "shiftCaps" : { + "output" : "D" + }, + "shiftCapsOption" : { + "output" : "Î" + }, + "shiftOption" : { + "output" : "Î" + }, + "unmodified" : { + "output" : "d" + } + }, + "20" : { + "caps" : { + "output" : "3" + }, + "capsOption" : { + "output" : "£" + }, + "keyCode" : 20, + "option" : { + "output" : "£" + }, + "shift" : { + "output" : "#" + }, + "shiftCaps" : { + "output" : "#" + }, + "shiftCapsOption" : { + "output" : "‹" + }, + "shiftOption" : { + "output" : "‹" + }, + "unmodified" : { + "output" : "3" + } + }, + "21" : { + "caps" : { + "output" : "4" + }, + "capsOption" : { + "output" : "¢" + }, + "keyCode" : 21, + "option" : { + "output" : "¢" + }, + "shift" : { + "output" : "$" + }, + "shiftCaps" : { + "output" : "$" + }, + "shiftCapsOption" : { + "output" : "›" + }, + "shiftOption" : { + "output" : "›" + }, + "unmodified" : { + "output" : "4" + } + }, + "22" : { + "caps" : { + "output" : "6" + }, + "capsOption" : { + "output" : "§" + }, + "keyCode" : 22, + "option" : { + "output" : "§" + }, + "shift" : { + "output" : "^" + }, + "shiftCaps" : { + "output" : "^" + }, + "shiftCapsOption" : { + "output" : "fl" + }, + "shiftOption" : { + "output" : "fl" + }, + "unmodified" : { + "output" : "6" + } + }, + "23" : { + "caps" : { + "output" : "5" + }, + "capsOption" : { + "output" : "∞" + }, + "keyCode" : 23, + "option" : { + "output" : "∞" + }, + "shift" : { + "output" : "%" + }, + "shiftCaps" : { + "output" : "%" + }, + "shiftCapsOption" : { + "output" : "fi" + }, + "shiftOption" : { + "output" : "fi" + }, + "unmodified" : { + "output" : "5" + } + }, + "24" : { + "caps" : { + "output" : "=" + }, + "capsOption" : { + "output" : "≠" + }, + "keyCode" : 24, + "option" : { + "output" : "≠" + }, + "shift" : { + "output" : "+" + }, + "shiftCaps" : { + "output" : "+" + }, + "shiftCapsOption" : { + "output" : "±" + }, + "shiftOption" : { + "output" : "±" + }, + "unmodified" : { + "output" : "=" + } + }, + "25" : { + "caps" : { + "output" : "9" + }, + "capsOption" : { + "output" : "ª" + }, + "keyCode" : 25, + "option" : { + "output" : "ª" + }, + "shift" : { + "output" : "(" + }, + "shiftCaps" : { + "output" : "(" + }, + "shiftCapsOption" : { + "output" : "·" + }, + "shiftOption" : { + "output" : "·" + }, + "unmodified" : { + "output" : "9" + } + }, + "26" : { + "caps" : { + "output" : "7" + }, + "capsOption" : { + "output" : "¶" + }, + "keyCode" : 26, + "option" : { + "output" : "¶" + }, + "shift" : { + "output" : "&" + }, + "shiftCaps" : { + "output" : "&" + }, + "shiftCapsOption" : { + "output" : "‡" + }, + "shiftOption" : { + "output" : "‡" + }, + "unmodified" : { + "output" : "7" + } + }, + "27" : { + "caps" : { + "output" : "-" + }, + "capsOption" : { + "output" : "–" + }, + "keyCode" : 27, + "option" : { + "output" : "–" + }, + "shift" : { + "output" : "_" + }, + "shiftCaps" : { + "output" : "_" + }, + "shiftCapsOption" : { + "output" : "—" + }, + "shiftOption" : { + "output" : "—" + }, + "unmodified" : { + "output" : "-" + } + }, + "28" : { + "caps" : { + "output" : "8" + }, + "capsOption" : { + "output" : "•" + }, + "keyCode" : 28, + "option" : { + "output" : "•" + }, + "shift" : { + "output" : "*" + }, + "shiftCaps" : { + "output" : "*" + }, + "shiftCapsOption" : { + "output" : "°" + }, + "shiftOption" : { + "output" : "°" + }, + "unmodified" : { + "output" : "8" + } + }, + "29" : { + "caps" : { + "output" : "0" + }, + "capsOption" : { + "output" : "º" + }, + "keyCode" : 29, + "option" : { + "output" : "º" + }, + "shift" : { + "output" : ")" + }, + "shiftCaps" : { + "output" : ")" + }, + "shiftCapsOption" : { + "output" : "‚" + }, + "shiftOption" : { + "output" : "‚" + }, + "unmodified" : { + "output" : "0" + } + }, + "3" : { + "caps" : { + "output" : "F" + }, + "capsOption" : { + "output" : "Ï" + }, + "keyCode" : 3, + "option" : { + "output" : "ƒ" + }, + "shift" : { + "output" : "F" + }, + "shiftCaps" : { + "output" : "F" + }, + "shiftCapsOption" : { + "output" : "Ï" + }, + "shiftOption" : { + "output" : "Ï" + }, + "unmodified" : { + "output" : "f" + } + }, + "30" : { + "caps" : { + "output" : "]" + }, + "capsOption" : { + "output" : "‘" + }, + "keyCode" : 30, + "option" : { + "output" : "‘" + }, + "shift" : { + "output" : "}" + }, + "shiftCaps" : { + "output" : "}" + }, + "shiftCapsOption" : { + "output" : "’" + }, + "shiftOption" : { + "output" : "’" + }, + "unmodified" : { + "output" : "]" + } + }, + "31" : { + "caps" : { + "output" : "O" + }, + "capsOption" : { + "output" : "Ø" + }, + "keyCode" : 31, + "option" : { + "output" : "ø" + }, + "shift" : { + "output" : "O" + }, + "shiftCaps" : { + "output" : "O" + }, + "shiftCapsOption" : { + "output" : "Ø" + }, + "shiftOption" : { + "output" : "Ø" + }, + "unmodified" : { + "output" : "o" + } + }, + "32" : { + "caps" : { + "output" : "U" + }, + "capsOption" : { + "output" : "¨" + }, + "keyCode" : 32, + "option" : { + "rejection" : "deadKey" + }, + "shift" : { + "output" : "U" + }, + "shiftCaps" : { + "output" : "U" + }, + "shiftCapsOption" : { + "output" : "¨" + }, + "shiftOption" : { + "output" : "¨" + }, + "unmodified" : { + "output" : "u" + } + }, + "33" : { + "caps" : { + "output" : "[" + }, + "capsOption" : { + "output" : "“" + }, + "keyCode" : 33, + "option" : { + "output" : "“" + }, + "shift" : { + "output" : "{" + }, + "shiftCaps" : { + "output" : "{" + }, + "shiftCapsOption" : { + "output" : "”" + }, + "shiftOption" : { + "output" : "”" + }, + "unmodified" : { + "output" : "[" + } + }, + "34" : { + "caps" : { + "output" : "I" + }, + "capsOption" : { + "output" : "ˆ" + }, + "keyCode" : 34, + "option" : { + "rejection" : "deadKey" + }, + "shift" : { + "output" : "I" + }, + "shiftCaps" : { + "output" : "I" + }, + "shiftCapsOption" : { + "output" : "ˆ" + }, + "shiftOption" : { + "output" : "ˆ" + }, + "unmodified" : { + "output" : "i" + } + }, + "35" : { + "caps" : { + "output" : "P" + }, + "capsOption" : { + "output" : "∏" + }, + "keyCode" : 35, + "option" : { + "output" : "π" + }, + "shift" : { + "output" : "P" + }, + "shiftCaps" : { + "output" : "P" + }, + "shiftCapsOption" : { + "output" : "∏" + }, + "shiftOption" : { + "output" : "∏" + }, + "unmodified" : { + "output" : "p" + } + }, + "36" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 36, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "37" : { + "caps" : { + "output" : "L" + }, + "capsOption" : { + "output" : "Ò" + }, + "keyCode" : 37, + "option" : { + "output" : "¬" + }, + "shift" : { + "output" : "L" + }, + "shiftCaps" : { + "output" : "L" + }, + "shiftCapsOption" : { + "output" : "Ò" + }, + "shiftOption" : { + "output" : "Ò" + }, + "unmodified" : { + "output" : "l" + } + }, + "38" : { + "caps" : { + "output" : "J" + }, + "capsOption" : { + "output" : "Ô" + }, + "keyCode" : 38, + "option" : { + "output" : "∆" + }, + "shift" : { + "output" : "J" + }, + "shiftCaps" : { + "output" : "J" + }, + "shiftCapsOption" : { + "output" : "Ô" + }, + "shiftOption" : { + "output" : "Ô" + }, + "unmodified" : { + "output" : "j" + } + }, + "39" : { + "caps" : { + "output" : "'" + }, + "capsOption" : { + "output" : "Æ" + }, + "keyCode" : 39, + "option" : { + "output" : "æ" + }, + "shift" : { + "output" : "\"" + }, + "shiftCaps" : { + "output" : "\"" + }, + "shiftCapsOption" : { + "output" : "Æ" + }, + "shiftOption" : { + "output" : "Æ" + }, + "unmodified" : { + "output" : "'" + } + }, + "4" : { + "caps" : { + "output" : "H" + }, + "capsOption" : { + "output" : "Ó" + }, + "keyCode" : 4, + "option" : { + "output" : "˙" + }, + "shift" : { + "output" : "H" + }, + "shiftCaps" : { + "output" : "H" + }, + "shiftCapsOption" : { + "output" : "Ó" + }, + "shiftOption" : { + "output" : "Ó" + }, + "unmodified" : { + "output" : "h" + } + }, + "40" : { + "caps" : { + "output" : "K" + }, + "capsOption" : { + "output" : "˚" + }, + "keyCode" : 40, + "option" : { + "output" : "˚" + }, + "shift" : { + "output" : "K" + }, + "shiftCaps" : { + "output" : "K" + }, + "shiftCapsOption" : { + "rejection" : "privateUseOrSurrogate" + }, + "shiftOption" : { + "rejection" : "privateUseOrSurrogate" + }, + "unmodified" : { + "output" : "k" + } + }, + "41" : { + "caps" : { + "output" : ";" + }, + "capsOption" : { + "output" : "…" + }, + "keyCode" : 41, + "option" : { + "output" : "…" + }, + "shift" : { + "output" : ":" + }, + "shiftCaps" : { + "output" : ":" + }, + "shiftCapsOption" : { + "output" : "Ú" + }, + "shiftOption" : { + "output" : "Ú" + }, + "unmodified" : { + "output" : ";" + } + }, + "42" : { + "caps" : { + "output" : "\\" + }, + "capsOption" : { + "output" : "«" + }, + "keyCode" : 42, + "option" : { + "output" : "«" + }, + "shift" : { + "output" : "|" + }, + "shiftCaps" : { + "output" : "|" + }, + "shiftCapsOption" : { + "output" : "»" + }, + "shiftOption" : { + "output" : "»" + }, + "unmodified" : { + "output" : "\\" + } + }, + "43" : { + "caps" : { + "output" : "," + }, + "capsOption" : { + "output" : "≤" + }, + "keyCode" : 43, + "option" : { + "output" : "≤" + }, + "shift" : { + "output" : "<" + }, + "shiftCaps" : { + "output" : "<" + }, + "shiftCapsOption" : { + "output" : "¯" + }, + "shiftOption" : { + "output" : "¯" + }, + "unmodified" : { + "output" : "," + } + }, + "44" : { + "caps" : { + "output" : "\/" + }, + "capsOption" : { + "output" : "÷" + }, + "keyCode" : 44, + "option" : { + "output" : "÷" + }, + "shift" : { + "output" : "?" + }, + "shiftCaps" : { + "output" : "?" + }, + "shiftCapsOption" : { + "output" : "¿" + }, + "shiftOption" : { + "output" : "¿" + }, + "unmodified" : { + "output" : "\/" + } + }, + "45" : { + "caps" : { + "output" : "N" + }, + "capsOption" : { + "output" : "˜" + }, + "keyCode" : 45, + "option" : { + "rejection" : "deadKey" + }, + "shift" : { + "output" : "N" + }, + "shiftCaps" : { + "output" : "N" + }, + "shiftCapsOption" : { + "output" : "˜" + }, + "shiftOption" : { + "output" : "˜" + }, + "unmodified" : { + "output" : "n" + } + }, + "46" : { + "caps" : { + "output" : "M" + }, + "capsOption" : { + "output" : "Â" + }, + "keyCode" : 46, + "option" : { + "output" : "µ" + }, + "shift" : { + "output" : "M" + }, + "shiftCaps" : { + "output" : "M" + }, + "shiftCapsOption" : { + "output" : "Â" + }, + "shiftOption" : { + "output" : "Â" + }, + "unmodified" : { + "output" : "m" + } + }, + "47" : { + "caps" : { + "output" : "." + }, + "capsOption" : { + "output" : "≥" + }, + "keyCode" : 47, + "option" : { + "output" : "≥" + }, + "shift" : { + "output" : ">" + }, + "shiftCaps" : { + "output" : ">" + }, + "shiftCapsOption" : { + "output" : "˘" + }, + "shiftOption" : { + "output" : "˘" + }, + "unmodified" : { + "output" : "." + } + }, + "48" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 48, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "49" : { + "caps" : { + "output" : " " + }, + "capsOption" : { + "output" : " " + }, + "keyCode" : 49, + "option" : { + "output" : " " + }, + "shift" : { + "output" : " " + }, + "shiftCaps" : { + "output" : " " + }, + "shiftCapsOption" : { + "output" : " " + }, + "shiftOption" : { + "output" : " " + }, + "unmodified" : { + "output" : " " + } + }, + "5" : { + "caps" : { + "output" : "G" + }, + "capsOption" : { + "output" : "©" + }, + "keyCode" : 5, + "option" : { + "output" : "©" + }, + "shift" : { + "output" : "G" + }, + "shiftCaps" : { + "output" : "G" + }, + "shiftCapsOption" : { + "output" : "˝" + }, + "shiftOption" : { + "output" : "˝" + }, + "unmodified" : { + "output" : "g" + } + }, + "50" : { + "caps" : { + "output" : "`" + }, + "capsOption" : { + "output" : "`" + }, + "keyCode" : 50, + "option" : { + "rejection" : "deadKey" + }, + "shift" : { + "output" : "~" + }, + "shiftCaps" : { + "output" : "~" + }, + "shiftCapsOption" : { + "output" : "`" + }, + "shiftOption" : { + "output" : "`" + }, + "unmodified" : { + "output" : "`" + } + }, + "51" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 51, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "52" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 52, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "53" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 53, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "54" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 54, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "55" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 55, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "56" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 56, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "57" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 57, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "58" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 58, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "59" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 59, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "6" : { + "caps" : { + "output" : "Z" + }, + "capsOption" : { + "output" : "Ω" + }, + "keyCode" : 6, + "option" : { + "output" : "Ω" + }, + "shift" : { + "output" : "Z" + }, + "shiftCaps" : { + "output" : "Z" + }, + "shiftCapsOption" : { + "output" : "¸" + }, + "shiftOption" : { + "output" : "¸" + }, + "unmodified" : { + "output" : "z" + } + }, + "60" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 60, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "61" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 61, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "62" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 62, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "63" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 63, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "64" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 64, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "65" : { + "caps" : { + "output" : "." + }, + "capsOption" : { + "output" : "." + }, + "keyCode" : 65, + "option" : { + "output" : "." + }, + "shift" : { + "output" : "." + }, + "shiftCaps" : { + "output" : "." + }, + "shiftCapsOption" : { + "output" : "." + }, + "shiftOption" : { + "output" : "." + }, + "unmodified" : { + "output" : "." + } + }, + "66" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 66, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "*" + }, + "shiftCaps" : { + "output" : "*" + }, + "shiftCapsOption" : { + "output" : "*" + }, + "shiftOption" : { + "output" : "*" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "67" : { + "caps" : { + "output" : "*" + }, + "capsOption" : { + "output" : "*" + }, + "keyCode" : 67, + "option" : { + "output" : "*" + }, + "shift" : { + "output" : "*" + }, + "shiftCaps" : { + "output" : "*" + }, + "shiftCapsOption" : { + "output" : "*" + }, + "shiftOption" : { + "output" : "*" + }, + "unmodified" : { + "output" : "*" + } + }, + "68" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 68, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "69" : { + "caps" : { + "output" : "+" + }, + "capsOption" : { + "output" : "+" + }, + "keyCode" : 69, + "option" : { + "output" : "+" + }, + "shift" : { + "output" : "+" + }, + "shiftCaps" : { + "output" : "+" + }, + "shiftCapsOption" : { + "output" : "+" + }, + "shiftOption" : { + "output" : "+" + }, + "unmodified" : { + "output" : "+" + } + }, + "7" : { + "caps" : { + "output" : "X" + }, + "capsOption" : { + "output" : "≈" + }, + "keyCode" : 7, + "option" : { + "output" : "≈" + }, + "shift" : { + "output" : "X" + }, + "shiftCaps" : { + "output" : "X" + }, + "shiftCapsOption" : { + "output" : "˛" + }, + "shiftOption" : { + "output" : "˛" + }, + "unmodified" : { + "output" : "x" + } + }, + "70" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 70, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "+" + }, + "shiftCaps" : { + "output" : "+" + }, + "shiftCapsOption" : { + "output" : "+" + }, + "shiftOption" : { + "output" : "+" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "71" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 71, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "72" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 72, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "=" + }, + "shiftCaps" : { + "output" : "=" + }, + "shiftCapsOption" : { + "output" : "=" + }, + "shiftOption" : { + "output" : "=" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "73" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 73, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "74" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 74, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "75" : { + "caps" : { + "output" : "\/" + }, + "capsOption" : { + "output" : "\/" + }, + "keyCode" : 75, + "option" : { + "output" : "\/" + }, + "shift" : { + "output" : "\/" + }, + "shiftCaps" : { + "output" : "\/" + }, + "shiftCapsOption" : { + "output" : "\/" + }, + "shiftOption" : { + "output" : "\/" + }, + "unmodified" : { + "output" : "\/" + } + }, + "76" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 76, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "77" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 77, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "\/" + }, + "shiftCaps" : { + "output" : "\/" + }, + "shiftCapsOption" : { + "output" : "\/" + }, + "shiftOption" : { + "output" : "\/" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "78" : { + "caps" : { + "output" : "-" + }, + "capsOption" : { + "output" : "-" + }, + "keyCode" : 78, + "option" : { + "output" : "-" + }, + "shift" : { + "output" : "-" + }, + "shiftCaps" : { + "output" : "-" + }, + "shiftCapsOption" : { + "output" : "-" + }, + "shiftOption" : { + "output" : "-" + }, + "unmodified" : { + "output" : "-" + } + }, + "79" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 79, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "8" : { + "caps" : { + "output" : "C" + }, + "capsOption" : { + "output" : "Ç" + }, + "keyCode" : 8, + "option" : { + "output" : "ç" + }, + "shift" : { + "output" : "C" + }, + "shiftCaps" : { + "output" : "C" + }, + "shiftCapsOption" : { + "output" : "Ç" + }, + "shiftOption" : { + "output" : "Ç" + }, + "unmodified" : { + "output" : "c" + } + }, + "80" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 80, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "81" : { + "caps" : { + "output" : "=" + }, + "capsOption" : { + "output" : "=" + }, + "keyCode" : 81, + "option" : { + "output" : "=" + }, + "shift" : { + "output" : "=" + }, + "shiftCaps" : { + "output" : "=" + }, + "shiftCapsOption" : { + "output" : "=" + }, + "shiftOption" : { + "output" : "=" + }, + "unmodified" : { + "output" : "=" + } + }, + "82" : { + "caps" : { + "output" : "0" + }, + "capsOption" : { + "output" : "0" + }, + "keyCode" : 82, + "option" : { + "output" : "0" + }, + "shift" : { + "output" : "0" + }, + "shiftCaps" : { + "output" : "0" + }, + "shiftCapsOption" : { + "output" : "0" + }, + "shiftOption" : { + "output" : "0" + }, + "unmodified" : { + "output" : "0" + } + }, + "83" : { + "caps" : { + "output" : "1" + }, + "capsOption" : { + "output" : "1" + }, + "keyCode" : 83, + "option" : { + "output" : "1" + }, + "shift" : { + "output" : "1" + }, + "shiftCaps" : { + "output" : "1" + }, + "shiftCapsOption" : { + "output" : "1" + }, + "shiftOption" : { + "output" : "1" + }, + "unmodified" : { + "output" : "1" + } + }, + "84" : { + "caps" : { + "output" : "2" + }, + "capsOption" : { + "output" : "2" + }, + "keyCode" : 84, + "option" : { + "output" : "2" + }, + "shift" : { + "output" : "2" + }, + "shiftCaps" : { + "output" : "2" + }, + "shiftCapsOption" : { + "output" : "2" + }, + "shiftOption" : { + "output" : "2" + }, + "unmodified" : { + "output" : "2" + } + }, + "85" : { + "caps" : { + "output" : "3" + }, + "capsOption" : { + "output" : "3" + }, + "keyCode" : 85, + "option" : { + "output" : "3" + }, + "shift" : { + "output" : "3" + }, + "shiftCaps" : { + "output" : "3" + }, + "shiftCapsOption" : { + "output" : "3" + }, + "shiftOption" : { + "output" : "3" + }, + "unmodified" : { + "output" : "3" + } + }, + "86" : { + "caps" : { + "output" : "4" + }, + "capsOption" : { + "output" : "4" + }, + "keyCode" : 86, + "option" : { + "output" : "4" + }, + "shift" : { + "output" : "4" + }, + "shiftCaps" : { + "output" : "4" + }, + "shiftCapsOption" : { + "output" : "4" + }, + "shiftOption" : { + "output" : "4" + }, + "unmodified" : { + "output" : "4" + } + }, + "87" : { + "caps" : { + "output" : "5" + }, + "capsOption" : { + "output" : "5" + }, + "keyCode" : 87, + "option" : { + "output" : "5" + }, + "shift" : { + "output" : "5" + }, + "shiftCaps" : { + "output" : "5" + }, + "shiftCapsOption" : { + "output" : "5" + }, + "shiftOption" : { + "output" : "5" + }, + "unmodified" : { + "output" : "5" + } + }, + "88" : { + "caps" : { + "output" : "6" + }, + "capsOption" : { + "output" : "6" + }, + "keyCode" : 88, + "option" : { + "output" : "6" + }, + "shift" : { + "output" : "6" + }, + "shiftCaps" : { + "output" : "6" + }, + "shiftCapsOption" : { + "output" : "6" + }, + "shiftOption" : { + "output" : "6" + }, + "unmodified" : { + "output" : "6" + } + }, + "89" : { + "caps" : { + "output" : "7" + }, + "capsOption" : { + "output" : "7" + }, + "keyCode" : 89, + "option" : { + "output" : "7" + }, + "shift" : { + "output" : "7" + }, + "shiftCaps" : { + "output" : "7" + }, + "shiftCapsOption" : { + "output" : "7" + }, + "shiftOption" : { + "output" : "7" + }, + "unmodified" : { + "output" : "7" + } + }, + "9" : { + "caps" : { + "output" : "V" + }, + "capsOption" : { + "output" : "√" + }, + "keyCode" : 9, + "option" : { + "output" : "√" + }, + "shift" : { + "output" : "V" + }, + "shiftCaps" : { + "output" : "V" + }, + "shiftCapsOption" : { + "output" : "◊" + }, + "shiftOption" : { + "output" : "◊" + }, + "unmodified" : { + "output" : "v" + } + }, + "90" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 90, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "91" : { + "caps" : { + "output" : "8" + }, + "capsOption" : { + "output" : "8" + }, + "keyCode" : 91, + "option" : { + "output" : "8" + }, + "shift" : { + "output" : "8" + }, + "shiftCaps" : { + "output" : "8" + }, + "shiftCapsOption" : { + "output" : "8" + }, + "shiftOption" : { + "output" : "8" + }, + "unmodified" : { + "output" : "8" + } + }, + "92" : { + "caps" : { + "output" : "9" + }, + "capsOption" : { + "output" : "9" + }, + "keyCode" : 92, + "option" : { + "output" : "9" + }, + "shift" : { + "output" : "9" + }, + "shiftCaps" : { + "output" : "9" + }, + "shiftCapsOption" : { + "output" : "9" + }, + "shiftOption" : { + "output" : "9" + }, + "unmodified" : { + "output" : "9" + } + }, + "93" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 93, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "94" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 94, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "95" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 95, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "96" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 96, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "97" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 97, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "98" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 98, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "99" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 99, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + } + }, + "keyboardType" : 37, + "osBuild" : "24G830", + "osVersion" : "15.7.9", + "schemaVersion" : 1, + "sourceID" : "com.apple.keylayout.ABC" +} \ No newline at end of file diff --git a/Benchmarks/layout-tables/v1/com.apple.keylayout.ABC@91.json b/Benchmarks/layout-tables/v1/com.apple.keylayout.ABC@91.json new file mode 100644 index 0000000..d9009cd --- /dev/null +++ b/Benchmarks/layout-tables/v1/com.apple.keylayout.ABC@91.json @@ -0,0 +1,3465 @@ +{ + "keyOutputs" : { + "0" : { + "caps" : { + "output" : "A" + }, + "capsOption" : { + "output" : "Å" + }, + "keyCode" : 0, + "option" : { + "output" : "å" + }, + "shift" : { + "output" : "A" + }, + "shiftCaps" : { + "output" : "A" + }, + "shiftCapsOption" : { + "output" : "Å" + }, + "shiftOption" : { + "output" : "Å" + }, + "unmodified" : { + "output" : "a" + } + }, + "1" : { + "caps" : { + "output" : "S" + }, + "capsOption" : { + "output" : "Í" + }, + "keyCode" : 1, + "option" : { + "output" : "ß" + }, + "shift" : { + "output" : "S" + }, + "shiftCaps" : { + "output" : "S" + }, + "shiftCapsOption" : { + "output" : "Í" + }, + "shiftOption" : { + "output" : "Í" + }, + "unmodified" : { + "output" : "s" + } + }, + "10" : { + "caps" : { + "output" : "§" + }, + "capsOption" : { + "output" : "§" + }, + "keyCode" : 10, + "option" : { + "output" : "§" + }, + "shift" : { + "output" : "±" + }, + "shiftCaps" : { + "output" : "±" + }, + "shiftCapsOption" : { + "output" : "±" + }, + "shiftOption" : { + "output" : "±" + }, + "unmodified" : { + "output" : "§" + } + }, + "100" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 100, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "101" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 101, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "102" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 102, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "103" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 103, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "104" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 104, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "105" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 105, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "106" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 106, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "107" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 107, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "108" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 108, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "109" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 109, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "11" : { + "caps" : { + "output" : "B" + }, + "capsOption" : { + "output" : "ı" + }, + "keyCode" : 11, + "option" : { + "output" : "∫" + }, + "shift" : { + "output" : "B" + }, + "shiftCaps" : { + "output" : "B" + }, + "shiftCapsOption" : { + "output" : "ı" + }, + "shiftOption" : { + "output" : "ı" + }, + "unmodified" : { + "output" : "b" + } + }, + "110" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 110, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "111" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 111, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "112" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 112, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "113" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 113, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "114" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 114, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "115" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 115, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "116" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 116, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "117" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 117, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "118" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 118, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "119" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 119, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "12" : { + "caps" : { + "output" : "Q" + }, + "capsOption" : { + "output" : "Œ" + }, + "keyCode" : 12, + "option" : { + "output" : "œ" + }, + "shift" : { + "output" : "Q" + }, + "shiftCaps" : { + "output" : "Q" + }, + "shiftCapsOption" : { + "output" : "Œ" + }, + "shiftOption" : { + "output" : "Œ" + }, + "unmodified" : { + "output" : "q" + } + }, + "120" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 120, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "121" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 121, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "122" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 122, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "123" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 123, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "124" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 124, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "125" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 125, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "126" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 126, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "127" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 127, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "13" : { + "caps" : { + "output" : "W" + }, + "capsOption" : { + "output" : "∑" + }, + "keyCode" : 13, + "option" : { + "output" : "∑" + }, + "shift" : { + "output" : "W" + }, + "shiftCaps" : { + "output" : "W" + }, + "shiftCapsOption" : { + "output" : "„" + }, + "shiftOption" : { + "output" : "„" + }, + "unmodified" : { + "output" : "w" + } + }, + "14" : { + "caps" : { + "output" : "E" + }, + "capsOption" : { + "output" : "´" + }, + "keyCode" : 14, + "option" : { + "rejection" : "deadKey" + }, + "shift" : { + "output" : "E" + }, + "shiftCaps" : { + "output" : "E" + }, + "shiftCapsOption" : { + "output" : "´" + }, + "shiftOption" : { + "output" : "´" + }, + "unmodified" : { + "output" : "e" + } + }, + "15" : { + "caps" : { + "output" : "R" + }, + "capsOption" : { + "output" : "®" + }, + "keyCode" : 15, + "option" : { + "output" : "®" + }, + "shift" : { + "output" : "R" + }, + "shiftCaps" : { + "output" : "R" + }, + "shiftCapsOption" : { + "output" : "‰" + }, + "shiftOption" : { + "output" : "‰" + }, + "unmodified" : { + "output" : "r" + } + }, + "16" : { + "caps" : { + "output" : "Y" + }, + "capsOption" : { + "output" : "Á" + }, + "keyCode" : 16, + "option" : { + "output" : "¥" + }, + "shift" : { + "output" : "Y" + }, + "shiftCaps" : { + "output" : "Y" + }, + "shiftCapsOption" : { + "output" : "Á" + }, + "shiftOption" : { + "output" : "Á" + }, + "unmodified" : { + "output" : "y" + } + }, + "17" : { + "caps" : { + "output" : "T" + }, + "capsOption" : { + "output" : "†" + }, + "keyCode" : 17, + "option" : { + "output" : "†" + }, + "shift" : { + "output" : "T" + }, + "shiftCaps" : { + "output" : "T" + }, + "shiftCapsOption" : { + "output" : "ˇ" + }, + "shiftOption" : { + "output" : "ˇ" + }, + "unmodified" : { + "output" : "t" + } + }, + "18" : { + "caps" : { + "output" : "1" + }, + "capsOption" : { + "output" : "¡" + }, + "keyCode" : 18, + "option" : { + "output" : "¡" + }, + "shift" : { + "output" : "!" + }, + "shiftCaps" : { + "output" : "!" + }, + "shiftCapsOption" : { + "output" : "⁄" + }, + "shiftOption" : { + "output" : "⁄" + }, + "unmodified" : { + "output" : "1" + } + }, + "19" : { + "caps" : { + "output" : "2" + }, + "capsOption" : { + "output" : "™" + }, + "keyCode" : 19, + "option" : { + "output" : "™" + }, + "shift" : { + "output" : "@" + }, + "shiftCaps" : { + "output" : "@" + }, + "shiftCapsOption" : { + "output" : "€" + }, + "shiftOption" : { + "output" : "€" + }, + "unmodified" : { + "output" : "2" + } + }, + "2" : { + "caps" : { + "output" : "D" + }, + "capsOption" : { + "output" : "Î" + }, + "keyCode" : 2, + "option" : { + "output" : "∂" + }, + "shift" : { + "output" : "D" + }, + "shiftCaps" : { + "output" : "D" + }, + "shiftCapsOption" : { + "output" : "Î" + }, + "shiftOption" : { + "output" : "Î" + }, + "unmodified" : { + "output" : "d" + } + }, + "20" : { + "caps" : { + "output" : "3" + }, + "capsOption" : { + "output" : "£" + }, + "keyCode" : 20, + "option" : { + "output" : "£" + }, + "shift" : { + "output" : "#" + }, + "shiftCaps" : { + "output" : "#" + }, + "shiftCapsOption" : { + "output" : "‹" + }, + "shiftOption" : { + "output" : "‹" + }, + "unmodified" : { + "output" : "3" + } + }, + "21" : { + "caps" : { + "output" : "4" + }, + "capsOption" : { + "output" : "¢" + }, + "keyCode" : 21, + "option" : { + "output" : "¢" + }, + "shift" : { + "output" : "$" + }, + "shiftCaps" : { + "output" : "$" + }, + "shiftCapsOption" : { + "output" : "›" + }, + "shiftOption" : { + "output" : "›" + }, + "unmodified" : { + "output" : "4" + } + }, + "22" : { + "caps" : { + "output" : "6" + }, + "capsOption" : { + "output" : "§" + }, + "keyCode" : 22, + "option" : { + "output" : "§" + }, + "shift" : { + "output" : "^" + }, + "shiftCaps" : { + "output" : "^" + }, + "shiftCapsOption" : { + "output" : "fl" + }, + "shiftOption" : { + "output" : "fl" + }, + "unmodified" : { + "output" : "6" + } + }, + "23" : { + "caps" : { + "output" : "5" + }, + "capsOption" : { + "output" : "∞" + }, + "keyCode" : 23, + "option" : { + "output" : "∞" + }, + "shift" : { + "output" : "%" + }, + "shiftCaps" : { + "output" : "%" + }, + "shiftCapsOption" : { + "output" : "fi" + }, + "shiftOption" : { + "output" : "fi" + }, + "unmodified" : { + "output" : "5" + } + }, + "24" : { + "caps" : { + "output" : "=" + }, + "capsOption" : { + "output" : "≠" + }, + "keyCode" : 24, + "option" : { + "output" : "≠" + }, + "shift" : { + "output" : "+" + }, + "shiftCaps" : { + "output" : "+" + }, + "shiftCapsOption" : { + "output" : "±" + }, + "shiftOption" : { + "output" : "±" + }, + "unmodified" : { + "output" : "=" + } + }, + "25" : { + "caps" : { + "output" : "9" + }, + "capsOption" : { + "output" : "ª" + }, + "keyCode" : 25, + "option" : { + "output" : "ª" + }, + "shift" : { + "output" : "(" + }, + "shiftCaps" : { + "output" : "(" + }, + "shiftCapsOption" : { + "output" : "·" + }, + "shiftOption" : { + "output" : "·" + }, + "unmodified" : { + "output" : "9" + } + }, + "26" : { + "caps" : { + "output" : "7" + }, + "capsOption" : { + "output" : "¶" + }, + "keyCode" : 26, + "option" : { + "output" : "¶" + }, + "shift" : { + "output" : "&" + }, + "shiftCaps" : { + "output" : "&" + }, + "shiftCapsOption" : { + "output" : "‡" + }, + "shiftOption" : { + "output" : "‡" + }, + "unmodified" : { + "output" : "7" + } + }, + "27" : { + "caps" : { + "output" : "-" + }, + "capsOption" : { + "output" : "–" + }, + "keyCode" : 27, + "option" : { + "output" : "–" + }, + "shift" : { + "output" : "_" + }, + "shiftCaps" : { + "output" : "_" + }, + "shiftCapsOption" : { + "output" : "—" + }, + "shiftOption" : { + "output" : "—" + }, + "unmodified" : { + "output" : "-" + } + }, + "28" : { + "caps" : { + "output" : "8" + }, + "capsOption" : { + "output" : "•" + }, + "keyCode" : 28, + "option" : { + "output" : "•" + }, + "shift" : { + "output" : "*" + }, + "shiftCaps" : { + "output" : "*" + }, + "shiftCapsOption" : { + "output" : "°" + }, + "shiftOption" : { + "output" : "°" + }, + "unmodified" : { + "output" : "8" + } + }, + "29" : { + "caps" : { + "output" : "0" + }, + "capsOption" : { + "output" : "º" + }, + "keyCode" : 29, + "option" : { + "output" : "º" + }, + "shift" : { + "output" : ")" + }, + "shiftCaps" : { + "output" : ")" + }, + "shiftCapsOption" : { + "output" : "‚" + }, + "shiftOption" : { + "output" : "‚" + }, + "unmodified" : { + "output" : "0" + } + }, + "3" : { + "caps" : { + "output" : "F" + }, + "capsOption" : { + "output" : "Ï" + }, + "keyCode" : 3, + "option" : { + "output" : "ƒ" + }, + "shift" : { + "output" : "F" + }, + "shiftCaps" : { + "output" : "F" + }, + "shiftCapsOption" : { + "output" : "Ï" + }, + "shiftOption" : { + "output" : "Ï" + }, + "unmodified" : { + "output" : "f" + } + }, + "30" : { + "caps" : { + "output" : "]" + }, + "capsOption" : { + "output" : "‘" + }, + "keyCode" : 30, + "option" : { + "output" : "‘" + }, + "shift" : { + "output" : "}" + }, + "shiftCaps" : { + "output" : "}" + }, + "shiftCapsOption" : { + "output" : "’" + }, + "shiftOption" : { + "output" : "’" + }, + "unmodified" : { + "output" : "]" + } + }, + "31" : { + "caps" : { + "output" : "O" + }, + "capsOption" : { + "output" : "Ø" + }, + "keyCode" : 31, + "option" : { + "output" : "ø" + }, + "shift" : { + "output" : "O" + }, + "shiftCaps" : { + "output" : "O" + }, + "shiftCapsOption" : { + "output" : "Ø" + }, + "shiftOption" : { + "output" : "Ø" + }, + "unmodified" : { + "output" : "o" + } + }, + "32" : { + "caps" : { + "output" : "U" + }, + "capsOption" : { + "output" : "¨" + }, + "keyCode" : 32, + "option" : { + "rejection" : "deadKey" + }, + "shift" : { + "output" : "U" + }, + "shiftCaps" : { + "output" : "U" + }, + "shiftCapsOption" : { + "output" : "¨" + }, + "shiftOption" : { + "output" : "¨" + }, + "unmodified" : { + "output" : "u" + } + }, + "33" : { + "caps" : { + "output" : "[" + }, + "capsOption" : { + "output" : "“" + }, + "keyCode" : 33, + "option" : { + "output" : "“" + }, + "shift" : { + "output" : "{" + }, + "shiftCaps" : { + "output" : "{" + }, + "shiftCapsOption" : { + "output" : "”" + }, + "shiftOption" : { + "output" : "”" + }, + "unmodified" : { + "output" : "[" + } + }, + "34" : { + "caps" : { + "output" : "I" + }, + "capsOption" : { + "output" : "ˆ" + }, + "keyCode" : 34, + "option" : { + "rejection" : "deadKey" + }, + "shift" : { + "output" : "I" + }, + "shiftCaps" : { + "output" : "I" + }, + "shiftCapsOption" : { + "output" : "ˆ" + }, + "shiftOption" : { + "output" : "ˆ" + }, + "unmodified" : { + "output" : "i" + } + }, + "35" : { + "caps" : { + "output" : "P" + }, + "capsOption" : { + "output" : "∏" + }, + "keyCode" : 35, + "option" : { + "output" : "π" + }, + "shift" : { + "output" : "P" + }, + "shiftCaps" : { + "output" : "P" + }, + "shiftCapsOption" : { + "output" : "∏" + }, + "shiftOption" : { + "output" : "∏" + }, + "unmodified" : { + "output" : "p" + } + }, + "36" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 36, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "37" : { + "caps" : { + "output" : "L" + }, + "capsOption" : { + "output" : "Ò" + }, + "keyCode" : 37, + "option" : { + "output" : "¬" + }, + "shift" : { + "output" : "L" + }, + "shiftCaps" : { + "output" : "L" + }, + "shiftCapsOption" : { + "output" : "Ò" + }, + "shiftOption" : { + "output" : "Ò" + }, + "unmodified" : { + "output" : "l" + } + }, + "38" : { + "caps" : { + "output" : "J" + }, + "capsOption" : { + "output" : "Ô" + }, + "keyCode" : 38, + "option" : { + "output" : "∆" + }, + "shift" : { + "output" : "J" + }, + "shiftCaps" : { + "output" : "J" + }, + "shiftCapsOption" : { + "output" : "Ô" + }, + "shiftOption" : { + "output" : "Ô" + }, + "unmodified" : { + "output" : "j" + } + }, + "39" : { + "caps" : { + "output" : "'" + }, + "capsOption" : { + "output" : "Æ" + }, + "keyCode" : 39, + "option" : { + "output" : "æ" + }, + "shift" : { + "output" : "\"" + }, + "shiftCaps" : { + "output" : "\"" + }, + "shiftCapsOption" : { + "output" : "Æ" + }, + "shiftOption" : { + "output" : "Æ" + }, + "unmodified" : { + "output" : "'" + } + }, + "4" : { + "caps" : { + "output" : "H" + }, + "capsOption" : { + "output" : "Ó" + }, + "keyCode" : 4, + "option" : { + "output" : "˙" + }, + "shift" : { + "output" : "H" + }, + "shiftCaps" : { + "output" : "H" + }, + "shiftCapsOption" : { + "output" : "Ó" + }, + "shiftOption" : { + "output" : "Ó" + }, + "unmodified" : { + "output" : "h" + } + }, + "40" : { + "caps" : { + "output" : "K" + }, + "capsOption" : { + "output" : "˚" + }, + "keyCode" : 40, + "option" : { + "output" : "˚" + }, + "shift" : { + "output" : "K" + }, + "shiftCaps" : { + "output" : "K" + }, + "shiftCapsOption" : { + "rejection" : "privateUseOrSurrogate" + }, + "shiftOption" : { + "rejection" : "privateUseOrSurrogate" + }, + "unmodified" : { + "output" : "k" + } + }, + "41" : { + "caps" : { + "output" : ";" + }, + "capsOption" : { + "output" : "…" + }, + "keyCode" : 41, + "option" : { + "output" : "…" + }, + "shift" : { + "output" : ":" + }, + "shiftCaps" : { + "output" : ":" + }, + "shiftCapsOption" : { + "output" : "Ú" + }, + "shiftOption" : { + "output" : "Ú" + }, + "unmodified" : { + "output" : ";" + } + }, + "42" : { + "caps" : { + "output" : "\\" + }, + "capsOption" : { + "output" : "«" + }, + "keyCode" : 42, + "option" : { + "output" : "«" + }, + "shift" : { + "output" : "|" + }, + "shiftCaps" : { + "output" : "|" + }, + "shiftCapsOption" : { + "output" : "»" + }, + "shiftOption" : { + "output" : "»" + }, + "unmodified" : { + "output" : "\\" + } + }, + "43" : { + "caps" : { + "output" : "," + }, + "capsOption" : { + "output" : "≤" + }, + "keyCode" : 43, + "option" : { + "output" : "≤" + }, + "shift" : { + "output" : "<" + }, + "shiftCaps" : { + "output" : "<" + }, + "shiftCapsOption" : { + "output" : "¯" + }, + "shiftOption" : { + "output" : "¯" + }, + "unmodified" : { + "output" : "," + } + }, + "44" : { + "caps" : { + "output" : "\/" + }, + "capsOption" : { + "output" : "÷" + }, + "keyCode" : 44, + "option" : { + "output" : "÷" + }, + "shift" : { + "output" : "?" + }, + "shiftCaps" : { + "output" : "?" + }, + "shiftCapsOption" : { + "output" : "¿" + }, + "shiftOption" : { + "output" : "¿" + }, + "unmodified" : { + "output" : "\/" + } + }, + "45" : { + "caps" : { + "output" : "N" + }, + "capsOption" : { + "output" : "˜" + }, + "keyCode" : 45, + "option" : { + "rejection" : "deadKey" + }, + "shift" : { + "output" : "N" + }, + "shiftCaps" : { + "output" : "N" + }, + "shiftCapsOption" : { + "output" : "˜" + }, + "shiftOption" : { + "output" : "˜" + }, + "unmodified" : { + "output" : "n" + } + }, + "46" : { + "caps" : { + "output" : "M" + }, + "capsOption" : { + "output" : "Â" + }, + "keyCode" : 46, + "option" : { + "output" : "µ" + }, + "shift" : { + "output" : "M" + }, + "shiftCaps" : { + "output" : "M" + }, + "shiftCapsOption" : { + "output" : "Â" + }, + "shiftOption" : { + "output" : "Â" + }, + "unmodified" : { + "output" : "m" + } + }, + "47" : { + "caps" : { + "output" : "." + }, + "capsOption" : { + "output" : "≥" + }, + "keyCode" : 47, + "option" : { + "output" : "≥" + }, + "shift" : { + "output" : ">" + }, + "shiftCaps" : { + "output" : ">" + }, + "shiftCapsOption" : { + "output" : "˘" + }, + "shiftOption" : { + "output" : "˘" + }, + "unmodified" : { + "output" : "." + } + }, + "48" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 48, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "49" : { + "caps" : { + "output" : " " + }, + "capsOption" : { + "output" : " " + }, + "keyCode" : 49, + "option" : { + "output" : " " + }, + "shift" : { + "output" : " " + }, + "shiftCaps" : { + "output" : " " + }, + "shiftCapsOption" : { + "output" : " " + }, + "shiftOption" : { + "output" : " " + }, + "unmodified" : { + "output" : " " + } + }, + "5" : { + "caps" : { + "output" : "G" + }, + "capsOption" : { + "output" : "©" + }, + "keyCode" : 5, + "option" : { + "output" : "©" + }, + "shift" : { + "output" : "G" + }, + "shiftCaps" : { + "output" : "G" + }, + "shiftCapsOption" : { + "output" : "˝" + }, + "shiftOption" : { + "output" : "˝" + }, + "unmodified" : { + "output" : "g" + } + }, + "50" : { + "caps" : { + "output" : "`" + }, + "capsOption" : { + "output" : "`" + }, + "keyCode" : 50, + "option" : { + "rejection" : "deadKey" + }, + "shift" : { + "output" : "~" + }, + "shiftCaps" : { + "output" : "~" + }, + "shiftCapsOption" : { + "output" : "`" + }, + "shiftOption" : { + "output" : "`" + }, + "unmodified" : { + "output" : "`" + } + }, + "51" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 51, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "52" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 52, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "53" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 53, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "54" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 54, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "55" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 55, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "56" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 56, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "57" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 57, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "58" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 58, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "59" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 59, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "6" : { + "caps" : { + "output" : "Z" + }, + "capsOption" : { + "output" : "Ω" + }, + "keyCode" : 6, + "option" : { + "output" : "Ω" + }, + "shift" : { + "output" : "Z" + }, + "shiftCaps" : { + "output" : "Z" + }, + "shiftCapsOption" : { + "output" : "¸" + }, + "shiftOption" : { + "output" : "¸" + }, + "unmodified" : { + "output" : "z" + } + }, + "60" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 60, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "61" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 61, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "62" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 62, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "63" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 63, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "64" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 64, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "65" : { + "caps" : { + "output" : "." + }, + "capsOption" : { + "output" : "." + }, + "keyCode" : 65, + "option" : { + "output" : "." + }, + "shift" : { + "output" : "." + }, + "shiftCaps" : { + "output" : "." + }, + "shiftCapsOption" : { + "output" : "." + }, + "shiftOption" : { + "output" : "." + }, + "unmodified" : { + "output" : "." + } + }, + "66" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 66, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "*" + }, + "shiftCaps" : { + "output" : "*" + }, + "shiftCapsOption" : { + "output" : "*" + }, + "shiftOption" : { + "output" : "*" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "67" : { + "caps" : { + "output" : "*" + }, + "capsOption" : { + "output" : "*" + }, + "keyCode" : 67, + "option" : { + "output" : "*" + }, + "shift" : { + "output" : "*" + }, + "shiftCaps" : { + "output" : "*" + }, + "shiftCapsOption" : { + "output" : "*" + }, + "shiftOption" : { + "output" : "*" + }, + "unmodified" : { + "output" : "*" + } + }, + "68" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 68, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "69" : { + "caps" : { + "output" : "+" + }, + "capsOption" : { + "output" : "+" + }, + "keyCode" : 69, + "option" : { + "output" : "+" + }, + "shift" : { + "output" : "+" + }, + "shiftCaps" : { + "output" : "+" + }, + "shiftCapsOption" : { + "output" : "+" + }, + "shiftOption" : { + "output" : "+" + }, + "unmodified" : { + "output" : "+" + } + }, + "7" : { + "caps" : { + "output" : "X" + }, + "capsOption" : { + "output" : "≈" + }, + "keyCode" : 7, + "option" : { + "output" : "≈" + }, + "shift" : { + "output" : "X" + }, + "shiftCaps" : { + "output" : "X" + }, + "shiftCapsOption" : { + "output" : "˛" + }, + "shiftOption" : { + "output" : "˛" + }, + "unmodified" : { + "output" : "x" + } + }, + "70" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 70, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "+" + }, + "shiftCaps" : { + "output" : "+" + }, + "shiftCapsOption" : { + "output" : "+" + }, + "shiftOption" : { + "output" : "+" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "71" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 71, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "72" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 72, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "=" + }, + "shiftCaps" : { + "output" : "=" + }, + "shiftCapsOption" : { + "output" : "=" + }, + "shiftOption" : { + "output" : "=" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "73" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 73, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "74" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 74, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "75" : { + "caps" : { + "output" : "\/" + }, + "capsOption" : { + "output" : "\/" + }, + "keyCode" : 75, + "option" : { + "output" : "\/" + }, + "shift" : { + "output" : "\/" + }, + "shiftCaps" : { + "output" : "\/" + }, + "shiftCapsOption" : { + "output" : "\/" + }, + "shiftOption" : { + "output" : "\/" + }, + "unmodified" : { + "output" : "\/" + } + }, + "76" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 76, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "77" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 77, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "\/" + }, + "shiftCaps" : { + "output" : "\/" + }, + "shiftCapsOption" : { + "output" : "\/" + }, + "shiftOption" : { + "output" : "\/" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "78" : { + "caps" : { + "output" : "-" + }, + "capsOption" : { + "output" : "-" + }, + "keyCode" : 78, + "option" : { + "output" : "-" + }, + "shift" : { + "output" : "-" + }, + "shiftCaps" : { + "output" : "-" + }, + "shiftCapsOption" : { + "output" : "-" + }, + "shiftOption" : { + "output" : "-" + }, + "unmodified" : { + "output" : "-" + } + }, + "79" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 79, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "8" : { + "caps" : { + "output" : "C" + }, + "capsOption" : { + "output" : "Ç" + }, + "keyCode" : 8, + "option" : { + "output" : "ç" + }, + "shift" : { + "output" : "C" + }, + "shiftCaps" : { + "output" : "C" + }, + "shiftCapsOption" : { + "output" : "Ç" + }, + "shiftOption" : { + "output" : "Ç" + }, + "unmodified" : { + "output" : "c" + } + }, + "80" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 80, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "81" : { + "caps" : { + "output" : "=" + }, + "capsOption" : { + "output" : "=" + }, + "keyCode" : 81, + "option" : { + "output" : "=" + }, + "shift" : { + "output" : "=" + }, + "shiftCaps" : { + "output" : "=" + }, + "shiftCapsOption" : { + "output" : "=" + }, + "shiftOption" : { + "output" : "=" + }, + "unmodified" : { + "output" : "=" + } + }, + "82" : { + "caps" : { + "output" : "0" + }, + "capsOption" : { + "output" : "0" + }, + "keyCode" : 82, + "option" : { + "output" : "0" + }, + "shift" : { + "output" : "0" + }, + "shiftCaps" : { + "output" : "0" + }, + "shiftCapsOption" : { + "output" : "0" + }, + "shiftOption" : { + "output" : "0" + }, + "unmodified" : { + "output" : "0" + } + }, + "83" : { + "caps" : { + "output" : "1" + }, + "capsOption" : { + "output" : "1" + }, + "keyCode" : 83, + "option" : { + "output" : "1" + }, + "shift" : { + "output" : "1" + }, + "shiftCaps" : { + "output" : "1" + }, + "shiftCapsOption" : { + "output" : "1" + }, + "shiftOption" : { + "output" : "1" + }, + "unmodified" : { + "output" : "1" + } + }, + "84" : { + "caps" : { + "output" : "2" + }, + "capsOption" : { + "output" : "2" + }, + "keyCode" : 84, + "option" : { + "output" : "2" + }, + "shift" : { + "output" : "2" + }, + "shiftCaps" : { + "output" : "2" + }, + "shiftCapsOption" : { + "output" : "2" + }, + "shiftOption" : { + "output" : "2" + }, + "unmodified" : { + "output" : "2" + } + }, + "85" : { + "caps" : { + "output" : "3" + }, + "capsOption" : { + "output" : "3" + }, + "keyCode" : 85, + "option" : { + "output" : "3" + }, + "shift" : { + "output" : "3" + }, + "shiftCaps" : { + "output" : "3" + }, + "shiftCapsOption" : { + "output" : "3" + }, + "shiftOption" : { + "output" : "3" + }, + "unmodified" : { + "output" : "3" + } + }, + "86" : { + "caps" : { + "output" : "4" + }, + "capsOption" : { + "output" : "4" + }, + "keyCode" : 86, + "option" : { + "output" : "4" + }, + "shift" : { + "output" : "4" + }, + "shiftCaps" : { + "output" : "4" + }, + "shiftCapsOption" : { + "output" : "4" + }, + "shiftOption" : { + "output" : "4" + }, + "unmodified" : { + "output" : "4" + } + }, + "87" : { + "caps" : { + "output" : "5" + }, + "capsOption" : { + "output" : "5" + }, + "keyCode" : 87, + "option" : { + "output" : "5" + }, + "shift" : { + "output" : "5" + }, + "shiftCaps" : { + "output" : "5" + }, + "shiftCapsOption" : { + "output" : "5" + }, + "shiftOption" : { + "output" : "5" + }, + "unmodified" : { + "output" : "5" + } + }, + "88" : { + "caps" : { + "output" : "6" + }, + "capsOption" : { + "output" : "6" + }, + "keyCode" : 88, + "option" : { + "output" : "6" + }, + "shift" : { + "output" : "6" + }, + "shiftCaps" : { + "output" : "6" + }, + "shiftCapsOption" : { + "output" : "6" + }, + "shiftOption" : { + "output" : "6" + }, + "unmodified" : { + "output" : "6" + } + }, + "89" : { + "caps" : { + "output" : "7" + }, + "capsOption" : { + "output" : "7" + }, + "keyCode" : 89, + "option" : { + "output" : "7" + }, + "shift" : { + "output" : "7" + }, + "shiftCaps" : { + "output" : "7" + }, + "shiftCapsOption" : { + "output" : "7" + }, + "shiftOption" : { + "output" : "7" + }, + "unmodified" : { + "output" : "7" + } + }, + "9" : { + "caps" : { + "output" : "V" + }, + "capsOption" : { + "output" : "√" + }, + "keyCode" : 9, + "option" : { + "output" : "√" + }, + "shift" : { + "output" : "V" + }, + "shiftCaps" : { + "output" : "V" + }, + "shiftCapsOption" : { + "output" : "◊" + }, + "shiftOption" : { + "output" : "◊" + }, + "unmodified" : { + "output" : "v" + } + }, + "90" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 90, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "91" : { + "caps" : { + "output" : "8" + }, + "capsOption" : { + "output" : "8" + }, + "keyCode" : 91, + "option" : { + "output" : "8" + }, + "shift" : { + "output" : "8" + }, + "shiftCaps" : { + "output" : "8" + }, + "shiftCapsOption" : { + "output" : "8" + }, + "shiftOption" : { + "output" : "8" + }, + "unmodified" : { + "output" : "8" + } + }, + "92" : { + "caps" : { + "output" : "9" + }, + "capsOption" : { + "output" : "9" + }, + "keyCode" : 92, + "option" : { + "output" : "9" + }, + "shift" : { + "output" : "9" + }, + "shiftCaps" : { + "output" : "9" + }, + "shiftCapsOption" : { + "output" : "9" + }, + "shiftOption" : { + "output" : "9" + }, + "unmodified" : { + "output" : "9" + } + }, + "93" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 93, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "94" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 94, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "95" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 95, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "96" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 96, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "97" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 97, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "98" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 98, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "99" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 99, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + } + }, + "keyboardType" : 91, + "osBuild" : "24G830", + "osVersion" : "15.7.9", + "schemaVersion" : 1, + "sourceID" : "com.apple.keylayout.ABC" +} \ No newline at end of file diff --git a/Benchmarks/layout-tables/v1/com.apple.keylayout.British@37.json b/Benchmarks/layout-tables/v1/com.apple.keylayout.British@37.json new file mode 100644 index 0000000..5e2588c --- /dev/null +++ b/Benchmarks/layout-tables/v1/com.apple.keylayout.British@37.json @@ -0,0 +1,3465 @@ +{ + "keyOutputs" : { + "0" : { + "caps" : { + "output" : "A" + }, + "capsOption" : { + "output" : "Å" + }, + "keyCode" : 0, + "option" : { + "output" : "å" + }, + "shift" : { + "output" : "A" + }, + "shiftCaps" : { + "output" : "A" + }, + "shiftCapsOption" : { + "output" : "Å" + }, + "shiftOption" : { + "output" : "Å" + }, + "unmodified" : { + "output" : "a" + } + }, + "1" : { + "caps" : { + "output" : "S" + }, + "capsOption" : { + "output" : "Í" + }, + "keyCode" : 1, + "option" : { + "output" : "ß" + }, + "shift" : { + "output" : "S" + }, + "shiftCaps" : { + "output" : "S" + }, + "shiftCapsOption" : { + "output" : "Í" + }, + "shiftOption" : { + "output" : "Í" + }, + "unmodified" : { + "output" : "s" + } + }, + "10" : { + "caps" : { + "output" : "§" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 10, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "output" : "±" + }, + "shiftCaps" : { + "output" : "±" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "output" : "§" + } + }, + "100" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 100, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "101" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 101, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "102" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 102, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "103" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 103, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "104" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 104, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "105" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 105, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "106" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 106, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "107" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 107, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "108" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 108, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "109" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 109, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "11" : { + "caps" : { + "output" : "B" + }, + "capsOption" : { + "output" : "ı" + }, + "keyCode" : 11, + "option" : { + "output" : "∫" + }, + "shift" : { + "output" : "B" + }, + "shiftCaps" : { + "output" : "B" + }, + "shiftCapsOption" : { + "output" : "ı" + }, + "shiftOption" : { + "output" : "ı" + }, + "unmodified" : { + "output" : "b" + } + }, + "110" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 110, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "111" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 111, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "112" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 112, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "113" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 113, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "114" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 114, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "115" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 115, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "116" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 116, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "117" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 117, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "118" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 118, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "119" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 119, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "12" : { + "caps" : { + "output" : "Q" + }, + "capsOption" : { + "output" : "Œ" + }, + "keyCode" : 12, + "option" : { + "output" : "œ" + }, + "shift" : { + "output" : "Q" + }, + "shiftCaps" : { + "output" : "Q" + }, + "shiftCapsOption" : { + "output" : "Œ" + }, + "shiftOption" : { + "output" : "Œ" + }, + "unmodified" : { + "output" : "q" + } + }, + "120" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 120, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "121" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 121, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "122" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 122, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "123" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 123, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "124" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 124, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "125" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 125, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "126" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 126, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "127" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 127, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "13" : { + "caps" : { + "output" : "W" + }, + "capsOption" : { + "output" : "„" + }, + "keyCode" : 13, + "option" : { + "output" : "∑" + }, + "shift" : { + "output" : "W" + }, + "shiftCaps" : { + "output" : "W" + }, + "shiftCapsOption" : { + "output" : "„" + }, + "shiftOption" : { + "output" : "„" + }, + "unmodified" : { + "output" : "w" + } + }, + "14" : { + "caps" : { + "output" : "E" + }, + "capsOption" : { + "output" : "‰" + }, + "keyCode" : 14, + "option" : { + "rejection" : "deadKey" + }, + "shift" : { + "output" : "E" + }, + "shiftCaps" : { + "output" : "E" + }, + "shiftCapsOption" : { + "output" : "‰" + }, + "shiftOption" : { + "output" : "‰" + }, + "unmodified" : { + "output" : "e" + } + }, + "15" : { + "caps" : { + "output" : "R" + }, + "capsOption" : { + "output" : "Â" + }, + "keyCode" : 15, + "option" : { + "output" : "®" + }, + "shift" : { + "output" : "R" + }, + "shiftCaps" : { + "output" : "R" + }, + "shiftCapsOption" : { + "output" : "Â" + }, + "shiftOption" : { + "output" : "Â" + }, + "unmodified" : { + "output" : "r" + } + }, + "16" : { + "caps" : { + "output" : "Y" + }, + "capsOption" : { + "output" : "Á" + }, + "keyCode" : 16, + "option" : { + "output" : "¥" + }, + "shift" : { + "output" : "Y" + }, + "shiftCaps" : { + "output" : "Y" + }, + "shiftCapsOption" : { + "output" : "Á" + }, + "shiftOption" : { + "output" : "Á" + }, + "unmodified" : { + "output" : "y" + } + }, + "17" : { + "caps" : { + "output" : "T" + }, + "capsOption" : { + "output" : "Ê" + }, + "keyCode" : 17, + "option" : { + "output" : "†" + }, + "shift" : { + "output" : "T" + }, + "shiftCaps" : { + "output" : "T" + }, + "shiftCapsOption" : { + "output" : "Ê" + }, + "shiftOption" : { + "output" : "Ê" + }, + "unmodified" : { + "output" : "t" + } + }, + "18" : { + "caps" : { + "output" : "1" + }, + "capsOption" : { + "output" : "¡" + }, + "keyCode" : 18, + "option" : { + "output" : "¡" + }, + "shift" : { + "output" : "!" + }, + "shiftCaps" : { + "output" : "!" + }, + "shiftCapsOption" : { + "output" : "⁄" + }, + "shiftOption" : { + "output" : "⁄" + }, + "unmodified" : { + "output" : "1" + } + }, + "19" : { + "caps" : { + "output" : "2" + }, + "capsOption" : { + "output" : "™" + }, + "keyCode" : 19, + "option" : { + "output" : "€" + }, + "shift" : { + "output" : "@" + }, + "shiftCaps" : { + "output" : "@" + }, + "shiftCapsOption" : { + "output" : "™" + }, + "shiftOption" : { + "output" : "™" + }, + "unmodified" : { + "output" : "2" + } + }, + "2" : { + "caps" : { + "output" : "D" + }, + "capsOption" : { + "output" : "Î" + }, + "keyCode" : 2, + "option" : { + "output" : "∂" + }, + "shift" : { + "output" : "D" + }, + "shiftCaps" : { + "output" : "D" + }, + "shiftCapsOption" : { + "output" : "Î" + }, + "shiftOption" : { + "output" : "Î" + }, + "unmodified" : { + "output" : "d" + } + }, + "20" : { + "caps" : { + "output" : "3" + }, + "capsOption" : { + "output" : "#" + }, + "keyCode" : 20, + "option" : { + "output" : "#" + }, + "shift" : { + "output" : "£" + }, + "shiftCaps" : { + "output" : "£" + }, + "shiftCapsOption" : { + "output" : "‹" + }, + "shiftOption" : { + "output" : "‹" + }, + "unmodified" : { + "output" : "3" + } + }, + "21" : { + "caps" : { + "output" : "4" + }, + "capsOption" : { + "output" : "¢" + }, + "keyCode" : 21, + "option" : { + "output" : "¢" + }, + "shift" : { + "output" : "$" + }, + "shiftCaps" : { + "output" : "$" + }, + "shiftCapsOption" : { + "output" : "›" + }, + "shiftOption" : { + "output" : "›" + }, + "unmodified" : { + "output" : "4" + } + }, + "22" : { + "caps" : { + "output" : "6" + }, + "capsOption" : { + "output" : "§" + }, + "keyCode" : 22, + "option" : { + "output" : "§" + }, + "shift" : { + "output" : "^" + }, + "shiftCaps" : { + "output" : "^" + }, + "shiftCapsOption" : { + "output" : "fl" + }, + "shiftOption" : { + "output" : "fl" + }, + "unmodified" : { + "output" : "6" + } + }, + "23" : { + "caps" : { + "output" : "5" + }, + "capsOption" : { + "output" : "∞" + }, + "keyCode" : 23, + "option" : { + "output" : "∞" + }, + "shift" : { + "output" : "%" + }, + "shiftCaps" : { + "output" : "%" + }, + "shiftCapsOption" : { + "output" : "fi" + }, + "shiftOption" : { + "output" : "fi" + }, + "unmodified" : { + "output" : "5" + } + }, + "24" : { + "caps" : { + "output" : "=" + }, + "capsOption" : { + "output" : "≠" + }, + "keyCode" : 24, + "option" : { + "output" : "≠" + }, + "shift" : { + "output" : "+" + }, + "shiftCaps" : { + "output" : "+" + }, + "shiftCapsOption" : { + "output" : "±" + }, + "shiftOption" : { + "output" : "±" + }, + "unmodified" : { + "output" : "=" + } + }, + "25" : { + "caps" : { + "output" : "9" + }, + "capsOption" : { + "output" : "ª" + }, + "keyCode" : 25, + "option" : { + "output" : "ª" + }, + "shift" : { + "output" : "(" + }, + "shiftCaps" : { + "output" : "(" + }, + "shiftCapsOption" : { + "output" : "·" + }, + "shiftOption" : { + "output" : "·" + }, + "unmodified" : { + "output" : "9" + } + }, + "26" : { + "caps" : { + "output" : "7" + }, + "capsOption" : { + "output" : "¶" + }, + "keyCode" : 26, + "option" : { + "output" : "¶" + }, + "shift" : { + "output" : "&" + }, + "shiftCaps" : { + "output" : "&" + }, + "shiftCapsOption" : { + "output" : "‡" + }, + "shiftOption" : { + "output" : "‡" + }, + "unmodified" : { + "output" : "7" + } + }, + "27" : { + "caps" : { + "output" : "-" + }, + "capsOption" : { + "output" : "–" + }, + "keyCode" : 27, + "option" : { + "output" : "–" + }, + "shift" : { + "output" : "_" + }, + "shiftCaps" : { + "output" : "_" + }, + "shiftCapsOption" : { + "output" : "—" + }, + "shiftOption" : { + "output" : "—" + }, + "unmodified" : { + "output" : "-" + } + }, + "28" : { + "caps" : { + "output" : "8" + }, + "capsOption" : { + "output" : "•" + }, + "keyCode" : 28, + "option" : { + "output" : "•" + }, + "shift" : { + "output" : "*" + }, + "shiftCaps" : { + "output" : "*" + }, + "shiftCapsOption" : { + "output" : "°" + }, + "shiftOption" : { + "output" : "°" + }, + "unmodified" : { + "output" : "8" + } + }, + "29" : { + "caps" : { + "output" : "0" + }, + "capsOption" : { + "output" : "º" + }, + "keyCode" : 29, + "option" : { + "output" : "º" + }, + "shift" : { + "output" : ")" + }, + "shiftCaps" : { + "output" : ")" + }, + "shiftCapsOption" : { + "output" : "‚" + }, + "shiftOption" : { + "output" : "‚" + }, + "unmodified" : { + "output" : "0" + } + }, + "3" : { + "caps" : { + "output" : "F" + }, + "capsOption" : { + "output" : "Ï" + }, + "keyCode" : 3, + "option" : { + "output" : "ƒ" + }, + "shift" : { + "output" : "F" + }, + "shiftCaps" : { + "output" : "F" + }, + "shiftCapsOption" : { + "output" : "Ï" + }, + "shiftOption" : { + "output" : "Ï" + }, + "unmodified" : { + "output" : "f" + } + }, + "30" : { + "caps" : { + "output" : "]" + }, + "capsOption" : { + "output" : "‘" + }, + "keyCode" : 30, + "option" : { + "output" : "‘" + }, + "shift" : { + "output" : "}" + }, + "shiftCaps" : { + "output" : "}" + }, + "shiftCapsOption" : { + "output" : "’" + }, + "shiftOption" : { + "output" : "’" + }, + "unmodified" : { + "output" : "]" + } + }, + "31" : { + "caps" : { + "output" : "O" + }, + "capsOption" : { + "output" : "Ø" + }, + "keyCode" : 31, + "option" : { + "output" : "ø" + }, + "shift" : { + "output" : "O" + }, + "shiftCaps" : { + "output" : "O" + }, + "shiftCapsOption" : { + "output" : "Ø" + }, + "shiftOption" : { + "output" : "Ø" + }, + "unmodified" : { + "output" : "o" + } + }, + "32" : { + "caps" : { + "output" : "U" + }, + "capsOption" : { + "output" : "Ë" + }, + "keyCode" : 32, + "option" : { + "rejection" : "deadKey" + }, + "shift" : { + "output" : "U" + }, + "shiftCaps" : { + "output" : "U" + }, + "shiftCapsOption" : { + "output" : "Ë" + }, + "shiftOption" : { + "output" : "Ë" + }, + "unmodified" : { + "output" : "u" + } + }, + "33" : { + "caps" : { + "output" : "[" + }, + "capsOption" : { + "output" : "“" + }, + "keyCode" : 33, + "option" : { + "output" : "“" + }, + "shift" : { + "output" : "{" + }, + "shiftCaps" : { + "output" : "{" + }, + "shiftCapsOption" : { + "output" : "”" + }, + "shiftOption" : { + "output" : "”" + }, + "unmodified" : { + "output" : "[" + } + }, + "34" : { + "caps" : { + "output" : "I" + }, + "capsOption" : { + "output" : "È" + }, + "keyCode" : 34, + "option" : { + "rejection" : "deadKey" + }, + "shift" : { + "output" : "I" + }, + "shiftCaps" : { + "output" : "I" + }, + "shiftCapsOption" : { + "output" : "È" + }, + "shiftOption" : { + "output" : "È" + }, + "unmodified" : { + "output" : "i" + } + }, + "35" : { + "caps" : { + "output" : "P" + }, + "capsOption" : { + "output" : "∏" + }, + "keyCode" : 35, + "option" : { + "output" : "π" + }, + "shift" : { + "output" : "P" + }, + "shiftCaps" : { + "output" : "P" + }, + "shiftCapsOption" : { + "output" : "∏" + }, + "shiftOption" : { + "output" : "∏" + }, + "unmodified" : { + "output" : "p" + } + }, + "36" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 36, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "37" : { + "caps" : { + "output" : "L" + }, + "capsOption" : { + "output" : "Ò" + }, + "keyCode" : 37, + "option" : { + "output" : "¬" + }, + "shift" : { + "output" : "L" + }, + "shiftCaps" : { + "output" : "L" + }, + "shiftCapsOption" : { + "output" : "Ò" + }, + "shiftOption" : { + "output" : "Ò" + }, + "unmodified" : { + "output" : "l" + } + }, + "38" : { + "caps" : { + "output" : "J" + }, + "capsOption" : { + "output" : "Ô" + }, + "keyCode" : 38, + "option" : { + "output" : "∆" + }, + "shift" : { + "output" : "J" + }, + "shiftCaps" : { + "output" : "J" + }, + "shiftCapsOption" : { + "output" : "Ô" + }, + "shiftOption" : { + "output" : "Ô" + }, + "unmodified" : { + "output" : "j" + } + }, + "39" : { + "caps" : { + "output" : "'" + }, + "capsOption" : { + "output" : "Æ" + }, + "keyCode" : 39, + "option" : { + "output" : "æ" + }, + "shift" : { + "output" : "\"" + }, + "shiftCaps" : { + "output" : "\"" + }, + "shiftCapsOption" : { + "output" : "Æ" + }, + "shiftOption" : { + "output" : "Æ" + }, + "unmodified" : { + "output" : "'" + } + }, + "4" : { + "caps" : { + "output" : "H" + }, + "capsOption" : { + "output" : "Ó" + }, + "keyCode" : 4, + "option" : { + "output" : "˙" + }, + "shift" : { + "output" : "H" + }, + "shiftCaps" : { + "output" : "H" + }, + "shiftCapsOption" : { + "output" : "Ó" + }, + "shiftOption" : { + "output" : "Ó" + }, + "unmodified" : { + "output" : "h" + } + }, + "40" : { + "caps" : { + "output" : "K" + }, + "capsOption" : { + "rejection" : "privateUseOrSurrogate" + }, + "keyCode" : 40, + "option" : { + "output" : "˚" + }, + "shift" : { + "output" : "K" + }, + "shiftCaps" : { + "output" : "K" + }, + "shiftCapsOption" : { + "rejection" : "privateUseOrSurrogate" + }, + "shiftOption" : { + "rejection" : "privateUseOrSurrogate" + }, + "unmodified" : { + "output" : "k" + } + }, + "41" : { + "caps" : { + "output" : ";" + }, + "capsOption" : { + "output" : "…" + }, + "keyCode" : 41, + "option" : { + "output" : "…" + }, + "shift" : { + "output" : ":" + }, + "shiftCaps" : { + "output" : ":" + }, + "shiftCapsOption" : { + "output" : "Ú" + }, + "shiftOption" : { + "output" : "Ú" + }, + "unmodified" : { + "output" : ";" + } + }, + "42" : { + "caps" : { + "output" : "\\" + }, + "capsOption" : { + "output" : "«" + }, + "keyCode" : 42, + "option" : { + "output" : "«" + }, + "shift" : { + "output" : "|" + }, + "shiftCaps" : { + "output" : "|" + }, + "shiftCapsOption" : { + "output" : "»" + }, + "shiftOption" : { + "output" : "»" + }, + "unmodified" : { + "output" : "\\" + } + }, + "43" : { + "caps" : { + "output" : "," + }, + "capsOption" : { + "output" : "≤" + }, + "keyCode" : 43, + "option" : { + "output" : "≤" + }, + "shift" : { + "output" : "<" + }, + "shiftCaps" : { + "output" : "<" + }, + "shiftCapsOption" : { + "output" : "¯" + }, + "shiftOption" : { + "output" : "¯" + }, + "unmodified" : { + "output" : "," + } + }, + "44" : { + "caps" : { + "output" : "\/" + }, + "capsOption" : { + "output" : "÷" + }, + "keyCode" : 44, + "option" : { + "output" : "÷" + }, + "shift" : { + "output" : "?" + }, + "shiftCaps" : { + "output" : "?" + }, + "shiftCapsOption" : { + "output" : "¿" + }, + "shiftOption" : { + "output" : "¿" + }, + "unmodified" : { + "output" : "\/" + } + }, + "45" : { + "caps" : { + "output" : "N" + }, + "capsOption" : { + "output" : "ˆ" + }, + "keyCode" : 45, + "option" : { + "rejection" : "deadKey" + }, + "shift" : { + "output" : "N" + }, + "shiftCaps" : { + "output" : "N" + }, + "shiftCapsOption" : { + "output" : "ˆ" + }, + "shiftOption" : { + "output" : "ˆ" + }, + "unmodified" : { + "output" : "n" + } + }, + "46" : { + "caps" : { + "output" : "M" + }, + "capsOption" : { + "output" : "˜" + }, + "keyCode" : 46, + "option" : { + "output" : "µ" + }, + "shift" : { + "output" : "M" + }, + "shiftCaps" : { + "output" : "M" + }, + "shiftCapsOption" : { + "output" : "˜" + }, + "shiftOption" : { + "output" : "˜" + }, + "unmodified" : { + "output" : "m" + } + }, + "47" : { + "caps" : { + "output" : "." + }, + "capsOption" : { + "output" : "≥" + }, + "keyCode" : 47, + "option" : { + "output" : "≥" + }, + "shift" : { + "output" : ">" + }, + "shiftCaps" : { + "output" : ">" + }, + "shiftCapsOption" : { + "output" : "˘" + }, + "shiftOption" : { + "output" : "˘" + }, + "unmodified" : { + "output" : "." + } + }, + "48" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 48, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "49" : { + "caps" : { + "output" : " " + }, + "capsOption" : { + "output" : " " + }, + "keyCode" : 49, + "option" : { + "output" : " " + }, + "shift" : { + "output" : " " + }, + "shiftCaps" : { + "output" : " " + }, + "shiftCapsOption" : { + "output" : " " + }, + "shiftOption" : { + "output" : " " + }, + "unmodified" : { + "output" : " " + } + }, + "5" : { + "caps" : { + "output" : "G" + }, + "capsOption" : { + "output" : "Ì" + }, + "keyCode" : 5, + "option" : { + "output" : "©" + }, + "shift" : { + "output" : "G" + }, + "shiftCaps" : { + "output" : "G" + }, + "shiftCapsOption" : { + "output" : "Ì" + }, + "shiftOption" : { + "output" : "Ì" + }, + "unmodified" : { + "output" : "g" + } + }, + "50" : { + "caps" : { + "output" : "`" + }, + "capsOption" : { + "output" : "`" + }, + "keyCode" : 50, + "option" : { + "rejection" : "deadKey" + }, + "shift" : { + "output" : "~" + }, + "shiftCaps" : { + "output" : "~" + }, + "shiftCapsOption" : { + "output" : "Ÿ" + }, + "shiftOption" : { + "output" : "Ÿ" + }, + "unmodified" : { + "output" : "`" + } + }, + "51" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 51, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "52" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 52, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "53" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 53, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "54" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 54, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "55" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 55, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "56" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 56, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "57" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 57, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "58" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 58, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "59" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 59, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "6" : { + "caps" : { + "output" : "Z" + }, + "capsOption" : { + "output" : "Û" + }, + "keyCode" : 6, + "option" : { + "output" : "Ω" + }, + "shift" : { + "output" : "Z" + }, + "shiftCaps" : { + "output" : "Z" + }, + "shiftCapsOption" : { + "output" : "Û" + }, + "shiftOption" : { + "output" : "Û" + }, + "unmodified" : { + "output" : "z" + } + }, + "60" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 60, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "61" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 61, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "62" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 62, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "63" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 63, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "64" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 64, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "65" : { + "caps" : { + "output" : "." + }, + "capsOption" : { + "output" : "." + }, + "keyCode" : 65, + "option" : { + "output" : "." + }, + "shift" : { + "output" : "." + }, + "shiftCaps" : { + "output" : "." + }, + "shiftCapsOption" : { + "output" : "." + }, + "shiftOption" : { + "output" : "." + }, + "unmodified" : { + "output" : "." + } + }, + "66" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 66, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "*" + }, + "shiftCaps" : { + "output" : "*" + }, + "shiftCapsOption" : { + "output" : "*" + }, + "shiftOption" : { + "output" : "*" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "67" : { + "caps" : { + "output" : "*" + }, + "capsOption" : { + "output" : "*" + }, + "keyCode" : 67, + "option" : { + "output" : "*" + }, + "shift" : { + "output" : "*" + }, + "shiftCaps" : { + "output" : "*" + }, + "shiftCapsOption" : { + "output" : "*" + }, + "shiftOption" : { + "output" : "*" + }, + "unmodified" : { + "output" : "*" + } + }, + "68" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 68, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "69" : { + "caps" : { + "output" : "+" + }, + "capsOption" : { + "output" : "+" + }, + "keyCode" : 69, + "option" : { + "output" : "+" + }, + "shift" : { + "output" : "+" + }, + "shiftCaps" : { + "output" : "+" + }, + "shiftCapsOption" : { + "output" : "+" + }, + "shiftOption" : { + "output" : "+" + }, + "unmodified" : { + "output" : "+" + } + }, + "7" : { + "caps" : { + "output" : "X" + }, + "capsOption" : { + "output" : "Ù" + }, + "keyCode" : 7, + "option" : { + "output" : "≈" + }, + "shift" : { + "output" : "X" + }, + "shiftCaps" : { + "output" : "X" + }, + "shiftCapsOption" : { + "output" : "Ù" + }, + "shiftOption" : { + "output" : "Ù" + }, + "unmodified" : { + "output" : "x" + } + }, + "70" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 70, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "+" + }, + "shiftCaps" : { + "output" : "+" + }, + "shiftCapsOption" : { + "output" : "+" + }, + "shiftOption" : { + "output" : "+" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "71" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 71, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "72" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 72, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "=" + }, + "shiftCaps" : { + "output" : "=" + }, + "shiftCapsOption" : { + "output" : "=" + }, + "shiftOption" : { + "output" : "=" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "73" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 73, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "74" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 74, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "75" : { + "caps" : { + "output" : "\/" + }, + "capsOption" : { + "output" : "\/" + }, + "keyCode" : 75, + "option" : { + "output" : "\/" + }, + "shift" : { + "output" : "\/" + }, + "shiftCaps" : { + "output" : "\/" + }, + "shiftCapsOption" : { + "output" : "\/" + }, + "shiftOption" : { + "output" : "\/" + }, + "unmodified" : { + "output" : "\/" + } + }, + "76" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 76, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "77" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 77, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "\/" + }, + "shiftCaps" : { + "output" : "\/" + }, + "shiftCapsOption" : { + "output" : "\/" + }, + "shiftOption" : { + "output" : "\/" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "78" : { + "caps" : { + "output" : "-" + }, + "capsOption" : { + "output" : "-" + }, + "keyCode" : 78, + "option" : { + "output" : "-" + }, + "shift" : { + "output" : "-" + }, + "shiftCaps" : { + "output" : "-" + }, + "shiftCapsOption" : { + "output" : "-" + }, + "shiftOption" : { + "output" : "-" + }, + "unmodified" : { + "output" : "-" + } + }, + "79" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 79, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "8" : { + "caps" : { + "output" : "C" + }, + "capsOption" : { + "output" : "Ç" + }, + "keyCode" : 8, + "option" : { + "output" : "ç" + }, + "shift" : { + "output" : "C" + }, + "shiftCaps" : { + "output" : "C" + }, + "shiftCapsOption" : { + "output" : "Ç" + }, + "shiftOption" : { + "output" : "Ç" + }, + "unmodified" : { + "output" : "c" + } + }, + "80" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 80, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "81" : { + "caps" : { + "output" : "=" + }, + "capsOption" : { + "output" : "=" + }, + "keyCode" : 81, + "option" : { + "output" : "=" + }, + "shift" : { + "output" : "=" + }, + "shiftCaps" : { + "output" : "=" + }, + "shiftCapsOption" : { + "output" : "=" + }, + "shiftOption" : { + "output" : "=" + }, + "unmodified" : { + "output" : "=" + } + }, + "82" : { + "caps" : { + "output" : "0" + }, + "capsOption" : { + "output" : "0" + }, + "keyCode" : 82, + "option" : { + "output" : "0" + }, + "shift" : { + "output" : "0" + }, + "shiftCaps" : { + "output" : "0" + }, + "shiftCapsOption" : { + "output" : "0" + }, + "shiftOption" : { + "output" : "0" + }, + "unmodified" : { + "output" : "0" + } + }, + "83" : { + "caps" : { + "output" : "1" + }, + "capsOption" : { + "output" : "1" + }, + "keyCode" : 83, + "option" : { + "output" : "1" + }, + "shift" : { + "output" : "1" + }, + "shiftCaps" : { + "output" : "1" + }, + "shiftCapsOption" : { + "output" : "1" + }, + "shiftOption" : { + "output" : "1" + }, + "unmodified" : { + "output" : "1" + } + }, + "84" : { + "caps" : { + "output" : "2" + }, + "capsOption" : { + "output" : "2" + }, + "keyCode" : 84, + "option" : { + "output" : "2" + }, + "shift" : { + "output" : "2" + }, + "shiftCaps" : { + "output" : "2" + }, + "shiftCapsOption" : { + "output" : "2" + }, + "shiftOption" : { + "output" : "2" + }, + "unmodified" : { + "output" : "2" + } + }, + "85" : { + "caps" : { + "output" : "3" + }, + "capsOption" : { + "output" : "3" + }, + "keyCode" : 85, + "option" : { + "output" : "3" + }, + "shift" : { + "output" : "3" + }, + "shiftCaps" : { + "output" : "3" + }, + "shiftCapsOption" : { + "output" : "3" + }, + "shiftOption" : { + "output" : "3" + }, + "unmodified" : { + "output" : "3" + } + }, + "86" : { + "caps" : { + "output" : "4" + }, + "capsOption" : { + "output" : "4" + }, + "keyCode" : 86, + "option" : { + "output" : "4" + }, + "shift" : { + "output" : "4" + }, + "shiftCaps" : { + "output" : "4" + }, + "shiftCapsOption" : { + "output" : "4" + }, + "shiftOption" : { + "output" : "4" + }, + "unmodified" : { + "output" : "4" + } + }, + "87" : { + "caps" : { + "output" : "5" + }, + "capsOption" : { + "output" : "5" + }, + "keyCode" : 87, + "option" : { + "output" : "5" + }, + "shift" : { + "output" : "5" + }, + "shiftCaps" : { + "output" : "5" + }, + "shiftCapsOption" : { + "output" : "5" + }, + "shiftOption" : { + "output" : "5" + }, + "unmodified" : { + "output" : "5" + } + }, + "88" : { + "caps" : { + "output" : "6" + }, + "capsOption" : { + "output" : "6" + }, + "keyCode" : 88, + "option" : { + "output" : "6" + }, + "shift" : { + "output" : "6" + }, + "shiftCaps" : { + "output" : "6" + }, + "shiftCapsOption" : { + "output" : "6" + }, + "shiftOption" : { + "output" : "6" + }, + "unmodified" : { + "output" : "6" + } + }, + "89" : { + "caps" : { + "output" : "7" + }, + "capsOption" : { + "output" : "7" + }, + "keyCode" : 89, + "option" : { + "output" : "7" + }, + "shift" : { + "output" : "7" + }, + "shiftCaps" : { + "output" : "7" + }, + "shiftCapsOption" : { + "output" : "7" + }, + "shiftOption" : { + "output" : "7" + }, + "unmodified" : { + "output" : "7" + } + }, + "9" : { + "caps" : { + "output" : "V" + }, + "capsOption" : { + "output" : "◊" + }, + "keyCode" : 9, + "option" : { + "output" : "√" + }, + "shift" : { + "output" : "V" + }, + "shiftCaps" : { + "output" : "V" + }, + "shiftCapsOption" : { + "output" : "◊" + }, + "shiftOption" : { + "output" : "◊" + }, + "unmodified" : { + "output" : "v" + } + }, + "90" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 90, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "91" : { + "caps" : { + "output" : "8" + }, + "capsOption" : { + "output" : "8" + }, + "keyCode" : 91, + "option" : { + "output" : "8" + }, + "shift" : { + "output" : "8" + }, + "shiftCaps" : { + "output" : "8" + }, + "shiftCapsOption" : { + "output" : "8" + }, + "shiftOption" : { + "output" : "8" + }, + "unmodified" : { + "output" : "8" + } + }, + "92" : { + "caps" : { + "output" : "9" + }, + "capsOption" : { + "output" : "9" + }, + "keyCode" : 92, + "option" : { + "output" : "9" + }, + "shift" : { + "output" : "9" + }, + "shiftCaps" : { + "output" : "9" + }, + "shiftCapsOption" : { + "output" : "9" + }, + "shiftOption" : { + "output" : "9" + }, + "unmodified" : { + "output" : "9" + } + }, + "93" : { + "caps" : { + "output" : "§" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 93, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "output" : "±" + }, + "shiftCaps" : { + "output" : "±" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "output" : "§" + } + }, + "94" : { + "caps" : { + "output" : "`" + }, + "capsOption" : { + "output" : "`" + }, + "keyCode" : 94, + "option" : { + "rejection" : "deadKey" + }, + "shift" : { + "output" : "~" + }, + "shiftCaps" : { + "output" : "~" + }, + "shiftCapsOption" : { + "output" : "Ÿ" + }, + "shiftOption" : { + "output" : "Ÿ" + }, + "unmodified" : { + "output" : "`" + } + }, + "95" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 95, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "96" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 96, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "97" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 97, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "98" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 98, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "99" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 99, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + } + }, + "keyboardType" : 37, + "osBuild" : "24G830", + "osVersion" : "15.7.9", + "schemaVersion" : 1, + "sourceID" : "com.apple.keylayout.British" +} \ No newline at end of file diff --git a/Benchmarks/layout-tables/v1/com.apple.keylayout.British@91.json b/Benchmarks/layout-tables/v1/com.apple.keylayout.British@91.json new file mode 100644 index 0000000..2996f73 --- /dev/null +++ b/Benchmarks/layout-tables/v1/com.apple.keylayout.British@91.json @@ -0,0 +1,3465 @@ +{ + "keyOutputs" : { + "0" : { + "caps" : { + "output" : "A" + }, + "capsOption" : { + "output" : "Å" + }, + "keyCode" : 0, + "option" : { + "output" : "å" + }, + "shift" : { + "output" : "A" + }, + "shiftCaps" : { + "output" : "A" + }, + "shiftCapsOption" : { + "output" : "Å" + }, + "shiftOption" : { + "output" : "Å" + }, + "unmodified" : { + "output" : "a" + } + }, + "1" : { + "caps" : { + "output" : "S" + }, + "capsOption" : { + "output" : "Í" + }, + "keyCode" : 1, + "option" : { + "output" : "ß" + }, + "shift" : { + "output" : "S" + }, + "shiftCaps" : { + "output" : "S" + }, + "shiftCapsOption" : { + "output" : "Í" + }, + "shiftOption" : { + "output" : "Í" + }, + "unmodified" : { + "output" : "s" + } + }, + "10" : { + "caps" : { + "output" : "§" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 10, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "output" : "±" + }, + "shiftCaps" : { + "output" : "±" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "output" : "§" + } + }, + "100" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 100, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "101" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 101, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "102" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 102, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "103" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 103, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "104" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 104, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "105" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 105, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "106" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 106, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "107" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 107, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "108" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 108, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "109" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 109, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "11" : { + "caps" : { + "output" : "B" + }, + "capsOption" : { + "output" : "ı" + }, + "keyCode" : 11, + "option" : { + "output" : "∫" + }, + "shift" : { + "output" : "B" + }, + "shiftCaps" : { + "output" : "B" + }, + "shiftCapsOption" : { + "output" : "ı" + }, + "shiftOption" : { + "output" : "ı" + }, + "unmodified" : { + "output" : "b" + } + }, + "110" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 110, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "111" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 111, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "112" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 112, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "113" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 113, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "114" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 114, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "115" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 115, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "116" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 116, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "117" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 117, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "118" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 118, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "119" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 119, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "12" : { + "caps" : { + "output" : "Q" + }, + "capsOption" : { + "output" : "Œ" + }, + "keyCode" : 12, + "option" : { + "output" : "œ" + }, + "shift" : { + "output" : "Q" + }, + "shiftCaps" : { + "output" : "Q" + }, + "shiftCapsOption" : { + "output" : "Œ" + }, + "shiftOption" : { + "output" : "Œ" + }, + "unmodified" : { + "output" : "q" + } + }, + "120" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 120, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "121" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 121, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "122" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 122, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "123" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 123, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "124" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 124, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "125" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 125, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "126" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 126, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "127" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 127, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "13" : { + "caps" : { + "output" : "W" + }, + "capsOption" : { + "output" : "„" + }, + "keyCode" : 13, + "option" : { + "output" : "∑" + }, + "shift" : { + "output" : "W" + }, + "shiftCaps" : { + "output" : "W" + }, + "shiftCapsOption" : { + "output" : "„" + }, + "shiftOption" : { + "output" : "„" + }, + "unmodified" : { + "output" : "w" + } + }, + "14" : { + "caps" : { + "output" : "E" + }, + "capsOption" : { + "output" : "‰" + }, + "keyCode" : 14, + "option" : { + "rejection" : "deadKey" + }, + "shift" : { + "output" : "E" + }, + "shiftCaps" : { + "output" : "E" + }, + "shiftCapsOption" : { + "output" : "‰" + }, + "shiftOption" : { + "output" : "‰" + }, + "unmodified" : { + "output" : "e" + } + }, + "15" : { + "caps" : { + "output" : "R" + }, + "capsOption" : { + "output" : "Â" + }, + "keyCode" : 15, + "option" : { + "output" : "®" + }, + "shift" : { + "output" : "R" + }, + "shiftCaps" : { + "output" : "R" + }, + "shiftCapsOption" : { + "output" : "Â" + }, + "shiftOption" : { + "output" : "Â" + }, + "unmodified" : { + "output" : "r" + } + }, + "16" : { + "caps" : { + "output" : "Y" + }, + "capsOption" : { + "output" : "Á" + }, + "keyCode" : 16, + "option" : { + "output" : "¥" + }, + "shift" : { + "output" : "Y" + }, + "shiftCaps" : { + "output" : "Y" + }, + "shiftCapsOption" : { + "output" : "Á" + }, + "shiftOption" : { + "output" : "Á" + }, + "unmodified" : { + "output" : "y" + } + }, + "17" : { + "caps" : { + "output" : "T" + }, + "capsOption" : { + "output" : "Ê" + }, + "keyCode" : 17, + "option" : { + "output" : "†" + }, + "shift" : { + "output" : "T" + }, + "shiftCaps" : { + "output" : "T" + }, + "shiftCapsOption" : { + "output" : "Ê" + }, + "shiftOption" : { + "output" : "Ê" + }, + "unmodified" : { + "output" : "t" + } + }, + "18" : { + "caps" : { + "output" : "1" + }, + "capsOption" : { + "output" : "¡" + }, + "keyCode" : 18, + "option" : { + "output" : "¡" + }, + "shift" : { + "output" : "!" + }, + "shiftCaps" : { + "output" : "!" + }, + "shiftCapsOption" : { + "output" : "⁄" + }, + "shiftOption" : { + "output" : "⁄" + }, + "unmodified" : { + "output" : "1" + } + }, + "19" : { + "caps" : { + "output" : "2" + }, + "capsOption" : { + "output" : "™" + }, + "keyCode" : 19, + "option" : { + "output" : "€" + }, + "shift" : { + "output" : "@" + }, + "shiftCaps" : { + "output" : "@" + }, + "shiftCapsOption" : { + "output" : "™" + }, + "shiftOption" : { + "output" : "™" + }, + "unmodified" : { + "output" : "2" + } + }, + "2" : { + "caps" : { + "output" : "D" + }, + "capsOption" : { + "output" : "Î" + }, + "keyCode" : 2, + "option" : { + "output" : "∂" + }, + "shift" : { + "output" : "D" + }, + "shiftCaps" : { + "output" : "D" + }, + "shiftCapsOption" : { + "output" : "Î" + }, + "shiftOption" : { + "output" : "Î" + }, + "unmodified" : { + "output" : "d" + } + }, + "20" : { + "caps" : { + "output" : "3" + }, + "capsOption" : { + "output" : "#" + }, + "keyCode" : 20, + "option" : { + "output" : "#" + }, + "shift" : { + "output" : "£" + }, + "shiftCaps" : { + "output" : "£" + }, + "shiftCapsOption" : { + "output" : "‹" + }, + "shiftOption" : { + "output" : "‹" + }, + "unmodified" : { + "output" : "3" + } + }, + "21" : { + "caps" : { + "output" : "4" + }, + "capsOption" : { + "output" : "¢" + }, + "keyCode" : 21, + "option" : { + "output" : "¢" + }, + "shift" : { + "output" : "$" + }, + "shiftCaps" : { + "output" : "$" + }, + "shiftCapsOption" : { + "output" : "›" + }, + "shiftOption" : { + "output" : "›" + }, + "unmodified" : { + "output" : "4" + } + }, + "22" : { + "caps" : { + "output" : "6" + }, + "capsOption" : { + "output" : "§" + }, + "keyCode" : 22, + "option" : { + "output" : "§" + }, + "shift" : { + "output" : "^" + }, + "shiftCaps" : { + "output" : "^" + }, + "shiftCapsOption" : { + "output" : "fl" + }, + "shiftOption" : { + "output" : "fl" + }, + "unmodified" : { + "output" : "6" + } + }, + "23" : { + "caps" : { + "output" : "5" + }, + "capsOption" : { + "output" : "∞" + }, + "keyCode" : 23, + "option" : { + "output" : "∞" + }, + "shift" : { + "output" : "%" + }, + "shiftCaps" : { + "output" : "%" + }, + "shiftCapsOption" : { + "output" : "fi" + }, + "shiftOption" : { + "output" : "fi" + }, + "unmodified" : { + "output" : "5" + } + }, + "24" : { + "caps" : { + "output" : "=" + }, + "capsOption" : { + "output" : "≠" + }, + "keyCode" : 24, + "option" : { + "output" : "≠" + }, + "shift" : { + "output" : "+" + }, + "shiftCaps" : { + "output" : "+" + }, + "shiftCapsOption" : { + "output" : "±" + }, + "shiftOption" : { + "output" : "±" + }, + "unmodified" : { + "output" : "=" + } + }, + "25" : { + "caps" : { + "output" : "9" + }, + "capsOption" : { + "output" : "ª" + }, + "keyCode" : 25, + "option" : { + "output" : "ª" + }, + "shift" : { + "output" : "(" + }, + "shiftCaps" : { + "output" : "(" + }, + "shiftCapsOption" : { + "output" : "·" + }, + "shiftOption" : { + "output" : "·" + }, + "unmodified" : { + "output" : "9" + } + }, + "26" : { + "caps" : { + "output" : "7" + }, + "capsOption" : { + "output" : "¶" + }, + "keyCode" : 26, + "option" : { + "output" : "¶" + }, + "shift" : { + "output" : "&" + }, + "shiftCaps" : { + "output" : "&" + }, + "shiftCapsOption" : { + "output" : "‡" + }, + "shiftOption" : { + "output" : "‡" + }, + "unmodified" : { + "output" : "7" + } + }, + "27" : { + "caps" : { + "output" : "-" + }, + "capsOption" : { + "output" : "–" + }, + "keyCode" : 27, + "option" : { + "output" : "–" + }, + "shift" : { + "output" : "_" + }, + "shiftCaps" : { + "output" : "_" + }, + "shiftCapsOption" : { + "output" : "—" + }, + "shiftOption" : { + "output" : "—" + }, + "unmodified" : { + "output" : "-" + } + }, + "28" : { + "caps" : { + "output" : "8" + }, + "capsOption" : { + "output" : "•" + }, + "keyCode" : 28, + "option" : { + "output" : "•" + }, + "shift" : { + "output" : "*" + }, + "shiftCaps" : { + "output" : "*" + }, + "shiftCapsOption" : { + "output" : "°" + }, + "shiftOption" : { + "output" : "°" + }, + "unmodified" : { + "output" : "8" + } + }, + "29" : { + "caps" : { + "output" : "0" + }, + "capsOption" : { + "output" : "º" + }, + "keyCode" : 29, + "option" : { + "output" : "º" + }, + "shift" : { + "output" : ")" + }, + "shiftCaps" : { + "output" : ")" + }, + "shiftCapsOption" : { + "output" : "‚" + }, + "shiftOption" : { + "output" : "‚" + }, + "unmodified" : { + "output" : "0" + } + }, + "3" : { + "caps" : { + "output" : "F" + }, + "capsOption" : { + "output" : "Ï" + }, + "keyCode" : 3, + "option" : { + "output" : "ƒ" + }, + "shift" : { + "output" : "F" + }, + "shiftCaps" : { + "output" : "F" + }, + "shiftCapsOption" : { + "output" : "Ï" + }, + "shiftOption" : { + "output" : "Ï" + }, + "unmodified" : { + "output" : "f" + } + }, + "30" : { + "caps" : { + "output" : "]" + }, + "capsOption" : { + "output" : "‘" + }, + "keyCode" : 30, + "option" : { + "output" : "‘" + }, + "shift" : { + "output" : "}" + }, + "shiftCaps" : { + "output" : "}" + }, + "shiftCapsOption" : { + "output" : "’" + }, + "shiftOption" : { + "output" : "’" + }, + "unmodified" : { + "output" : "]" + } + }, + "31" : { + "caps" : { + "output" : "O" + }, + "capsOption" : { + "output" : "Ø" + }, + "keyCode" : 31, + "option" : { + "output" : "ø" + }, + "shift" : { + "output" : "O" + }, + "shiftCaps" : { + "output" : "O" + }, + "shiftCapsOption" : { + "output" : "Ø" + }, + "shiftOption" : { + "output" : "Ø" + }, + "unmodified" : { + "output" : "o" + } + }, + "32" : { + "caps" : { + "output" : "U" + }, + "capsOption" : { + "output" : "Ë" + }, + "keyCode" : 32, + "option" : { + "rejection" : "deadKey" + }, + "shift" : { + "output" : "U" + }, + "shiftCaps" : { + "output" : "U" + }, + "shiftCapsOption" : { + "output" : "Ë" + }, + "shiftOption" : { + "output" : "Ë" + }, + "unmodified" : { + "output" : "u" + } + }, + "33" : { + "caps" : { + "output" : "[" + }, + "capsOption" : { + "output" : "“" + }, + "keyCode" : 33, + "option" : { + "output" : "“" + }, + "shift" : { + "output" : "{" + }, + "shiftCaps" : { + "output" : "{" + }, + "shiftCapsOption" : { + "output" : "”" + }, + "shiftOption" : { + "output" : "”" + }, + "unmodified" : { + "output" : "[" + } + }, + "34" : { + "caps" : { + "output" : "I" + }, + "capsOption" : { + "output" : "È" + }, + "keyCode" : 34, + "option" : { + "rejection" : "deadKey" + }, + "shift" : { + "output" : "I" + }, + "shiftCaps" : { + "output" : "I" + }, + "shiftCapsOption" : { + "output" : "È" + }, + "shiftOption" : { + "output" : "È" + }, + "unmodified" : { + "output" : "i" + } + }, + "35" : { + "caps" : { + "output" : "P" + }, + "capsOption" : { + "output" : "∏" + }, + "keyCode" : 35, + "option" : { + "output" : "π" + }, + "shift" : { + "output" : "P" + }, + "shiftCaps" : { + "output" : "P" + }, + "shiftCapsOption" : { + "output" : "∏" + }, + "shiftOption" : { + "output" : "∏" + }, + "unmodified" : { + "output" : "p" + } + }, + "36" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 36, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "37" : { + "caps" : { + "output" : "L" + }, + "capsOption" : { + "output" : "Ò" + }, + "keyCode" : 37, + "option" : { + "output" : "¬" + }, + "shift" : { + "output" : "L" + }, + "shiftCaps" : { + "output" : "L" + }, + "shiftCapsOption" : { + "output" : "Ò" + }, + "shiftOption" : { + "output" : "Ò" + }, + "unmodified" : { + "output" : "l" + } + }, + "38" : { + "caps" : { + "output" : "J" + }, + "capsOption" : { + "output" : "Ô" + }, + "keyCode" : 38, + "option" : { + "output" : "∆" + }, + "shift" : { + "output" : "J" + }, + "shiftCaps" : { + "output" : "J" + }, + "shiftCapsOption" : { + "output" : "Ô" + }, + "shiftOption" : { + "output" : "Ô" + }, + "unmodified" : { + "output" : "j" + } + }, + "39" : { + "caps" : { + "output" : "'" + }, + "capsOption" : { + "output" : "Æ" + }, + "keyCode" : 39, + "option" : { + "output" : "æ" + }, + "shift" : { + "output" : "\"" + }, + "shiftCaps" : { + "output" : "\"" + }, + "shiftCapsOption" : { + "output" : "Æ" + }, + "shiftOption" : { + "output" : "Æ" + }, + "unmodified" : { + "output" : "'" + } + }, + "4" : { + "caps" : { + "output" : "H" + }, + "capsOption" : { + "output" : "Ó" + }, + "keyCode" : 4, + "option" : { + "output" : "˙" + }, + "shift" : { + "output" : "H" + }, + "shiftCaps" : { + "output" : "H" + }, + "shiftCapsOption" : { + "output" : "Ó" + }, + "shiftOption" : { + "output" : "Ó" + }, + "unmodified" : { + "output" : "h" + } + }, + "40" : { + "caps" : { + "output" : "K" + }, + "capsOption" : { + "rejection" : "privateUseOrSurrogate" + }, + "keyCode" : 40, + "option" : { + "output" : "˚" + }, + "shift" : { + "output" : "K" + }, + "shiftCaps" : { + "output" : "K" + }, + "shiftCapsOption" : { + "rejection" : "privateUseOrSurrogate" + }, + "shiftOption" : { + "rejection" : "privateUseOrSurrogate" + }, + "unmodified" : { + "output" : "k" + } + }, + "41" : { + "caps" : { + "output" : ";" + }, + "capsOption" : { + "output" : "…" + }, + "keyCode" : 41, + "option" : { + "output" : "…" + }, + "shift" : { + "output" : ":" + }, + "shiftCaps" : { + "output" : ":" + }, + "shiftCapsOption" : { + "output" : "Ú" + }, + "shiftOption" : { + "output" : "Ú" + }, + "unmodified" : { + "output" : ";" + } + }, + "42" : { + "caps" : { + "output" : "\\" + }, + "capsOption" : { + "output" : "«" + }, + "keyCode" : 42, + "option" : { + "output" : "«" + }, + "shift" : { + "output" : "|" + }, + "shiftCaps" : { + "output" : "|" + }, + "shiftCapsOption" : { + "output" : "»" + }, + "shiftOption" : { + "output" : "»" + }, + "unmodified" : { + "output" : "\\" + } + }, + "43" : { + "caps" : { + "output" : "," + }, + "capsOption" : { + "output" : "≤" + }, + "keyCode" : 43, + "option" : { + "output" : "≤" + }, + "shift" : { + "output" : "<" + }, + "shiftCaps" : { + "output" : "<" + }, + "shiftCapsOption" : { + "output" : "¯" + }, + "shiftOption" : { + "output" : "¯" + }, + "unmodified" : { + "output" : "," + } + }, + "44" : { + "caps" : { + "output" : "\/" + }, + "capsOption" : { + "output" : "÷" + }, + "keyCode" : 44, + "option" : { + "output" : "÷" + }, + "shift" : { + "output" : "?" + }, + "shiftCaps" : { + "output" : "?" + }, + "shiftCapsOption" : { + "output" : "¿" + }, + "shiftOption" : { + "output" : "¿" + }, + "unmodified" : { + "output" : "\/" + } + }, + "45" : { + "caps" : { + "output" : "N" + }, + "capsOption" : { + "output" : "ˆ" + }, + "keyCode" : 45, + "option" : { + "rejection" : "deadKey" + }, + "shift" : { + "output" : "N" + }, + "shiftCaps" : { + "output" : "N" + }, + "shiftCapsOption" : { + "output" : "ˆ" + }, + "shiftOption" : { + "output" : "ˆ" + }, + "unmodified" : { + "output" : "n" + } + }, + "46" : { + "caps" : { + "output" : "M" + }, + "capsOption" : { + "output" : "˜" + }, + "keyCode" : 46, + "option" : { + "output" : "µ" + }, + "shift" : { + "output" : "M" + }, + "shiftCaps" : { + "output" : "M" + }, + "shiftCapsOption" : { + "output" : "˜" + }, + "shiftOption" : { + "output" : "˜" + }, + "unmodified" : { + "output" : "m" + } + }, + "47" : { + "caps" : { + "output" : "." + }, + "capsOption" : { + "output" : "≥" + }, + "keyCode" : 47, + "option" : { + "output" : "≥" + }, + "shift" : { + "output" : ">" + }, + "shiftCaps" : { + "output" : ">" + }, + "shiftCapsOption" : { + "output" : "˘" + }, + "shiftOption" : { + "output" : "˘" + }, + "unmodified" : { + "output" : "." + } + }, + "48" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 48, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "49" : { + "caps" : { + "output" : " " + }, + "capsOption" : { + "output" : " " + }, + "keyCode" : 49, + "option" : { + "output" : " " + }, + "shift" : { + "output" : " " + }, + "shiftCaps" : { + "output" : " " + }, + "shiftCapsOption" : { + "output" : " " + }, + "shiftOption" : { + "output" : " " + }, + "unmodified" : { + "output" : " " + } + }, + "5" : { + "caps" : { + "output" : "G" + }, + "capsOption" : { + "output" : "Ì" + }, + "keyCode" : 5, + "option" : { + "output" : "©" + }, + "shift" : { + "output" : "G" + }, + "shiftCaps" : { + "output" : "G" + }, + "shiftCapsOption" : { + "output" : "Ì" + }, + "shiftOption" : { + "output" : "Ì" + }, + "unmodified" : { + "output" : "g" + } + }, + "50" : { + "caps" : { + "output" : "`" + }, + "capsOption" : { + "output" : "`" + }, + "keyCode" : 50, + "option" : { + "rejection" : "deadKey" + }, + "shift" : { + "output" : "~" + }, + "shiftCaps" : { + "output" : "~" + }, + "shiftCapsOption" : { + "output" : "Ÿ" + }, + "shiftOption" : { + "output" : "Ÿ" + }, + "unmodified" : { + "output" : "`" + } + }, + "51" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 51, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "52" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 52, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "53" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 53, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "54" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 54, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "55" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 55, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "56" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 56, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "57" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 57, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "58" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 58, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "59" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 59, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "6" : { + "caps" : { + "output" : "Z" + }, + "capsOption" : { + "output" : "Û" + }, + "keyCode" : 6, + "option" : { + "output" : "Ω" + }, + "shift" : { + "output" : "Z" + }, + "shiftCaps" : { + "output" : "Z" + }, + "shiftCapsOption" : { + "output" : "Û" + }, + "shiftOption" : { + "output" : "Û" + }, + "unmodified" : { + "output" : "z" + } + }, + "60" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 60, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "61" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 61, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "62" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 62, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "63" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 63, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "64" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 64, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "65" : { + "caps" : { + "output" : "." + }, + "capsOption" : { + "output" : "." + }, + "keyCode" : 65, + "option" : { + "output" : "." + }, + "shift" : { + "output" : "." + }, + "shiftCaps" : { + "output" : "." + }, + "shiftCapsOption" : { + "output" : "." + }, + "shiftOption" : { + "output" : "." + }, + "unmodified" : { + "output" : "." + } + }, + "66" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 66, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "*" + }, + "shiftCaps" : { + "output" : "*" + }, + "shiftCapsOption" : { + "output" : "*" + }, + "shiftOption" : { + "output" : "*" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "67" : { + "caps" : { + "output" : "*" + }, + "capsOption" : { + "output" : "*" + }, + "keyCode" : 67, + "option" : { + "output" : "*" + }, + "shift" : { + "output" : "*" + }, + "shiftCaps" : { + "output" : "*" + }, + "shiftCapsOption" : { + "output" : "*" + }, + "shiftOption" : { + "output" : "*" + }, + "unmodified" : { + "output" : "*" + } + }, + "68" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 68, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "69" : { + "caps" : { + "output" : "+" + }, + "capsOption" : { + "output" : "+" + }, + "keyCode" : 69, + "option" : { + "output" : "+" + }, + "shift" : { + "output" : "+" + }, + "shiftCaps" : { + "output" : "+" + }, + "shiftCapsOption" : { + "output" : "+" + }, + "shiftOption" : { + "output" : "+" + }, + "unmodified" : { + "output" : "+" + } + }, + "7" : { + "caps" : { + "output" : "X" + }, + "capsOption" : { + "output" : "Ù" + }, + "keyCode" : 7, + "option" : { + "output" : "≈" + }, + "shift" : { + "output" : "X" + }, + "shiftCaps" : { + "output" : "X" + }, + "shiftCapsOption" : { + "output" : "Ù" + }, + "shiftOption" : { + "output" : "Ù" + }, + "unmodified" : { + "output" : "x" + } + }, + "70" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 70, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "+" + }, + "shiftCaps" : { + "output" : "+" + }, + "shiftCapsOption" : { + "output" : "+" + }, + "shiftOption" : { + "output" : "+" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "71" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 71, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "72" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 72, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "=" + }, + "shiftCaps" : { + "output" : "=" + }, + "shiftCapsOption" : { + "output" : "=" + }, + "shiftOption" : { + "output" : "=" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "73" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 73, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "74" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 74, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "75" : { + "caps" : { + "output" : "\/" + }, + "capsOption" : { + "output" : "\/" + }, + "keyCode" : 75, + "option" : { + "output" : "\/" + }, + "shift" : { + "output" : "\/" + }, + "shiftCaps" : { + "output" : "\/" + }, + "shiftCapsOption" : { + "output" : "\/" + }, + "shiftOption" : { + "output" : "\/" + }, + "unmodified" : { + "output" : "\/" + } + }, + "76" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 76, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "77" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 77, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "\/" + }, + "shiftCaps" : { + "output" : "\/" + }, + "shiftCapsOption" : { + "output" : "\/" + }, + "shiftOption" : { + "output" : "\/" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "78" : { + "caps" : { + "output" : "-" + }, + "capsOption" : { + "output" : "-" + }, + "keyCode" : 78, + "option" : { + "output" : "-" + }, + "shift" : { + "output" : "-" + }, + "shiftCaps" : { + "output" : "-" + }, + "shiftCapsOption" : { + "output" : "-" + }, + "shiftOption" : { + "output" : "-" + }, + "unmodified" : { + "output" : "-" + } + }, + "79" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 79, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "8" : { + "caps" : { + "output" : "C" + }, + "capsOption" : { + "output" : "Ç" + }, + "keyCode" : 8, + "option" : { + "output" : "ç" + }, + "shift" : { + "output" : "C" + }, + "shiftCaps" : { + "output" : "C" + }, + "shiftCapsOption" : { + "output" : "Ç" + }, + "shiftOption" : { + "output" : "Ç" + }, + "unmodified" : { + "output" : "c" + } + }, + "80" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 80, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "81" : { + "caps" : { + "output" : "=" + }, + "capsOption" : { + "output" : "=" + }, + "keyCode" : 81, + "option" : { + "output" : "=" + }, + "shift" : { + "output" : "=" + }, + "shiftCaps" : { + "output" : "=" + }, + "shiftCapsOption" : { + "output" : "=" + }, + "shiftOption" : { + "output" : "=" + }, + "unmodified" : { + "output" : "=" + } + }, + "82" : { + "caps" : { + "output" : "0" + }, + "capsOption" : { + "output" : "0" + }, + "keyCode" : 82, + "option" : { + "output" : "0" + }, + "shift" : { + "output" : "0" + }, + "shiftCaps" : { + "output" : "0" + }, + "shiftCapsOption" : { + "output" : "0" + }, + "shiftOption" : { + "output" : "0" + }, + "unmodified" : { + "output" : "0" + } + }, + "83" : { + "caps" : { + "output" : "1" + }, + "capsOption" : { + "output" : "1" + }, + "keyCode" : 83, + "option" : { + "output" : "1" + }, + "shift" : { + "output" : "1" + }, + "shiftCaps" : { + "output" : "1" + }, + "shiftCapsOption" : { + "output" : "1" + }, + "shiftOption" : { + "output" : "1" + }, + "unmodified" : { + "output" : "1" + } + }, + "84" : { + "caps" : { + "output" : "2" + }, + "capsOption" : { + "output" : "2" + }, + "keyCode" : 84, + "option" : { + "output" : "2" + }, + "shift" : { + "output" : "2" + }, + "shiftCaps" : { + "output" : "2" + }, + "shiftCapsOption" : { + "output" : "2" + }, + "shiftOption" : { + "output" : "2" + }, + "unmodified" : { + "output" : "2" + } + }, + "85" : { + "caps" : { + "output" : "3" + }, + "capsOption" : { + "output" : "3" + }, + "keyCode" : 85, + "option" : { + "output" : "3" + }, + "shift" : { + "output" : "3" + }, + "shiftCaps" : { + "output" : "3" + }, + "shiftCapsOption" : { + "output" : "3" + }, + "shiftOption" : { + "output" : "3" + }, + "unmodified" : { + "output" : "3" + } + }, + "86" : { + "caps" : { + "output" : "4" + }, + "capsOption" : { + "output" : "4" + }, + "keyCode" : 86, + "option" : { + "output" : "4" + }, + "shift" : { + "output" : "4" + }, + "shiftCaps" : { + "output" : "4" + }, + "shiftCapsOption" : { + "output" : "4" + }, + "shiftOption" : { + "output" : "4" + }, + "unmodified" : { + "output" : "4" + } + }, + "87" : { + "caps" : { + "output" : "5" + }, + "capsOption" : { + "output" : "5" + }, + "keyCode" : 87, + "option" : { + "output" : "5" + }, + "shift" : { + "output" : "5" + }, + "shiftCaps" : { + "output" : "5" + }, + "shiftCapsOption" : { + "output" : "5" + }, + "shiftOption" : { + "output" : "5" + }, + "unmodified" : { + "output" : "5" + } + }, + "88" : { + "caps" : { + "output" : "6" + }, + "capsOption" : { + "output" : "6" + }, + "keyCode" : 88, + "option" : { + "output" : "6" + }, + "shift" : { + "output" : "6" + }, + "shiftCaps" : { + "output" : "6" + }, + "shiftCapsOption" : { + "output" : "6" + }, + "shiftOption" : { + "output" : "6" + }, + "unmodified" : { + "output" : "6" + } + }, + "89" : { + "caps" : { + "output" : "7" + }, + "capsOption" : { + "output" : "7" + }, + "keyCode" : 89, + "option" : { + "output" : "7" + }, + "shift" : { + "output" : "7" + }, + "shiftCaps" : { + "output" : "7" + }, + "shiftCapsOption" : { + "output" : "7" + }, + "shiftOption" : { + "output" : "7" + }, + "unmodified" : { + "output" : "7" + } + }, + "9" : { + "caps" : { + "output" : "V" + }, + "capsOption" : { + "output" : "◊" + }, + "keyCode" : 9, + "option" : { + "output" : "√" + }, + "shift" : { + "output" : "V" + }, + "shiftCaps" : { + "output" : "V" + }, + "shiftCapsOption" : { + "output" : "◊" + }, + "shiftOption" : { + "output" : "◊" + }, + "unmodified" : { + "output" : "v" + } + }, + "90" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 90, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "91" : { + "caps" : { + "output" : "8" + }, + "capsOption" : { + "output" : "8" + }, + "keyCode" : 91, + "option" : { + "output" : "8" + }, + "shift" : { + "output" : "8" + }, + "shiftCaps" : { + "output" : "8" + }, + "shiftCapsOption" : { + "output" : "8" + }, + "shiftOption" : { + "output" : "8" + }, + "unmodified" : { + "output" : "8" + } + }, + "92" : { + "caps" : { + "output" : "9" + }, + "capsOption" : { + "output" : "9" + }, + "keyCode" : 92, + "option" : { + "output" : "9" + }, + "shift" : { + "output" : "9" + }, + "shiftCaps" : { + "output" : "9" + }, + "shiftCapsOption" : { + "output" : "9" + }, + "shiftOption" : { + "output" : "9" + }, + "unmodified" : { + "output" : "9" + } + }, + "93" : { + "caps" : { + "output" : "§" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 93, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "output" : "±" + }, + "shiftCaps" : { + "output" : "±" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "output" : "§" + } + }, + "94" : { + "caps" : { + "output" : "`" + }, + "capsOption" : { + "output" : "`" + }, + "keyCode" : 94, + "option" : { + "rejection" : "deadKey" + }, + "shift" : { + "output" : "~" + }, + "shiftCaps" : { + "output" : "~" + }, + "shiftCapsOption" : { + "output" : "Ÿ" + }, + "shiftOption" : { + "output" : "Ÿ" + }, + "unmodified" : { + "output" : "`" + } + }, + "95" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 95, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "96" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 96, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "97" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 97, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "98" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 98, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "99" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 99, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + } + }, + "keyboardType" : 91, + "osBuild" : "24G830", + "osVersion" : "15.7.9", + "schemaVersion" : 1, + "sourceID" : "com.apple.keylayout.British" +} \ No newline at end of file diff --git a/Benchmarks/layout-tables/v1/com.apple.keylayout.Colemak@37.json b/Benchmarks/layout-tables/v1/com.apple.keylayout.Colemak@37.json new file mode 100644 index 0000000..0913ab0 --- /dev/null +++ b/Benchmarks/layout-tables/v1/com.apple.keylayout.Colemak@37.json @@ -0,0 +1,3465 @@ +{ + "keyOutputs" : { + "0" : { + "caps" : { + "output" : "A" + }, + "capsOption" : { + "output" : "Å" + }, + "keyCode" : 0, + "option" : { + "output" : "å" + }, + "shift" : { + "output" : "A" + }, + "shiftCaps" : { + "output" : "A" + }, + "shiftCapsOption" : { + "output" : "Å" + }, + "shiftOption" : { + "output" : "Å" + }, + "unmodified" : { + "output" : "a" + } + }, + "1" : { + "caps" : { + "output" : "R" + }, + "capsOption" : { + "output" : "®" + }, + "keyCode" : 1, + "option" : { + "output" : "®" + }, + "shift" : { + "output" : "R" + }, + "shiftCaps" : { + "output" : "R" + }, + "shiftCapsOption" : { + "output" : "‰" + }, + "shiftOption" : { + "output" : "‰" + }, + "unmodified" : { + "output" : "r" + } + }, + "10" : { + "caps" : { + "output" : "§" + }, + "capsOption" : { + "output" : "§" + }, + "keyCode" : 10, + "option" : { + "output" : "§" + }, + "shift" : { + "output" : "±" + }, + "shiftCaps" : { + "output" : "±" + }, + "shiftCapsOption" : { + "output" : "±" + }, + "shiftOption" : { + "output" : "±" + }, + "unmodified" : { + "output" : "§" + } + }, + "100" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 100, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "101" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 101, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "102" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 102, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "103" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 103, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "104" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 104, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "105" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 105, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "106" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 106, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "107" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 107, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "108" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 108, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "109" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 109, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "11" : { + "caps" : { + "output" : "B" + }, + "capsOption" : { + "output" : "ı" + }, + "keyCode" : 11, + "option" : { + "output" : "∫" + }, + "shift" : { + "output" : "B" + }, + "shiftCaps" : { + "output" : "B" + }, + "shiftCapsOption" : { + "output" : "ı" + }, + "shiftOption" : { + "output" : "ı" + }, + "unmodified" : { + "output" : "b" + } + }, + "110" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 110, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "111" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 111, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "112" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 112, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "113" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 113, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "114" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 114, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "115" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 115, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "116" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 116, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "117" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 117, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "118" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 118, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "119" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 119, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "12" : { + "caps" : { + "output" : "Q" + }, + "capsOption" : { + "output" : "Œ" + }, + "keyCode" : 12, + "option" : { + "output" : "œ" + }, + "shift" : { + "output" : "Q" + }, + "shiftCaps" : { + "output" : "Q" + }, + "shiftCapsOption" : { + "output" : "Œ" + }, + "shiftOption" : { + "output" : "Œ" + }, + "unmodified" : { + "output" : "q" + } + }, + "120" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 120, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "121" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 121, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "122" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 122, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "123" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 123, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "124" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 124, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "125" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 125, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "126" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 126, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "127" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 127, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "13" : { + "caps" : { + "output" : "W" + }, + "capsOption" : { + "output" : "∑" + }, + "keyCode" : 13, + "option" : { + "output" : "∑" + }, + "shift" : { + "output" : "W" + }, + "shiftCaps" : { + "output" : "W" + }, + "shiftCapsOption" : { + "output" : "„" + }, + "shiftOption" : { + "output" : "„" + }, + "unmodified" : { + "output" : "w" + } + }, + "14" : { + "caps" : { + "output" : "F" + }, + "capsOption" : { + "output" : "Ï" + }, + "keyCode" : 14, + "option" : { + "output" : "ƒ" + }, + "shift" : { + "output" : "F" + }, + "shiftCaps" : { + "output" : "F" + }, + "shiftCapsOption" : { + "output" : "Ï" + }, + "shiftOption" : { + "output" : "Ï" + }, + "unmodified" : { + "output" : "f" + } + }, + "15" : { + "caps" : { + "output" : "P" + }, + "capsOption" : { + "output" : "∏" + }, + "keyCode" : 15, + "option" : { + "output" : "π" + }, + "shift" : { + "output" : "P" + }, + "shiftCaps" : { + "output" : "P" + }, + "shiftCapsOption" : { + "output" : "∏" + }, + "shiftOption" : { + "output" : "∏" + }, + "unmodified" : { + "output" : "p" + } + }, + "16" : { + "caps" : { + "output" : "J" + }, + "capsOption" : { + "output" : "Ô" + }, + "keyCode" : 16, + "option" : { + "output" : "∆" + }, + "shift" : { + "output" : "J" + }, + "shiftCaps" : { + "output" : "J" + }, + "shiftCapsOption" : { + "output" : "Ô" + }, + "shiftOption" : { + "output" : "Ô" + }, + "unmodified" : { + "output" : "j" + } + }, + "17" : { + "caps" : { + "output" : "G" + }, + "capsOption" : { + "output" : "©" + }, + "keyCode" : 17, + "option" : { + "output" : "©" + }, + "shift" : { + "output" : "G" + }, + "shiftCaps" : { + "output" : "G" + }, + "shiftCapsOption" : { + "output" : "˝" + }, + "shiftOption" : { + "output" : "˝" + }, + "unmodified" : { + "output" : "g" + } + }, + "18" : { + "caps" : { + "output" : "1" + }, + "capsOption" : { + "output" : "¡" + }, + "keyCode" : 18, + "option" : { + "output" : "¡" + }, + "shift" : { + "output" : "!" + }, + "shiftCaps" : { + "output" : "!" + }, + "shiftCapsOption" : { + "output" : "⁄" + }, + "shiftOption" : { + "output" : "⁄" + }, + "unmodified" : { + "output" : "1" + } + }, + "19" : { + "caps" : { + "output" : "2" + }, + "capsOption" : { + "output" : "™" + }, + "keyCode" : 19, + "option" : { + "output" : "™" + }, + "shift" : { + "output" : "@" + }, + "shiftCaps" : { + "output" : "@" + }, + "shiftCapsOption" : { + "output" : "€" + }, + "shiftOption" : { + "output" : "€" + }, + "unmodified" : { + "output" : "2" + } + }, + "2" : { + "caps" : { + "output" : "S" + }, + "capsOption" : { + "output" : "Í" + }, + "keyCode" : 2, + "option" : { + "output" : "ß" + }, + "shift" : { + "output" : "S" + }, + "shiftCaps" : { + "output" : "S" + }, + "shiftCapsOption" : { + "output" : "Í" + }, + "shiftOption" : { + "output" : "Í" + }, + "unmodified" : { + "output" : "s" + } + }, + "20" : { + "caps" : { + "output" : "3" + }, + "capsOption" : { + "output" : "£" + }, + "keyCode" : 20, + "option" : { + "output" : "£" + }, + "shift" : { + "output" : "#" + }, + "shiftCaps" : { + "output" : "#" + }, + "shiftCapsOption" : { + "output" : "‹" + }, + "shiftOption" : { + "output" : "‹" + }, + "unmodified" : { + "output" : "3" + } + }, + "21" : { + "caps" : { + "output" : "4" + }, + "capsOption" : { + "output" : "¢" + }, + "keyCode" : 21, + "option" : { + "output" : "¢" + }, + "shift" : { + "output" : "$" + }, + "shiftCaps" : { + "output" : "$" + }, + "shiftCapsOption" : { + "output" : "›" + }, + "shiftOption" : { + "output" : "›" + }, + "unmodified" : { + "output" : "4" + } + }, + "22" : { + "caps" : { + "output" : "6" + }, + "capsOption" : { + "output" : "§" + }, + "keyCode" : 22, + "option" : { + "output" : "§" + }, + "shift" : { + "output" : "^" + }, + "shiftCaps" : { + "output" : "^" + }, + "shiftCapsOption" : { + "output" : "fl" + }, + "shiftOption" : { + "output" : "fl" + }, + "unmodified" : { + "output" : "6" + } + }, + "23" : { + "caps" : { + "output" : "5" + }, + "capsOption" : { + "output" : "∞" + }, + "keyCode" : 23, + "option" : { + "output" : "∞" + }, + "shift" : { + "output" : "%" + }, + "shiftCaps" : { + "output" : "%" + }, + "shiftCapsOption" : { + "output" : "fi" + }, + "shiftOption" : { + "output" : "fi" + }, + "unmodified" : { + "output" : "5" + } + }, + "24" : { + "caps" : { + "output" : "=" + }, + "capsOption" : { + "output" : "≠" + }, + "keyCode" : 24, + "option" : { + "output" : "≠" + }, + "shift" : { + "output" : "+" + }, + "shiftCaps" : { + "output" : "+" + }, + "shiftCapsOption" : { + "output" : "±" + }, + "shiftOption" : { + "output" : "±" + }, + "unmodified" : { + "output" : "=" + } + }, + "25" : { + "caps" : { + "output" : "9" + }, + "capsOption" : { + "output" : "ª" + }, + "keyCode" : 25, + "option" : { + "output" : "ª" + }, + "shift" : { + "output" : "(" + }, + "shiftCaps" : { + "output" : "(" + }, + "shiftCapsOption" : { + "output" : "·" + }, + "shiftOption" : { + "output" : "·" + }, + "unmodified" : { + "output" : "9" + } + }, + "26" : { + "caps" : { + "output" : "7" + }, + "capsOption" : { + "output" : "¶" + }, + "keyCode" : 26, + "option" : { + "output" : "¶" + }, + "shift" : { + "output" : "&" + }, + "shiftCaps" : { + "output" : "&" + }, + "shiftCapsOption" : { + "output" : "‡" + }, + "shiftOption" : { + "output" : "‡" + }, + "unmodified" : { + "output" : "7" + } + }, + "27" : { + "caps" : { + "output" : "-" + }, + "capsOption" : { + "output" : "–" + }, + "keyCode" : 27, + "option" : { + "output" : "–" + }, + "shift" : { + "output" : "_" + }, + "shiftCaps" : { + "output" : "_" + }, + "shiftCapsOption" : { + "output" : "—" + }, + "shiftOption" : { + "output" : "—" + }, + "unmodified" : { + "output" : "-" + } + }, + "28" : { + "caps" : { + "output" : "8" + }, + "capsOption" : { + "output" : "•" + }, + "keyCode" : 28, + "option" : { + "output" : "•" + }, + "shift" : { + "output" : "*" + }, + "shiftCaps" : { + "output" : "*" + }, + "shiftCapsOption" : { + "output" : "°" + }, + "shiftOption" : { + "output" : "°" + }, + "unmodified" : { + "output" : "8" + } + }, + "29" : { + "caps" : { + "output" : "0" + }, + "capsOption" : { + "output" : "º" + }, + "keyCode" : 29, + "option" : { + "output" : "º" + }, + "shift" : { + "output" : ")" + }, + "shiftCaps" : { + "output" : ")" + }, + "shiftCapsOption" : { + "output" : "‚" + }, + "shiftOption" : { + "output" : "‚" + }, + "unmodified" : { + "output" : "0" + } + }, + "3" : { + "caps" : { + "output" : "T" + }, + "capsOption" : { + "output" : "†" + }, + "keyCode" : 3, + "option" : { + "output" : "†" + }, + "shift" : { + "output" : "T" + }, + "shiftCaps" : { + "output" : "T" + }, + "shiftCapsOption" : { + "output" : "ˇ" + }, + "shiftOption" : { + "output" : "ˇ" + }, + "unmodified" : { + "output" : "t" + } + }, + "30" : { + "caps" : { + "output" : "]" + }, + "capsOption" : { + "output" : "‘" + }, + "keyCode" : 30, + "option" : { + "output" : "‘" + }, + "shift" : { + "output" : "}" + }, + "shiftCaps" : { + "output" : "}" + }, + "shiftCapsOption" : { + "output" : "’" + }, + "shiftOption" : { + "output" : "’" + }, + "unmodified" : { + "output" : "]" + } + }, + "31" : { + "caps" : { + "output" : "Y" + }, + "capsOption" : { + "output" : "Á" + }, + "keyCode" : 31, + "option" : { + "output" : "¥" + }, + "shift" : { + "output" : "Y" + }, + "shiftCaps" : { + "output" : "Y" + }, + "shiftCapsOption" : { + "output" : "Á" + }, + "shiftOption" : { + "output" : "Á" + }, + "unmodified" : { + "output" : "y" + } + }, + "32" : { + "caps" : { + "output" : "L" + }, + "capsOption" : { + "output" : "Ò" + }, + "keyCode" : 32, + "option" : { + "output" : "¬" + }, + "shift" : { + "output" : "L" + }, + "shiftCaps" : { + "output" : "L" + }, + "shiftCapsOption" : { + "output" : "Ò" + }, + "shiftOption" : { + "output" : "Ò" + }, + "unmodified" : { + "output" : "l" + } + }, + "33" : { + "caps" : { + "output" : "[" + }, + "capsOption" : { + "output" : "“" + }, + "keyCode" : 33, + "option" : { + "output" : "“" + }, + "shift" : { + "output" : "{" + }, + "shiftCaps" : { + "output" : "{" + }, + "shiftCapsOption" : { + "output" : "”" + }, + "shiftOption" : { + "output" : "”" + }, + "unmodified" : { + "output" : "[" + } + }, + "34" : { + "caps" : { + "output" : "U" + }, + "capsOption" : { + "output" : "¨" + }, + "keyCode" : 34, + "option" : { + "rejection" : "deadKey" + }, + "shift" : { + "output" : "U" + }, + "shiftCaps" : { + "output" : "U" + }, + "shiftCapsOption" : { + "output" : "¨" + }, + "shiftOption" : { + "output" : "¨" + }, + "unmodified" : { + "output" : "u" + } + }, + "35" : { + "caps" : { + "output" : ";" + }, + "capsOption" : { + "output" : "…" + }, + "keyCode" : 35, + "option" : { + "output" : "…" + }, + "shift" : { + "output" : ":" + }, + "shiftCaps" : { + "output" : ":" + }, + "shiftCapsOption" : { + "output" : "Ú" + }, + "shiftOption" : { + "output" : "Ú" + }, + "unmodified" : { + "output" : ";" + } + }, + "36" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 36, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "37" : { + "caps" : { + "output" : "I" + }, + "capsOption" : { + "output" : "ˆ" + }, + "keyCode" : 37, + "option" : { + "rejection" : "deadKey" + }, + "shift" : { + "output" : "I" + }, + "shiftCaps" : { + "output" : "I" + }, + "shiftCapsOption" : { + "output" : "ˆ" + }, + "shiftOption" : { + "output" : "ˆ" + }, + "unmodified" : { + "output" : "i" + } + }, + "38" : { + "caps" : { + "output" : "N" + }, + "capsOption" : { + "output" : "˜" + }, + "keyCode" : 38, + "option" : { + "rejection" : "deadKey" + }, + "shift" : { + "output" : "N" + }, + "shiftCaps" : { + "output" : "N" + }, + "shiftCapsOption" : { + "output" : "˜" + }, + "shiftOption" : { + "output" : "˜" + }, + "unmodified" : { + "output" : "n" + } + }, + "39" : { + "caps" : { + "output" : "'" + }, + "capsOption" : { + "output" : "Æ" + }, + "keyCode" : 39, + "option" : { + "output" : "æ" + }, + "shift" : { + "output" : "\"" + }, + "shiftCaps" : { + "output" : "\"" + }, + "shiftCapsOption" : { + "output" : "Æ" + }, + "shiftOption" : { + "output" : "Æ" + }, + "unmodified" : { + "output" : "'" + } + }, + "4" : { + "caps" : { + "output" : "H" + }, + "capsOption" : { + "output" : "Ó" + }, + "keyCode" : 4, + "option" : { + "output" : "˙" + }, + "shift" : { + "output" : "H" + }, + "shiftCaps" : { + "output" : "H" + }, + "shiftCapsOption" : { + "output" : "Ó" + }, + "shiftOption" : { + "output" : "Ó" + }, + "unmodified" : { + "output" : "h" + } + }, + "40" : { + "caps" : { + "output" : "E" + }, + "capsOption" : { + "output" : "´" + }, + "keyCode" : 40, + "option" : { + "rejection" : "deadKey" + }, + "shift" : { + "output" : "E" + }, + "shiftCaps" : { + "output" : "E" + }, + "shiftCapsOption" : { + "output" : "´" + }, + "shiftOption" : { + "output" : "´" + }, + "unmodified" : { + "output" : "e" + } + }, + "41" : { + "caps" : { + "output" : "O" + }, + "capsOption" : { + "output" : "Ø" + }, + "keyCode" : 41, + "option" : { + "output" : "ø" + }, + "shift" : { + "output" : "O" + }, + "shiftCaps" : { + "output" : "O" + }, + "shiftCapsOption" : { + "output" : "Ø" + }, + "shiftOption" : { + "output" : "Ø" + }, + "unmodified" : { + "output" : "o" + } + }, + "42" : { + "caps" : { + "output" : "\\" + }, + "capsOption" : { + "output" : "«" + }, + "keyCode" : 42, + "option" : { + "output" : "«" + }, + "shift" : { + "output" : "|" + }, + "shiftCaps" : { + "output" : "|" + }, + "shiftCapsOption" : { + "output" : "»" + }, + "shiftOption" : { + "output" : "»" + }, + "unmodified" : { + "output" : "\\" + } + }, + "43" : { + "caps" : { + "output" : "," + }, + "capsOption" : { + "output" : "≤" + }, + "keyCode" : 43, + "option" : { + "output" : "≤" + }, + "shift" : { + "output" : "<" + }, + "shiftCaps" : { + "output" : "<" + }, + "shiftCapsOption" : { + "output" : "¯" + }, + "shiftOption" : { + "output" : "¯" + }, + "unmodified" : { + "output" : "," + } + }, + "44" : { + "caps" : { + "output" : "\/" + }, + "capsOption" : { + "output" : "÷" + }, + "keyCode" : 44, + "option" : { + "output" : "÷" + }, + "shift" : { + "output" : "?" + }, + "shiftCaps" : { + "output" : "?" + }, + "shiftCapsOption" : { + "output" : "¿" + }, + "shiftOption" : { + "output" : "¿" + }, + "unmodified" : { + "output" : "\/" + } + }, + "45" : { + "caps" : { + "output" : "K" + }, + "capsOption" : { + "output" : "˚" + }, + "keyCode" : 45, + "option" : { + "output" : "˚" + }, + "shift" : { + "output" : "K" + }, + "shiftCaps" : { + "output" : "K" + }, + "shiftCapsOption" : { + "rejection" : "privateUseOrSurrogate" + }, + "shiftOption" : { + "rejection" : "privateUseOrSurrogate" + }, + "unmodified" : { + "output" : "k" + } + }, + "46" : { + "caps" : { + "output" : "M" + }, + "capsOption" : { + "output" : "Â" + }, + "keyCode" : 46, + "option" : { + "output" : "µ" + }, + "shift" : { + "output" : "M" + }, + "shiftCaps" : { + "output" : "M" + }, + "shiftCapsOption" : { + "output" : "Â" + }, + "shiftOption" : { + "output" : "Â" + }, + "unmodified" : { + "output" : "m" + } + }, + "47" : { + "caps" : { + "output" : "." + }, + "capsOption" : { + "output" : "≥" + }, + "keyCode" : 47, + "option" : { + "output" : "≥" + }, + "shift" : { + "output" : ">" + }, + "shiftCaps" : { + "output" : ">" + }, + "shiftCapsOption" : { + "output" : "˘" + }, + "shiftOption" : { + "output" : "˘" + }, + "unmodified" : { + "output" : "." + } + }, + "48" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 48, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "49" : { + "caps" : { + "output" : " " + }, + "capsOption" : { + "output" : " " + }, + "keyCode" : 49, + "option" : { + "output" : " " + }, + "shift" : { + "output" : " " + }, + "shiftCaps" : { + "output" : " " + }, + "shiftCapsOption" : { + "output" : " " + }, + "shiftOption" : { + "output" : " " + }, + "unmodified" : { + "output" : " " + } + }, + "5" : { + "caps" : { + "output" : "D" + }, + "capsOption" : { + "output" : "Î" + }, + "keyCode" : 5, + "option" : { + "output" : "∂" + }, + "shift" : { + "output" : "D" + }, + "shiftCaps" : { + "output" : "D" + }, + "shiftCapsOption" : { + "output" : "Î" + }, + "shiftOption" : { + "output" : "Î" + }, + "unmodified" : { + "output" : "d" + } + }, + "50" : { + "caps" : { + "output" : "`" + }, + "capsOption" : { + "output" : "`" + }, + "keyCode" : 50, + "option" : { + "rejection" : "deadKey" + }, + "shift" : { + "output" : "~" + }, + "shiftCaps" : { + "output" : "~" + }, + "shiftCapsOption" : { + "output" : "`" + }, + "shiftOption" : { + "output" : "`" + }, + "unmodified" : { + "output" : "`" + } + }, + "51" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 51, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "52" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 52, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "53" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 53, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "54" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 54, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "55" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 55, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "56" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 56, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "57" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 57, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "58" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 58, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "59" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 59, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "6" : { + "caps" : { + "output" : "Z" + }, + "capsOption" : { + "output" : "Ω" + }, + "keyCode" : 6, + "option" : { + "output" : "Ω" + }, + "shift" : { + "output" : "Z" + }, + "shiftCaps" : { + "output" : "Z" + }, + "shiftCapsOption" : { + "output" : "¸" + }, + "shiftOption" : { + "output" : "¸" + }, + "unmodified" : { + "output" : "z" + } + }, + "60" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 60, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "61" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 61, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "62" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 62, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "63" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 63, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "64" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 64, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "65" : { + "caps" : { + "output" : "." + }, + "capsOption" : { + "output" : "." + }, + "keyCode" : 65, + "option" : { + "output" : "." + }, + "shift" : { + "output" : "." + }, + "shiftCaps" : { + "output" : "." + }, + "shiftCapsOption" : { + "output" : "." + }, + "shiftOption" : { + "output" : "." + }, + "unmodified" : { + "output" : "." + } + }, + "66" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 66, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "*" + }, + "shiftCaps" : { + "output" : "*" + }, + "shiftCapsOption" : { + "output" : "*" + }, + "shiftOption" : { + "output" : "*" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "67" : { + "caps" : { + "output" : "*" + }, + "capsOption" : { + "output" : "*" + }, + "keyCode" : 67, + "option" : { + "output" : "*" + }, + "shift" : { + "output" : "*" + }, + "shiftCaps" : { + "output" : "*" + }, + "shiftCapsOption" : { + "output" : "*" + }, + "shiftOption" : { + "output" : "*" + }, + "unmodified" : { + "output" : "*" + } + }, + "68" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 68, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "69" : { + "caps" : { + "output" : "+" + }, + "capsOption" : { + "output" : "+" + }, + "keyCode" : 69, + "option" : { + "output" : "+" + }, + "shift" : { + "output" : "+" + }, + "shiftCaps" : { + "output" : "+" + }, + "shiftCapsOption" : { + "output" : "+" + }, + "shiftOption" : { + "output" : "+" + }, + "unmodified" : { + "output" : "+" + } + }, + "7" : { + "caps" : { + "output" : "X" + }, + "capsOption" : { + "output" : "≈" + }, + "keyCode" : 7, + "option" : { + "output" : "≈" + }, + "shift" : { + "output" : "X" + }, + "shiftCaps" : { + "output" : "X" + }, + "shiftCapsOption" : { + "output" : "˛" + }, + "shiftOption" : { + "output" : "˛" + }, + "unmodified" : { + "output" : "x" + } + }, + "70" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 70, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "+" + }, + "shiftCaps" : { + "output" : "+" + }, + "shiftCapsOption" : { + "output" : "+" + }, + "shiftOption" : { + "output" : "+" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "71" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 71, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "72" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 72, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "=" + }, + "shiftCaps" : { + "output" : "=" + }, + "shiftCapsOption" : { + "output" : "=" + }, + "shiftOption" : { + "output" : "=" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "73" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 73, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "74" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 74, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "75" : { + "caps" : { + "output" : "\/" + }, + "capsOption" : { + "output" : "\/" + }, + "keyCode" : 75, + "option" : { + "output" : "\/" + }, + "shift" : { + "output" : "\/" + }, + "shiftCaps" : { + "output" : "\/" + }, + "shiftCapsOption" : { + "output" : "\/" + }, + "shiftOption" : { + "output" : "\/" + }, + "unmodified" : { + "output" : "\/" + } + }, + "76" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 76, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "77" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 77, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "\/" + }, + "shiftCaps" : { + "output" : "\/" + }, + "shiftCapsOption" : { + "output" : "\/" + }, + "shiftOption" : { + "output" : "\/" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "78" : { + "caps" : { + "output" : "-" + }, + "capsOption" : { + "output" : "-" + }, + "keyCode" : 78, + "option" : { + "output" : "-" + }, + "shift" : { + "output" : "-" + }, + "shiftCaps" : { + "output" : "-" + }, + "shiftCapsOption" : { + "output" : "-" + }, + "shiftOption" : { + "output" : "-" + }, + "unmodified" : { + "output" : "-" + } + }, + "79" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 79, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "8" : { + "caps" : { + "output" : "C" + }, + "capsOption" : { + "output" : "Ç" + }, + "keyCode" : 8, + "option" : { + "output" : "ç" + }, + "shift" : { + "output" : "C" + }, + "shiftCaps" : { + "output" : "C" + }, + "shiftCapsOption" : { + "output" : "Ç" + }, + "shiftOption" : { + "output" : "Ç" + }, + "unmodified" : { + "output" : "c" + } + }, + "80" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 80, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "81" : { + "caps" : { + "output" : "=" + }, + "capsOption" : { + "output" : "=" + }, + "keyCode" : 81, + "option" : { + "output" : "=" + }, + "shift" : { + "output" : "=" + }, + "shiftCaps" : { + "output" : "=" + }, + "shiftCapsOption" : { + "output" : "=" + }, + "shiftOption" : { + "output" : "=" + }, + "unmodified" : { + "output" : "=" + } + }, + "82" : { + "caps" : { + "output" : "0" + }, + "capsOption" : { + "output" : "0" + }, + "keyCode" : 82, + "option" : { + "output" : "0" + }, + "shift" : { + "output" : "0" + }, + "shiftCaps" : { + "output" : "0" + }, + "shiftCapsOption" : { + "output" : "0" + }, + "shiftOption" : { + "output" : "0" + }, + "unmodified" : { + "output" : "0" + } + }, + "83" : { + "caps" : { + "output" : "1" + }, + "capsOption" : { + "output" : "1" + }, + "keyCode" : 83, + "option" : { + "output" : "1" + }, + "shift" : { + "output" : "1" + }, + "shiftCaps" : { + "output" : "1" + }, + "shiftCapsOption" : { + "output" : "1" + }, + "shiftOption" : { + "output" : "1" + }, + "unmodified" : { + "output" : "1" + } + }, + "84" : { + "caps" : { + "output" : "2" + }, + "capsOption" : { + "output" : "2" + }, + "keyCode" : 84, + "option" : { + "output" : "2" + }, + "shift" : { + "output" : "2" + }, + "shiftCaps" : { + "output" : "2" + }, + "shiftCapsOption" : { + "output" : "2" + }, + "shiftOption" : { + "output" : "2" + }, + "unmodified" : { + "output" : "2" + } + }, + "85" : { + "caps" : { + "output" : "3" + }, + "capsOption" : { + "output" : "3" + }, + "keyCode" : 85, + "option" : { + "output" : "3" + }, + "shift" : { + "output" : "3" + }, + "shiftCaps" : { + "output" : "3" + }, + "shiftCapsOption" : { + "output" : "3" + }, + "shiftOption" : { + "output" : "3" + }, + "unmodified" : { + "output" : "3" + } + }, + "86" : { + "caps" : { + "output" : "4" + }, + "capsOption" : { + "output" : "4" + }, + "keyCode" : 86, + "option" : { + "output" : "4" + }, + "shift" : { + "output" : "4" + }, + "shiftCaps" : { + "output" : "4" + }, + "shiftCapsOption" : { + "output" : "4" + }, + "shiftOption" : { + "output" : "4" + }, + "unmodified" : { + "output" : "4" + } + }, + "87" : { + "caps" : { + "output" : "5" + }, + "capsOption" : { + "output" : "5" + }, + "keyCode" : 87, + "option" : { + "output" : "5" + }, + "shift" : { + "output" : "5" + }, + "shiftCaps" : { + "output" : "5" + }, + "shiftCapsOption" : { + "output" : "5" + }, + "shiftOption" : { + "output" : "5" + }, + "unmodified" : { + "output" : "5" + } + }, + "88" : { + "caps" : { + "output" : "6" + }, + "capsOption" : { + "output" : "6" + }, + "keyCode" : 88, + "option" : { + "output" : "6" + }, + "shift" : { + "output" : "6" + }, + "shiftCaps" : { + "output" : "6" + }, + "shiftCapsOption" : { + "output" : "6" + }, + "shiftOption" : { + "output" : "6" + }, + "unmodified" : { + "output" : "6" + } + }, + "89" : { + "caps" : { + "output" : "7" + }, + "capsOption" : { + "output" : "7" + }, + "keyCode" : 89, + "option" : { + "output" : "7" + }, + "shift" : { + "output" : "7" + }, + "shiftCaps" : { + "output" : "7" + }, + "shiftCapsOption" : { + "output" : "7" + }, + "shiftOption" : { + "output" : "7" + }, + "unmodified" : { + "output" : "7" + } + }, + "9" : { + "caps" : { + "output" : "V" + }, + "capsOption" : { + "output" : "√" + }, + "keyCode" : 9, + "option" : { + "output" : "√" + }, + "shift" : { + "output" : "V" + }, + "shiftCaps" : { + "output" : "V" + }, + "shiftCapsOption" : { + "output" : "◊" + }, + "shiftOption" : { + "output" : "◊" + }, + "unmodified" : { + "output" : "v" + } + }, + "90" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 90, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "91" : { + "caps" : { + "output" : "8" + }, + "capsOption" : { + "output" : "8" + }, + "keyCode" : 91, + "option" : { + "output" : "8" + }, + "shift" : { + "output" : "8" + }, + "shiftCaps" : { + "output" : "8" + }, + "shiftCapsOption" : { + "output" : "8" + }, + "shiftOption" : { + "output" : "8" + }, + "unmodified" : { + "output" : "8" + } + }, + "92" : { + "caps" : { + "output" : "9" + }, + "capsOption" : { + "output" : "9" + }, + "keyCode" : 92, + "option" : { + "output" : "9" + }, + "shift" : { + "output" : "9" + }, + "shiftCaps" : { + "output" : "9" + }, + "shiftCapsOption" : { + "output" : "9" + }, + "shiftOption" : { + "output" : "9" + }, + "unmodified" : { + "output" : "9" + } + }, + "93" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 93, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "94" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 94, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "95" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 95, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "96" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 96, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "97" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 97, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "98" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 98, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "99" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 99, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + } + }, + "keyboardType" : 37, + "osBuild" : "24G830", + "osVersion" : "15.7.9", + "schemaVersion" : 1, + "sourceID" : "com.apple.keylayout.Colemak" +} \ No newline at end of file diff --git a/Benchmarks/layout-tables/v1/com.apple.keylayout.Colemak@91.json b/Benchmarks/layout-tables/v1/com.apple.keylayout.Colemak@91.json new file mode 100644 index 0000000..aab2ba4 --- /dev/null +++ b/Benchmarks/layout-tables/v1/com.apple.keylayout.Colemak@91.json @@ -0,0 +1,3465 @@ +{ + "keyOutputs" : { + "0" : { + "caps" : { + "output" : "A" + }, + "capsOption" : { + "output" : "Å" + }, + "keyCode" : 0, + "option" : { + "output" : "å" + }, + "shift" : { + "output" : "A" + }, + "shiftCaps" : { + "output" : "A" + }, + "shiftCapsOption" : { + "output" : "Å" + }, + "shiftOption" : { + "output" : "Å" + }, + "unmodified" : { + "output" : "a" + } + }, + "1" : { + "caps" : { + "output" : "R" + }, + "capsOption" : { + "output" : "®" + }, + "keyCode" : 1, + "option" : { + "output" : "®" + }, + "shift" : { + "output" : "R" + }, + "shiftCaps" : { + "output" : "R" + }, + "shiftCapsOption" : { + "output" : "‰" + }, + "shiftOption" : { + "output" : "‰" + }, + "unmodified" : { + "output" : "r" + } + }, + "10" : { + "caps" : { + "output" : "§" + }, + "capsOption" : { + "output" : "§" + }, + "keyCode" : 10, + "option" : { + "output" : "§" + }, + "shift" : { + "output" : "±" + }, + "shiftCaps" : { + "output" : "±" + }, + "shiftCapsOption" : { + "output" : "±" + }, + "shiftOption" : { + "output" : "±" + }, + "unmodified" : { + "output" : "§" + } + }, + "100" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 100, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "101" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 101, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "102" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 102, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "103" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 103, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "104" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 104, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "105" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 105, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "106" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 106, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "107" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 107, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "108" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 108, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "109" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 109, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "11" : { + "caps" : { + "output" : "B" + }, + "capsOption" : { + "output" : "ı" + }, + "keyCode" : 11, + "option" : { + "output" : "∫" + }, + "shift" : { + "output" : "B" + }, + "shiftCaps" : { + "output" : "B" + }, + "shiftCapsOption" : { + "output" : "ı" + }, + "shiftOption" : { + "output" : "ı" + }, + "unmodified" : { + "output" : "b" + } + }, + "110" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 110, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "111" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 111, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "112" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 112, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "113" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 113, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "114" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 114, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "115" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 115, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "116" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 116, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "117" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 117, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "118" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 118, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "119" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 119, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "12" : { + "caps" : { + "output" : "Q" + }, + "capsOption" : { + "output" : "Œ" + }, + "keyCode" : 12, + "option" : { + "output" : "œ" + }, + "shift" : { + "output" : "Q" + }, + "shiftCaps" : { + "output" : "Q" + }, + "shiftCapsOption" : { + "output" : "Œ" + }, + "shiftOption" : { + "output" : "Œ" + }, + "unmodified" : { + "output" : "q" + } + }, + "120" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 120, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "121" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 121, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "122" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 122, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "123" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 123, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "124" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 124, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "125" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 125, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "126" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 126, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "127" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 127, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "13" : { + "caps" : { + "output" : "W" + }, + "capsOption" : { + "output" : "∑" + }, + "keyCode" : 13, + "option" : { + "output" : "∑" + }, + "shift" : { + "output" : "W" + }, + "shiftCaps" : { + "output" : "W" + }, + "shiftCapsOption" : { + "output" : "„" + }, + "shiftOption" : { + "output" : "„" + }, + "unmodified" : { + "output" : "w" + } + }, + "14" : { + "caps" : { + "output" : "F" + }, + "capsOption" : { + "output" : "Ï" + }, + "keyCode" : 14, + "option" : { + "output" : "ƒ" + }, + "shift" : { + "output" : "F" + }, + "shiftCaps" : { + "output" : "F" + }, + "shiftCapsOption" : { + "output" : "Ï" + }, + "shiftOption" : { + "output" : "Ï" + }, + "unmodified" : { + "output" : "f" + } + }, + "15" : { + "caps" : { + "output" : "P" + }, + "capsOption" : { + "output" : "∏" + }, + "keyCode" : 15, + "option" : { + "output" : "π" + }, + "shift" : { + "output" : "P" + }, + "shiftCaps" : { + "output" : "P" + }, + "shiftCapsOption" : { + "output" : "∏" + }, + "shiftOption" : { + "output" : "∏" + }, + "unmodified" : { + "output" : "p" + } + }, + "16" : { + "caps" : { + "output" : "J" + }, + "capsOption" : { + "output" : "Ô" + }, + "keyCode" : 16, + "option" : { + "output" : "∆" + }, + "shift" : { + "output" : "J" + }, + "shiftCaps" : { + "output" : "J" + }, + "shiftCapsOption" : { + "output" : "Ô" + }, + "shiftOption" : { + "output" : "Ô" + }, + "unmodified" : { + "output" : "j" + } + }, + "17" : { + "caps" : { + "output" : "G" + }, + "capsOption" : { + "output" : "©" + }, + "keyCode" : 17, + "option" : { + "output" : "©" + }, + "shift" : { + "output" : "G" + }, + "shiftCaps" : { + "output" : "G" + }, + "shiftCapsOption" : { + "output" : "˝" + }, + "shiftOption" : { + "output" : "˝" + }, + "unmodified" : { + "output" : "g" + } + }, + "18" : { + "caps" : { + "output" : "1" + }, + "capsOption" : { + "output" : "¡" + }, + "keyCode" : 18, + "option" : { + "output" : "¡" + }, + "shift" : { + "output" : "!" + }, + "shiftCaps" : { + "output" : "!" + }, + "shiftCapsOption" : { + "output" : "⁄" + }, + "shiftOption" : { + "output" : "⁄" + }, + "unmodified" : { + "output" : "1" + } + }, + "19" : { + "caps" : { + "output" : "2" + }, + "capsOption" : { + "output" : "™" + }, + "keyCode" : 19, + "option" : { + "output" : "™" + }, + "shift" : { + "output" : "@" + }, + "shiftCaps" : { + "output" : "@" + }, + "shiftCapsOption" : { + "output" : "€" + }, + "shiftOption" : { + "output" : "€" + }, + "unmodified" : { + "output" : "2" + } + }, + "2" : { + "caps" : { + "output" : "S" + }, + "capsOption" : { + "output" : "Í" + }, + "keyCode" : 2, + "option" : { + "output" : "ß" + }, + "shift" : { + "output" : "S" + }, + "shiftCaps" : { + "output" : "S" + }, + "shiftCapsOption" : { + "output" : "Í" + }, + "shiftOption" : { + "output" : "Í" + }, + "unmodified" : { + "output" : "s" + } + }, + "20" : { + "caps" : { + "output" : "3" + }, + "capsOption" : { + "output" : "£" + }, + "keyCode" : 20, + "option" : { + "output" : "£" + }, + "shift" : { + "output" : "#" + }, + "shiftCaps" : { + "output" : "#" + }, + "shiftCapsOption" : { + "output" : "‹" + }, + "shiftOption" : { + "output" : "‹" + }, + "unmodified" : { + "output" : "3" + } + }, + "21" : { + "caps" : { + "output" : "4" + }, + "capsOption" : { + "output" : "¢" + }, + "keyCode" : 21, + "option" : { + "output" : "¢" + }, + "shift" : { + "output" : "$" + }, + "shiftCaps" : { + "output" : "$" + }, + "shiftCapsOption" : { + "output" : "›" + }, + "shiftOption" : { + "output" : "›" + }, + "unmodified" : { + "output" : "4" + } + }, + "22" : { + "caps" : { + "output" : "6" + }, + "capsOption" : { + "output" : "§" + }, + "keyCode" : 22, + "option" : { + "output" : "§" + }, + "shift" : { + "output" : "^" + }, + "shiftCaps" : { + "output" : "^" + }, + "shiftCapsOption" : { + "output" : "fl" + }, + "shiftOption" : { + "output" : "fl" + }, + "unmodified" : { + "output" : "6" + } + }, + "23" : { + "caps" : { + "output" : "5" + }, + "capsOption" : { + "output" : "∞" + }, + "keyCode" : 23, + "option" : { + "output" : "∞" + }, + "shift" : { + "output" : "%" + }, + "shiftCaps" : { + "output" : "%" + }, + "shiftCapsOption" : { + "output" : "fi" + }, + "shiftOption" : { + "output" : "fi" + }, + "unmodified" : { + "output" : "5" + } + }, + "24" : { + "caps" : { + "output" : "=" + }, + "capsOption" : { + "output" : "≠" + }, + "keyCode" : 24, + "option" : { + "output" : "≠" + }, + "shift" : { + "output" : "+" + }, + "shiftCaps" : { + "output" : "+" + }, + "shiftCapsOption" : { + "output" : "±" + }, + "shiftOption" : { + "output" : "±" + }, + "unmodified" : { + "output" : "=" + } + }, + "25" : { + "caps" : { + "output" : "9" + }, + "capsOption" : { + "output" : "ª" + }, + "keyCode" : 25, + "option" : { + "output" : "ª" + }, + "shift" : { + "output" : "(" + }, + "shiftCaps" : { + "output" : "(" + }, + "shiftCapsOption" : { + "output" : "·" + }, + "shiftOption" : { + "output" : "·" + }, + "unmodified" : { + "output" : "9" + } + }, + "26" : { + "caps" : { + "output" : "7" + }, + "capsOption" : { + "output" : "¶" + }, + "keyCode" : 26, + "option" : { + "output" : "¶" + }, + "shift" : { + "output" : "&" + }, + "shiftCaps" : { + "output" : "&" + }, + "shiftCapsOption" : { + "output" : "‡" + }, + "shiftOption" : { + "output" : "‡" + }, + "unmodified" : { + "output" : "7" + } + }, + "27" : { + "caps" : { + "output" : "-" + }, + "capsOption" : { + "output" : "–" + }, + "keyCode" : 27, + "option" : { + "output" : "–" + }, + "shift" : { + "output" : "_" + }, + "shiftCaps" : { + "output" : "_" + }, + "shiftCapsOption" : { + "output" : "—" + }, + "shiftOption" : { + "output" : "—" + }, + "unmodified" : { + "output" : "-" + } + }, + "28" : { + "caps" : { + "output" : "8" + }, + "capsOption" : { + "output" : "•" + }, + "keyCode" : 28, + "option" : { + "output" : "•" + }, + "shift" : { + "output" : "*" + }, + "shiftCaps" : { + "output" : "*" + }, + "shiftCapsOption" : { + "output" : "°" + }, + "shiftOption" : { + "output" : "°" + }, + "unmodified" : { + "output" : "8" + } + }, + "29" : { + "caps" : { + "output" : "0" + }, + "capsOption" : { + "output" : "º" + }, + "keyCode" : 29, + "option" : { + "output" : "º" + }, + "shift" : { + "output" : ")" + }, + "shiftCaps" : { + "output" : ")" + }, + "shiftCapsOption" : { + "output" : "‚" + }, + "shiftOption" : { + "output" : "‚" + }, + "unmodified" : { + "output" : "0" + } + }, + "3" : { + "caps" : { + "output" : "T" + }, + "capsOption" : { + "output" : "†" + }, + "keyCode" : 3, + "option" : { + "output" : "†" + }, + "shift" : { + "output" : "T" + }, + "shiftCaps" : { + "output" : "T" + }, + "shiftCapsOption" : { + "output" : "ˇ" + }, + "shiftOption" : { + "output" : "ˇ" + }, + "unmodified" : { + "output" : "t" + } + }, + "30" : { + "caps" : { + "output" : "]" + }, + "capsOption" : { + "output" : "‘" + }, + "keyCode" : 30, + "option" : { + "output" : "‘" + }, + "shift" : { + "output" : "}" + }, + "shiftCaps" : { + "output" : "}" + }, + "shiftCapsOption" : { + "output" : "’" + }, + "shiftOption" : { + "output" : "’" + }, + "unmodified" : { + "output" : "]" + } + }, + "31" : { + "caps" : { + "output" : "Y" + }, + "capsOption" : { + "output" : "Á" + }, + "keyCode" : 31, + "option" : { + "output" : "¥" + }, + "shift" : { + "output" : "Y" + }, + "shiftCaps" : { + "output" : "Y" + }, + "shiftCapsOption" : { + "output" : "Á" + }, + "shiftOption" : { + "output" : "Á" + }, + "unmodified" : { + "output" : "y" + } + }, + "32" : { + "caps" : { + "output" : "L" + }, + "capsOption" : { + "output" : "Ò" + }, + "keyCode" : 32, + "option" : { + "output" : "¬" + }, + "shift" : { + "output" : "L" + }, + "shiftCaps" : { + "output" : "L" + }, + "shiftCapsOption" : { + "output" : "Ò" + }, + "shiftOption" : { + "output" : "Ò" + }, + "unmodified" : { + "output" : "l" + } + }, + "33" : { + "caps" : { + "output" : "[" + }, + "capsOption" : { + "output" : "“" + }, + "keyCode" : 33, + "option" : { + "output" : "“" + }, + "shift" : { + "output" : "{" + }, + "shiftCaps" : { + "output" : "{" + }, + "shiftCapsOption" : { + "output" : "”" + }, + "shiftOption" : { + "output" : "”" + }, + "unmodified" : { + "output" : "[" + } + }, + "34" : { + "caps" : { + "output" : "U" + }, + "capsOption" : { + "output" : "¨" + }, + "keyCode" : 34, + "option" : { + "rejection" : "deadKey" + }, + "shift" : { + "output" : "U" + }, + "shiftCaps" : { + "output" : "U" + }, + "shiftCapsOption" : { + "output" : "¨" + }, + "shiftOption" : { + "output" : "¨" + }, + "unmodified" : { + "output" : "u" + } + }, + "35" : { + "caps" : { + "output" : ";" + }, + "capsOption" : { + "output" : "…" + }, + "keyCode" : 35, + "option" : { + "output" : "…" + }, + "shift" : { + "output" : ":" + }, + "shiftCaps" : { + "output" : ":" + }, + "shiftCapsOption" : { + "output" : "Ú" + }, + "shiftOption" : { + "output" : "Ú" + }, + "unmodified" : { + "output" : ";" + } + }, + "36" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 36, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "37" : { + "caps" : { + "output" : "I" + }, + "capsOption" : { + "output" : "ˆ" + }, + "keyCode" : 37, + "option" : { + "rejection" : "deadKey" + }, + "shift" : { + "output" : "I" + }, + "shiftCaps" : { + "output" : "I" + }, + "shiftCapsOption" : { + "output" : "ˆ" + }, + "shiftOption" : { + "output" : "ˆ" + }, + "unmodified" : { + "output" : "i" + } + }, + "38" : { + "caps" : { + "output" : "N" + }, + "capsOption" : { + "output" : "˜" + }, + "keyCode" : 38, + "option" : { + "rejection" : "deadKey" + }, + "shift" : { + "output" : "N" + }, + "shiftCaps" : { + "output" : "N" + }, + "shiftCapsOption" : { + "output" : "˜" + }, + "shiftOption" : { + "output" : "˜" + }, + "unmodified" : { + "output" : "n" + } + }, + "39" : { + "caps" : { + "output" : "'" + }, + "capsOption" : { + "output" : "Æ" + }, + "keyCode" : 39, + "option" : { + "output" : "æ" + }, + "shift" : { + "output" : "\"" + }, + "shiftCaps" : { + "output" : "\"" + }, + "shiftCapsOption" : { + "output" : "Æ" + }, + "shiftOption" : { + "output" : "Æ" + }, + "unmodified" : { + "output" : "'" + } + }, + "4" : { + "caps" : { + "output" : "H" + }, + "capsOption" : { + "output" : "Ó" + }, + "keyCode" : 4, + "option" : { + "output" : "˙" + }, + "shift" : { + "output" : "H" + }, + "shiftCaps" : { + "output" : "H" + }, + "shiftCapsOption" : { + "output" : "Ó" + }, + "shiftOption" : { + "output" : "Ó" + }, + "unmodified" : { + "output" : "h" + } + }, + "40" : { + "caps" : { + "output" : "E" + }, + "capsOption" : { + "output" : "´" + }, + "keyCode" : 40, + "option" : { + "rejection" : "deadKey" + }, + "shift" : { + "output" : "E" + }, + "shiftCaps" : { + "output" : "E" + }, + "shiftCapsOption" : { + "output" : "´" + }, + "shiftOption" : { + "output" : "´" + }, + "unmodified" : { + "output" : "e" + } + }, + "41" : { + "caps" : { + "output" : "O" + }, + "capsOption" : { + "output" : "Ø" + }, + "keyCode" : 41, + "option" : { + "output" : "ø" + }, + "shift" : { + "output" : "O" + }, + "shiftCaps" : { + "output" : "O" + }, + "shiftCapsOption" : { + "output" : "Ø" + }, + "shiftOption" : { + "output" : "Ø" + }, + "unmodified" : { + "output" : "o" + } + }, + "42" : { + "caps" : { + "output" : "\\" + }, + "capsOption" : { + "output" : "«" + }, + "keyCode" : 42, + "option" : { + "output" : "«" + }, + "shift" : { + "output" : "|" + }, + "shiftCaps" : { + "output" : "|" + }, + "shiftCapsOption" : { + "output" : "»" + }, + "shiftOption" : { + "output" : "»" + }, + "unmodified" : { + "output" : "\\" + } + }, + "43" : { + "caps" : { + "output" : "," + }, + "capsOption" : { + "output" : "≤" + }, + "keyCode" : 43, + "option" : { + "output" : "≤" + }, + "shift" : { + "output" : "<" + }, + "shiftCaps" : { + "output" : "<" + }, + "shiftCapsOption" : { + "output" : "¯" + }, + "shiftOption" : { + "output" : "¯" + }, + "unmodified" : { + "output" : "," + } + }, + "44" : { + "caps" : { + "output" : "\/" + }, + "capsOption" : { + "output" : "÷" + }, + "keyCode" : 44, + "option" : { + "output" : "÷" + }, + "shift" : { + "output" : "?" + }, + "shiftCaps" : { + "output" : "?" + }, + "shiftCapsOption" : { + "output" : "¿" + }, + "shiftOption" : { + "output" : "¿" + }, + "unmodified" : { + "output" : "\/" + } + }, + "45" : { + "caps" : { + "output" : "K" + }, + "capsOption" : { + "output" : "˚" + }, + "keyCode" : 45, + "option" : { + "output" : "˚" + }, + "shift" : { + "output" : "K" + }, + "shiftCaps" : { + "output" : "K" + }, + "shiftCapsOption" : { + "rejection" : "privateUseOrSurrogate" + }, + "shiftOption" : { + "rejection" : "privateUseOrSurrogate" + }, + "unmodified" : { + "output" : "k" + } + }, + "46" : { + "caps" : { + "output" : "M" + }, + "capsOption" : { + "output" : "Â" + }, + "keyCode" : 46, + "option" : { + "output" : "µ" + }, + "shift" : { + "output" : "M" + }, + "shiftCaps" : { + "output" : "M" + }, + "shiftCapsOption" : { + "output" : "Â" + }, + "shiftOption" : { + "output" : "Â" + }, + "unmodified" : { + "output" : "m" + } + }, + "47" : { + "caps" : { + "output" : "." + }, + "capsOption" : { + "output" : "≥" + }, + "keyCode" : 47, + "option" : { + "output" : "≥" + }, + "shift" : { + "output" : ">" + }, + "shiftCaps" : { + "output" : ">" + }, + "shiftCapsOption" : { + "output" : "˘" + }, + "shiftOption" : { + "output" : "˘" + }, + "unmodified" : { + "output" : "." + } + }, + "48" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 48, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "49" : { + "caps" : { + "output" : " " + }, + "capsOption" : { + "output" : " " + }, + "keyCode" : 49, + "option" : { + "output" : " " + }, + "shift" : { + "output" : " " + }, + "shiftCaps" : { + "output" : " " + }, + "shiftCapsOption" : { + "output" : " " + }, + "shiftOption" : { + "output" : " " + }, + "unmodified" : { + "output" : " " + } + }, + "5" : { + "caps" : { + "output" : "D" + }, + "capsOption" : { + "output" : "Î" + }, + "keyCode" : 5, + "option" : { + "output" : "∂" + }, + "shift" : { + "output" : "D" + }, + "shiftCaps" : { + "output" : "D" + }, + "shiftCapsOption" : { + "output" : "Î" + }, + "shiftOption" : { + "output" : "Î" + }, + "unmodified" : { + "output" : "d" + } + }, + "50" : { + "caps" : { + "output" : "`" + }, + "capsOption" : { + "output" : "`" + }, + "keyCode" : 50, + "option" : { + "rejection" : "deadKey" + }, + "shift" : { + "output" : "~" + }, + "shiftCaps" : { + "output" : "~" + }, + "shiftCapsOption" : { + "output" : "`" + }, + "shiftOption" : { + "output" : "`" + }, + "unmodified" : { + "output" : "`" + } + }, + "51" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 51, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "52" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 52, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "53" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 53, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "54" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 54, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "55" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 55, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "56" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 56, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "57" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 57, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "58" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 58, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "59" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 59, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "6" : { + "caps" : { + "output" : "Z" + }, + "capsOption" : { + "output" : "Ω" + }, + "keyCode" : 6, + "option" : { + "output" : "Ω" + }, + "shift" : { + "output" : "Z" + }, + "shiftCaps" : { + "output" : "Z" + }, + "shiftCapsOption" : { + "output" : "¸" + }, + "shiftOption" : { + "output" : "¸" + }, + "unmodified" : { + "output" : "z" + } + }, + "60" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 60, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "61" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 61, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "62" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 62, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "63" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 63, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "64" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 64, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "65" : { + "caps" : { + "output" : "." + }, + "capsOption" : { + "output" : "." + }, + "keyCode" : 65, + "option" : { + "output" : "." + }, + "shift" : { + "output" : "." + }, + "shiftCaps" : { + "output" : "." + }, + "shiftCapsOption" : { + "output" : "." + }, + "shiftOption" : { + "output" : "." + }, + "unmodified" : { + "output" : "." + } + }, + "66" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 66, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "*" + }, + "shiftCaps" : { + "output" : "*" + }, + "shiftCapsOption" : { + "output" : "*" + }, + "shiftOption" : { + "output" : "*" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "67" : { + "caps" : { + "output" : "*" + }, + "capsOption" : { + "output" : "*" + }, + "keyCode" : 67, + "option" : { + "output" : "*" + }, + "shift" : { + "output" : "*" + }, + "shiftCaps" : { + "output" : "*" + }, + "shiftCapsOption" : { + "output" : "*" + }, + "shiftOption" : { + "output" : "*" + }, + "unmodified" : { + "output" : "*" + } + }, + "68" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 68, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "69" : { + "caps" : { + "output" : "+" + }, + "capsOption" : { + "output" : "+" + }, + "keyCode" : 69, + "option" : { + "output" : "+" + }, + "shift" : { + "output" : "+" + }, + "shiftCaps" : { + "output" : "+" + }, + "shiftCapsOption" : { + "output" : "+" + }, + "shiftOption" : { + "output" : "+" + }, + "unmodified" : { + "output" : "+" + } + }, + "7" : { + "caps" : { + "output" : "X" + }, + "capsOption" : { + "output" : "≈" + }, + "keyCode" : 7, + "option" : { + "output" : "≈" + }, + "shift" : { + "output" : "X" + }, + "shiftCaps" : { + "output" : "X" + }, + "shiftCapsOption" : { + "output" : "˛" + }, + "shiftOption" : { + "output" : "˛" + }, + "unmodified" : { + "output" : "x" + } + }, + "70" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 70, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "+" + }, + "shiftCaps" : { + "output" : "+" + }, + "shiftCapsOption" : { + "output" : "+" + }, + "shiftOption" : { + "output" : "+" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "71" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 71, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "72" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 72, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "=" + }, + "shiftCaps" : { + "output" : "=" + }, + "shiftCapsOption" : { + "output" : "=" + }, + "shiftOption" : { + "output" : "=" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "73" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 73, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "74" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 74, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "75" : { + "caps" : { + "output" : "\/" + }, + "capsOption" : { + "output" : "\/" + }, + "keyCode" : 75, + "option" : { + "output" : "\/" + }, + "shift" : { + "output" : "\/" + }, + "shiftCaps" : { + "output" : "\/" + }, + "shiftCapsOption" : { + "output" : "\/" + }, + "shiftOption" : { + "output" : "\/" + }, + "unmodified" : { + "output" : "\/" + } + }, + "76" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 76, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "77" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 77, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "\/" + }, + "shiftCaps" : { + "output" : "\/" + }, + "shiftCapsOption" : { + "output" : "\/" + }, + "shiftOption" : { + "output" : "\/" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "78" : { + "caps" : { + "output" : "-" + }, + "capsOption" : { + "output" : "-" + }, + "keyCode" : 78, + "option" : { + "output" : "-" + }, + "shift" : { + "output" : "-" + }, + "shiftCaps" : { + "output" : "-" + }, + "shiftCapsOption" : { + "output" : "-" + }, + "shiftOption" : { + "output" : "-" + }, + "unmodified" : { + "output" : "-" + } + }, + "79" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 79, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "8" : { + "caps" : { + "output" : "C" + }, + "capsOption" : { + "output" : "Ç" + }, + "keyCode" : 8, + "option" : { + "output" : "ç" + }, + "shift" : { + "output" : "C" + }, + "shiftCaps" : { + "output" : "C" + }, + "shiftCapsOption" : { + "output" : "Ç" + }, + "shiftOption" : { + "output" : "Ç" + }, + "unmodified" : { + "output" : "c" + } + }, + "80" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 80, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "81" : { + "caps" : { + "output" : "=" + }, + "capsOption" : { + "output" : "=" + }, + "keyCode" : 81, + "option" : { + "output" : "=" + }, + "shift" : { + "output" : "=" + }, + "shiftCaps" : { + "output" : "=" + }, + "shiftCapsOption" : { + "output" : "=" + }, + "shiftOption" : { + "output" : "=" + }, + "unmodified" : { + "output" : "=" + } + }, + "82" : { + "caps" : { + "output" : "0" + }, + "capsOption" : { + "output" : "0" + }, + "keyCode" : 82, + "option" : { + "output" : "0" + }, + "shift" : { + "output" : "0" + }, + "shiftCaps" : { + "output" : "0" + }, + "shiftCapsOption" : { + "output" : "0" + }, + "shiftOption" : { + "output" : "0" + }, + "unmodified" : { + "output" : "0" + } + }, + "83" : { + "caps" : { + "output" : "1" + }, + "capsOption" : { + "output" : "1" + }, + "keyCode" : 83, + "option" : { + "output" : "1" + }, + "shift" : { + "output" : "1" + }, + "shiftCaps" : { + "output" : "1" + }, + "shiftCapsOption" : { + "output" : "1" + }, + "shiftOption" : { + "output" : "1" + }, + "unmodified" : { + "output" : "1" + } + }, + "84" : { + "caps" : { + "output" : "2" + }, + "capsOption" : { + "output" : "2" + }, + "keyCode" : 84, + "option" : { + "output" : "2" + }, + "shift" : { + "output" : "2" + }, + "shiftCaps" : { + "output" : "2" + }, + "shiftCapsOption" : { + "output" : "2" + }, + "shiftOption" : { + "output" : "2" + }, + "unmodified" : { + "output" : "2" + } + }, + "85" : { + "caps" : { + "output" : "3" + }, + "capsOption" : { + "output" : "3" + }, + "keyCode" : 85, + "option" : { + "output" : "3" + }, + "shift" : { + "output" : "3" + }, + "shiftCaps" : { + "output" : "3" + }, + "shiftCapsOption" : { + "output" : "3" + }, + "shiftOption" : { + "output" : "3" + }, + "unmodified" : { + "output" : "3" + } + }, + "86" : { + "caps" : { + "output" : "4" + }, + "capsOption" : { + "output" : "4" + }, + "keyCode" : 86, + "option" : { + "output" : "4" + }, + "shift" : { + "output" : "4" + }, + "shiftCaps" : { + "output" : "4" + }, + "shiftCapsOption" : { + "output" : "4" + }, + "shiftOption" : { + "output" : "4" + }, + "unmodified" : { + "output" : "4" + } + }, + "87" : { + "caps" : { + "output" : "5" + }, + "capsOption" : { + "output" : "5" + }, + "keyCode" : 87, + "option" : { + "output" : "5" + }, + "shift" : { + "output" : "5" + }, + "shiftCaps" : { + "output" : "5" + }, + "shiftCapsOption" : { + "output" : "5" + }, + "shiftOption" : { + "output" : "5" + }, + "unmodified" : { + "output" : "5" + } + }, + "88" : { + "caps" : { + "output" : "6" + }, + "capsOption" : { + "output" : "6" + }, + "keyCode" : 88, + "option" : { + "output" : "6" + }, + "shift" : { + "output" : "6" + }, + "shiftCaps" : { + "output" : "6" + }, + "shiftCapsOption" : { + "output" : "6" + }, + "shiftOption" : { + "output" : "6" + }, + "unmodified" : { + "output" : "6" + } + }, + "89" : { + "caps" : { + "output" : "7" + }, + "capsOption" : { + "output" : "7" + }, + "keyCode" : 89, + "option" : { + "output" : "7" + }, + "shift" : { + "output" : "7" + }, + "shiftCaps" : { + "output" : "7" + }, + "shiftCapsOption" : { + "output" : "7" + }, + "shiftOption" : { + "output" : "7" + }, + "unmodified" : { + "output" : "7" + } + }, + "9" : { + "caps" : { + "output" : "V" + }, + "capsOption" : { + "output" : "√" + }, + "keyCode" : 9, + "option" : { + "output" : "√" + }, + "shift" : { + "output" : "V" + }, + "shiftCaps" : { + "output" : "V" + }, + "shiftCapsOption" : { + "output" : "◊" + }, + "shiftOption" : { + "output" : "◊" + }, + "unmodified" : { + "output" : "v" + } + }, + "90" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 90, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "91" : { + "caps" : { + "output" : "8" + }, + "capsOption" : { + "output" : "8" + }, + "keyCode" : 91, + "option" : { + "output" : "8" + }, + "shift" : { + "output" : "8" + }, + "shiftCaps" : { + "output" : "8" + }, + "shiftCapsOption" : { + "output" : "8" + }, + "shiftOption" : { + "output" : "8" + }, + "unmodified" : { + "output" : "8" + } + }, + "92" : { + "caps" : { + "output" : "9" + }, + "capsOption" : { + "output" : "9" + }, + "keyCode" : 92, + "option" : { + "output" : "9" + }, + "shift" : { + "output" : "9" + }, + "shiftCaps" : { + "output" : "9" + }, + "shiftCapsOption" : { + "output" : "9" + }, + "shiftOption" : { + "output" : "9" + }, + "unmodified" : { + "output" : "9" + } + }, + "93" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 93, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "94" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 94, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "95" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 95, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "96" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 96, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "97" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 97, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "98" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 98, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "99" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 99, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + } + }, + "keyboardType" : 91, + "osBuild" : "24G830", + "osVersion" : "15.7.9", + "schemaVersion" : 1, + "sourceID" : "com.apple.keylayout.Colemak" +} \ No newline at end of file diff --git a/Benchmarks/layout-tables/v1/com.apple.keylayout.Dvorak@37.json b/Benchmarks/layout-tables/v1/com.apple.keylayout.Dvorak@37.json new file mode 100644 index 0000000..5a57bda --- /dev/null +++ b/Benchmarks/layout-tables/v1/com.apple.keylayout.Dvorak@37.json @@ -0,0 +1,3465 @@ +{ + "keyOutputs" : { + "0" : { + "caps" : { + "output" : "A" + }, + "capsOption" : { + "output" : "Å" + }, + "keyCode" : 0, + "option" : { + "output" : "å" + }, + "shift" : { + "output" : "A" + }, + "shiftCaps" : { + "output" : "A" + }, + "shiftCapsOption" : { + "output" : "Å" + }, + "shiftOption" : { + "output" : "Å" + }, + "unmodified" : { + "output" : "a" + } + }, + "1" : { + "caps" : { + "output" : "O" + }, + "capsOption" : { + "output" : "Ø" + }, + "keyCode" : 1, + "option" : { + "output" : "ø" + }, + "shift" : { + "output" : "O" + }, + "shiftCaps" : { + "output" : "O" + }, + "shiftCapsOption" : { + "output" : "Ø" + }, + "shiftOption" : { + "output" : "Ø" + }, + "unmodified" : { + "output" : "o" + } + }, + "10" : { + "caps" : { + "output" : "§" + }, + "capsOption" : { + "output" : "§" + }, + "keyCode" : 10, + "option" : { + "output" : "§" + }, + "shift" : { + "output" : "±" + }, + "shiftCaps" : { + "output" : "±" + }, + "shiftCapsOption" : { + "output" : "±" + }, + "shiftOption" : { + "output" : "±" + }, + "unmodified" : { + "output" : "§" + } + }, + "100" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 100, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "101" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 101, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "102" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 102, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "103" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 103, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "104" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 104, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "105" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 105, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "106" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 106, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "107" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 107, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "108" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 108, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "109" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 109, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "11" : { + "caps" : { + "output" : "X" + }, + "capsOption" : { + "output" : "≈" + }, + "keyCode" : 11, + "option" : { + "output" : "≈" + }, + "shift" : { + "output" : "X" + }, + "shiftCaps" : { + "output" : "X" + }, + "shiftCapsOption" : { + "output" : "˛" + }, + "shiftOption" : { + "output" : "˛" + }, + "unmodified" : { + "output" : "x" + } + }, + "110" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 110, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "111" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 111, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "112" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 112, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "113" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 113, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "114" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 114, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "115" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 115, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "116" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 116, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "117" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 117, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "118" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 118, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "119" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 119, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "12" : { + "caps" : { + "output" : "'" + }, + "capsOption" : { + "output" : "Æ" + }, + "keyCode" : 12, + "option" : { + "output" : "æ" + }, + "shift" : { + "output" : "\"" + }, + "shiftCaps" : { + "output" : "\"" + }, + "shiftCapsOption" : { + "output" : "Æ" + }, + "shiftOption" : { + "output" : "Æ" + }, + "unmodified" : { + "output" : "'" + } + }, + "120" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 120, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "121" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 121, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "122" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 122, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "123" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 123, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "124" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 124, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "125" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 125, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "126" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 126, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "127" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 127, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "13" : { + "caps" : { + "output" : "," + }, + "capsOption" : { + "output" : "≤" + }, + "keyCode" : 13, + "option" : { + "output" : "≤" + }, + "shift" : { + "output" : "<" + }, + "shiftCaps" : { + "output" : "<" + }, + "shiftCapsOption" : { + "output" : "¯" + }, + "shiftOption" : { + "output" : "¯" + }, + "unmodified" : { + "output" : "," + } + }, + "14" : { + "caps" : { + "output" : "." + }, + "capsOption" : { + "output" : "≥" + }, + "keyCode" : 14, + "option" : { + "output" : "≥" + }, + "shift" : { + "output" : ">" + }, + "shiftCaps" : { + "output" : ">" + }, + "shiftCapsOption" : { + "output" : "˘" + }, + "shiftOption" : { + "output" : "˘" + }, + "unmodified" : { + "output" : "." + } + }, + "15" : { + "caps" : { + "output" : "P" + }, + "capsOption" : { + "output" : "∏" + }, + "keyCode" : 15, + "option" : { + "output" : "π" + }, + "shift" : { + "output" : "P" + }, + "shiftCaps" : { + "output" : "P" + }, + "shiftCapsOption" : { + "output" : "∏" + }, + "shiftOption" : { + "output" : "∏" + }, + "unmodified" : { + "output" : "p" + } + }, + "16" : { + "caps" : { + "output" : "F" + }, + "capsOption" : { + "output" : "Ï" + }, + "keyCode" : 16, + "option" : { + "output" : "ƒ" + }, + "shift" : { + "output" : "F" + }, + "shiftCaps" : { + "output" : "F" + }, + "shiftCapsOption" : { + "output" : "Ï" + }, + "shiftOption" : { + "output" : "Ï" + }, + "unmodified" : { + "output" : "f" + } + }, + "17" : { + "caps" : { + "output" : "Y" + }, + "capsOption" : { + "output" : "Á" + }, + "keyCode" : 17, + "option" : { + "output" : "¥" + }, + "shift" : { + "output" : "Y" + }, + "shiftCaps" : { + "output" : "Y" + }, + "shiftCapsOption" : { + "output" : "Á" + }, + "shiftOption" : { + "output" : "Á" + }, + "unmodified" : { + "output" : "y" + } + }, + "18" : { + "caps" : { + "output" : "1" + }, + "capsOption" : { + "output" : "¡" + }, + "keyCode" : 18, + "option" : { + "output" : "¡" + }, + "shift" : { + "output" : "!" + }, + "shiftCaps" : { + "output" : "!" + }, + "shiftCapsOption" : { + "output" : "⁄" + }, + "shiftOption" : { + "output" : "⁄" + }, + "unmodified" : { + "output" : "1" + } + }, + "19" : { + "caps" : { + "output" : "2" + }, + "capsOption" : { + "output" : "™" + }, + "keyCode" : 19, + "option" : { + "output" : "™" + }, + "shift" : { + "output" : "@" + }, + "shiftCaps" : { + "output" : "@" + }, + "shiftCapsOption" : { + "output" : "€" + }, + "shiftOption" : { + "output" : "€" + }, + "unmodified" : { + "output" : "2" + } + }, + "2" : { + "caps" : { + "output" : "E" + }, + "capsOption" : { + "output" : "´" + }, + "keyCode" : 2, + "option" : { + "rejection" : "deadKey" + }, + "shift" : { + "output" : "E" + }, + "shiftCaps" : { + "output" : "E" + }, + "shiftCapsOption" : { + "output" : "´" + }, + "shiftOption" : { + "output" : "´" + }, + "unmodified" : { + "output" : "e" + } + }, + "20" : { + "caps" : { + "output" : "3" + }, + "capsOption" : { + "output" : "£" + }, + "keyCode" : 20, + "option" : { + "output" : "£" + }, + "shift" : { + "output" : "#" + }, + "shiftCaps" : { + "output" : "#" + }, + "shiftCapsOption" : { + "output" : "‹" + }, + "shiftOption" : { + "output" : "‹" + }, + "unmodified" : { + "output" : "3" + } + }, + "21" : { + "caps" : { + "output" : "4" + }, + "capsOption" : { + "output" : "¢" + }, + "keyCode" : 21, + "option" : { + "output" : "¢" + }, + "shift" : { + "output" : "$" + }, + "shiftCaps" : { + "output" : "$" + }, + "shiftCapsOption" : { + "output" : "›" + }, + "shiftOption" : { + "output" : "›" + }, + "unmodified" : { + "output" : "4" + } + }, + "22" : { + "caps" : { + "output" : "6" + }, + "capsOption" : { + "output" : "§" + }, + "keyCode" : 22, + "option" : { + "output" : "§" + }, + "shift" : { + "output" : "^" + }, + "shiftCaps" : { + "output" : "^" + }, + "shiftCapsOption" : { + "output" : "fl" + }, + "shiftOption" : { + "output" : "fl" + }, + "unmodified" : { + "output" : "6" + } + }, + "23" : { + "caps" : { + "output" : "5" + }, + "capsOption" : { + "output" : "∞" + }, + "keyCode" : 23, + "option" : { + "output" : "∞" + }, + "shift" : { + "output" : "%" + }, + "shiftCaps" : { + "output" : "%" + }, + "shiftCapsOption" : { + "output" : "fi" + }, + "shiftOption" : { + "output" : "fi" + }, + "unmodified" : { + "output" : "5" + } + }, + "24" : { + "caps" : { + "output" : "]" + }, + "capsOption" : { + "output" : "‘" + }, + "keyCode" : 24, + "option" : { + "output" : "‘" + }, + "shift" : { + "output" : "}" + }, + "shiftCaps" : { + "output" : "}" + }, + "shiftCapsOption" : { + "output" : "’" + }, + "shiftOption" : { + "output" : "’" + }, + "unmodified" : { + "output" : "]" + } + }, + "25" : { + "caps" : { + "output" : "9" + }, + "capsOption" : { + "output" : "ª" + }, + "keyCode" : 25, + "option" : { + "output" : "ª" + }, + "shift" : { + "output" : "(" + }, + "shiftCaps" : { + "output" : "(" + }, + "shiftCapsOption" : { + "output" : "·" + }, + "shiftOption" : { + "output" : "·" + }, + "unmodified" : { + "output" : "9" + } + }, + "26" : { + "caps" : { + "output" : "7" + }, + "capsOption" : { + "output" : "¶" + }, + "keyCode" : 26, + "option" : { + "output" : "¶" + }, + "shift" : { + "output" : "&" + }, + "shiftCaps" : { + "output" : "&" + }, + "shiftCapsOption" : { + "output" : "‡" + }, + "shiftOption" : { + "output" : "‡" + }, + "unmodified" : { + "output" : "7" + } + }, + "27" : { + "caps" : { + "output" : "[" + }, + "capsOption" : { + "output" : "“" + }, + "keyCode" : 27, + "option" : { + "output" : "“" + }, + "shift" : { + "output" : "{" + }, + "shiftCaps" : { + "output" : "{" + }, + "shiftCapsOption" : { + "output" : "”" + }, + "shiftOption" : { + "output" : "”" + }, + "unmodified" : { + "output" : "[" + } + }, + "28" : { + "caps" : { + "output" : "8" + }, + "capsOption" : { + "output" : "•" + }, + "keyCode" : 28, + "option" : { + "output" : "•" + }, + "shift" : { + "output" : "*" + }, + "shiftCaps" : { + "output" : "*" + }, + "shiftCapsOption" : { + "output" : "°" + }, + "shiftOption" : { + "output" : "°" + }, + "unmodified" : { + "output" : "8" + } + }, + "29" : { + "caps" : { + "output" : "0" + }, + "capsOption" : { + "output" : "º" + }, + "keyCode" : 29, + "option" : { + "output" : "º" + }, + "shift" : { + "output" : ")" + }, + "shiftCaps" : { + "output" : ")" + }, + "shiftCapsOption" : { + "output" : "‚" + }, + "shiftOption" : { + "output" : "‚" + }, + "unmodified" : { + "output" : "0" + } + }, + "3" : { + "caps" : { + "output" : "U" + }, + "capsOption" : { + "output" : "¨" + }, + "keyCode" : 3, + "option" : { + "rejection" : "deadKey" + }, + "shift" : { + "output" : "U" + }, + "shiftCaps" : { + "output" : "U" + }, + "shiftCapsOption" : { + "output" : "¨" + }, + "shiftOption" : { + "output" : "¨" + }, + "unmodified" : { + "output" : "u" + } + }, + "30" : { + "caps" : { + "output" : "=" + }, + "capsOption" : { + "output" : "≠" + }, + "keyCode" : 30, + "option" : { + "output" : "≠" + }, + "shift" : { + "output" : "+" + }, + "shiftCaps" : { + "output" : "+" + }, + "shiftCapsOption" : { + "output" : "±" + }, + "shiftOption" : { + "output" : "±" + }, + "unmodified" : { + "output" : "=" + } + }, + "31" : { + "caps" : { + "output" : "R" + }, + "capsOption" : { + "output" : "®" + }, + "keyCode" : 31, + "option" : { + "output" : "®" + }, + "shift" : { + "output" : "R" + }, + "shiftCaps" : { + "output" : "R" + }, + "shiftCapsOption" : { + "output" : "‰" + }, + "shiftOption" : { + "output" : "‰" + }, + "unmodified" : { + "output" : "r" + } + }, + "32" : { + "caps" : { + "output" : "G" + }, + "capsOption" : { + "output" : "©" + }, + "keyCode" : 32, + "option" : { + "output" : "©" + }, + "shift" : { + "output" : "G" + }, + "shiftCaps" : { + "output" : "G" + }, + "shiftCapsOption" : { + "output" : "˝" + }, + "shiftOption" : { + "output" : "˝" + }, + "unmodified" : { + "output" : "g" + } + }, + "33" : { + "caps" : { + "output" : "\/" + }, + "capsOption" : { + "output" : "÷" + }, + "keyCode" : 33, + "option" : { + "output" : "÷" + }, + "shift" : { + "output" : "?" + }, + "shiftCaps" : { + "output" : "?" + }, + "shiftCapsOption" : { + "output" : "¿" + }, + "shiftOption" : { + "output" : "¿" + }, + "unmodified" : { + "output" : "\/" + } + }, + "34" : { + "caps" : { + "output" : "C" + }, + "capsOption" : { + "output" : "Ç" + }, + "keyCode" : 34, + "option" : { + "output" : "ç" + }, + "shift" : { + "output" : "C" + }, + "shiftCaps" : { + "output" : "C" + }, + "shiftCapsOption" : { + "output" : "Ç" + }, + "shiftOption" : { + "output" : "Ç" + }, + "unmodified" : { + "output" : "c" + } + }, + "35" : { + "caps" : { + "output" : "L" + }, + "capsOption" : { + "output" : "Ò" + }, + "keyCode" : 35, + "option" : { + "output" : "¬" + }, + "shift" : { + "output" : "L" + }, + "shiftCaps" : { + "output" : "L" + }, + "shiftCapsOption" : { + "output" : "Ò" + }, + "shiftOption" : { + "output" : "Ò" + }, + "unmodified" : { + "output" : "l" + } + }, + "36" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 36, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "37" : { + "caps" : { + "output" : "N" + }, + "capsOption" : { + "output" : "˜" + }, + "keyCode" : 37, + "option" : { + "rejection" : "deadKey" + }, + "shift" : { + "output" : "N" + }, + "shiftCaps" : { + "output" : "N" + }, + "shiftCapsOption" : { + "output" : "˜" + }, + "shiftOption" : { + "output" : "˜" + }, + "unmodified" : { + "output" : "n" + } + }, + "38" : { + "caps" : { + "output" : "H" + }, + "capsOption" : { + "output" : "Ó" + }, + "keyCode" : 38, + "option" : { + "output" : "˙" + }, + "shift" : { + "output" : "H" + }, + "shiftCaps" : { + "output" : "H" + }, + "shiftCapsOption" : { + "output" : "Ó" + }, + "shiftOption" : { + "output" : "Ó" + }, + "unmodified" : { + "output" : "h" + } + }, + "39" : { + "caps" : { + "output" : "-" + }, + "capsOption" : { + "output" : "–" + }, + "keyCode" : 39, + "option" : { + "output" : "–" + }, + "shift" : { + "output" : "_" + }, + "shiftCaps" : { + "output" : "_" + }, + "shiftCapsOption" : { + "output" : "—" + }, + "shiftOption" : { + "output" : "—" + }, + "unmodified" : { + "output" : "-" + } + }, + "4" : { + "caps" : { + "output" : "D" + }, + "capsOption" : { + "output" : "Î" + }, + "keyCode" : 4, + "option" : { + "output" : "∂" + }, + "shift" : { + "output" : "D" + }, + "shiftCaps" : { + "output" : "D" + }, + "shiftCapsOption" : { + "output" : "Î" + }, + "shiftOption" : { + "output" : "Î" + }, + "unmodified" : { + "output" : "d" + } + }, + "40" : { + "caps" : { + "output" : "T" + }, + "capsOption" : { + "output" : "†" + }, + "keyCode" : 40, + "option" : { + "output" : "†" + }, + "shift" : { + "output" : "T" + }, + "shiftCaps" : { + "output" : "T" + }, + "shiftCapsOption" : { + "output" : "ˇ" + }, + "shiftOption" : { + "output" : "ˇ" + }, + "unmodified" : { + "output" : "t" + } + }, + "41" : { + "caps" : { + "output" : "S" + }, + "capsOption" : { + "output" : "Í" + }, + "keyCode" : 41, + "option" : { + "output" : "ß" + }, + "shift" : { + "output" : "S" + }, + "shiftCaps" : { + "output" : "S" + }, + "shiftCapsOption" : { + "output" : "Í" + }, + "shiftOption" : { + "output" : "Í" + }, + "unmodified" : { + "output" : "s" + } + }, + "42" : { + "caps" : { + "output" : "\\" + }, + "capsOption" : { + "output" : "«" + }, + "keyCode" : 42, + "option" : { + "output" : "«" + }, + "shift" : { + "output" : "|" + }, + "shiftCaps" : { + "output" : "|" + }, + "shiftCapsOption" : { + "output" : "»" + }, + "shiftOption" : { + "output" : "»" + }, + "unmodified" : { + "output" : "\\" + } + }, + "43" : { + "caps" : { + "output" : "W" + }, + "capsOption" : { + "output" : "∑" + }, + "keyCode" : 43, + "option" : { + "output" : "∑" + }, + "shift" : { + "output" : "W" + }, + "shiftCaps" : { + "output" : "W" + }, + "shiftCapsOption" : { + "output" : "„" + }, + "shiftOption" : { + "output" : "„" + }, + "unmodified" : { + "output" : "w" + } + }, + "44" : { + "caps" : { + "output" : "Z" + }, + "capsOption" : { + "output" : "Ω" + }, + "keyCode" : 44, + "option" : { + "output" : "Ω" + }, + "shift" : { + "output" : "Z" + }, + "shiftCaps" : { + "output" : "Z" + }, + "shiftCapsOption" : { + "output" : "¸" + }, + "shiftOption" : { + "output" : "¸" + }, + "unmodified" : { + "output" : "z" + } + }, + "45" : { + "caps" : { + "output" : "B" + }, + "capsOption" : { + "output" : "ı" + }, + "keyCode" : 45, + "option" : { + "output" : "∫" + }, + "shift" : { + "output" : "B" + }, + "shiftCaps" : { + "output" : "B" + }, + "shiftCapsOption" : { + "output" : "ı" + }, + "shiftOption" : { + "output" : "ı" + }, + "unmodified" : { + "output" : "b" + } + }, + "46" : { + "caps" : { + "output" : "M" + }, + "capsOption" : { + "output" : "Â" + }, + "keyCode" : 46, + "option" : { + "output" : "µ" + }, + "shift" : { + "output" : "M" + }, + "shiftCaps" : { + "output" : "M" + }, + "shiftCapsOption" : { + "output" : "Â" + }, + "shiftOption" : { + "output" : "Â" + }, + "unmodified" : { + "output" : "m" + } + }, + "47" : { + "caps" : { + "output" : "V" + }, + "capsOption" : { + "output" : "√" + }, + "keyCode" : 47, + "option" : { + "output" : "√" + }, + "shift" : { + "output" : "V" + }, + "shiftCaps" : { + "output" : "V" + }, + "shiftCapsOption" : { + "output" : "◊" + }, + "shiftOption" : { + "output" : "◊" + }, + "unmodified" : { + "output" : "v" + } + }, + "48" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 48, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "49" : { + "caps" : { + "output" : " " + }, + "capsOption" : { + "output" : " " + }, + "keyCode" : 49, + "option" : { + "output" : " " + }, + "shift" : { + "output" : " " + }, + "shiftCaps" : { + "output" : " " + }, + "shiftCapsOption" : { + "output" : " " + }, + "shiftOption" : { + "output" : " " + }, + "unmodified" : { + "output" : " " + } + }, + "5" : { + "caps" : { + "output" : "I" + }, + "capsOption" : { + "output" : "ˆ" + }, + "keyCode" : 5, + "option" : { + "rejection" : "deadKey" + }, + "shift" : { + "output" : "I" + }, + "shiftCaps" : { + "output" : "I" + }, + "shiftCapsOption" : { + "output" : "ˆ" + }, + "shiftOption" : { + "output" : "ˆ" + }, + "unmodified" : { + "output" : "i" + } + }, + "50" : { + "caps" : { + "output" : "`" + }, + "capsOption" : { + "output" : "`" + }, + "keyCode" : 50, + "option" : { + "rejection" : "deadKey" + }, + "shift" : { + "output" : "~" + }, + "shiftCaps" : { + "output" : "~" + }, + "shiftCapsOption" : { + "output" : "`" + }, + "shiftOption" : { + "output" : "`" + }, + "unmodified" : { + "output" : "`" + } + }, + "51" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 51, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "52" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 52, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "53" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 53, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "54" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 54, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "55" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 55, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "56" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 56, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "57" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 57, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "58" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 58, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "59" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 59, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "6" : { + "caps" : { + "output" : ";" + }, + "capsOption" : { + "output" : "…" + }, + "keyCode" : 6, + "option" : { + "output" : "…" + }, + "shift" : { + "output" : ":" + }, + "shiftCaps" : { + "output" : ":" + }, + "shiftCapsOption" : { + "output" : "Ú" + }, + "shiftOption" : { + "output" : "Ú" + }, + "unmodified" : { + "output" : ";" + } + }, + "60" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 60, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "61" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 61, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "62" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 62, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "63" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 63, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "64" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 64, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "65" : { + "caps" : { + "output" : "." + }, + "capsOption" : { + "output" : "." + }, + "keyCode" : 65, + "option" : { + "output" : "." + }, + "shift" : { + "output" : "." + }, + "shiftCaps" : { + "output" : "." + }, + "shiftCapsOption" : { + "output" : "." + }, + "shiftOption" : { + "output" : "." + }, + "unmodified" : { + "output" : "." + } + }, + "66" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 66, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "*" + }, + "shiftCaps" : { + "output" : "*" + }, + "shiftCapsOption" : { + "output" : "*" + }, + "shiftOption" : { + "output" : "*" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "67" : { + "caps" : { + "output" : "*" + }, + "capsOption" : { + "output" : "*" + }, + "keyCode" : 67, + "option" : { + "output" : "*" + }, + "shift" : { + "output" : "*" + }, + "shiftCaps" : { + "output" : "*" + }, + "shiftCapsOption" : { + "output" : "*" + }, + "shiftOption" : { + "output" : "*" + }, + "unmodified" : { + "output" : "*" + } + }, + "68" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 68, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "69" : { + "caps" : { + "output" : "+" + }, + "capsOption" : { + "output" : "+" + }, + "keyCode" : 69, + "option" : { + "output" : "+" + }, + "shift" : { + "output" : "+" + }, + "shiftCaps" : { + "output" : "+" + }, + "shiftCapsOption" : { + "output" : "+" + }, + "shiftOption" : { + "output" : "+" + }, + "unmodified" : { + "output" : "+" + } + }, + "7" : { + "caps" : { + "output" : "Q" + }, + "capsOption" : { + "output" : "Œ" + }, + "keyCode" : 7, + "option" : { + "output" : "œ" + }, + "shift" : { + "output" : "Q" + }, + "shiftCaps" : { + "output" : "Q" + }, + "shiftCapsOption" : { + "output" : "Œ" + }, + "shiftOption" : { + "output" : "Œ" + }, + "unmodified" : { + "output" : "q" + } + }, + "70" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 70, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "+" + }, + "shiftCaps" : { + "output" : "+" + }, + "shiftCapsOption" : { + "output" : "+" + }, + "shiftOption" : { + "output" : "+" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "71" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 71, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "72" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 72, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "=" + }, + "shiftCaps" : { + "output" : "=" + }, + "shiftCapsOption" : { + "output" : "=" + }, + "shiftOption" : { + "output" : "=" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "73" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 73, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "74" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 74, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "75" : { + "caps" : { + "output" : "\/" + }, + "capsOption" : { + "output" : "\/" + }, + "keyCode" : 75, + "option" : { + "output" : "\/" + }, + "shift" : { + "output" : "\/" + }, + "shiftCaps" : { + "output" : "\/" + }, + "shiftCapsOption" : { + "output" : "\/" + }, + "shiftOption" : { + "output" : "\/" + }, + "unmodified" : { + "output" : "\/" + } + }, + "76" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 76, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "77" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 77, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "\/" + }, + "shiftCaps" : { + "output" : "\/" + }, + "shiftCapsOption" : { + "output" : "\/" + }, + "shiftOption" : { + "output" : "\/" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "78" : { + "caps" : { + "output" : "-" + }, + "capsOption" : { + "output" : "-" + }, + "keyCode" : 78, + "option" : { + "output" : "-" + }, + "shift" : { + "output" : "-" + }, + "shiftCaps" : { + "output" : "-" + }, + "shiftCapsOption" : { + "output" : "-" + }, + "shiftOption" : { + "output" : "-" + }, + "unmodified" : { + "output" : "-" + } + }, + "79" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 79, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "8" : { + "caps" : { + "output" : "J" + }, + "capsOption" : { + "output" : "Ô" + }, + "keyCode" : 8, + "option" : { + "output" : "∆" + }, + "shift" : { + "output" : "J" + }, + "shiftCaps" : { + "output" : "J" + }, + "shiftCapsOption" : { + "output" : "Ô" + }, + "shiftOption" : { + "output" : "Ô" + }, + "unmodified" : { + "output" : "j" + } + }, + "80" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 80, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "81" : { + "caps" : { + "output" : "=" + }, + "capsOption" : { + "output" : "=" + }, + "keyCode" : 81, + "option" : { + "output" : "=" + }, + "shift" : { + "output" : "=" + }, + "shiftCaps" : { + "output" : "=" + }, + "shiftCapsOption" : { + "output" : "=" + }, + "shiftOption" : { + "output" : "=" + }, + "unmodified" : { + "output" : "=" + } + }, + "82" : { + "caps" : { + "output" : "0" + }, + "capsOption" : { + "output" : "0" + }, + "keyCode" : 82, + "option" : { + "output" : "0" + }, + "shift" : { + "output" : "0" + }, + "shiftCaps" : { + "output" : "0" + }, + "shiftCapsOption" : { + "output" : "0" + }, + "shiftOption" : { + "output" : "0" + }, + "unmodified" : { + "output" : "0" + } + }, + "83" : { + "caps" : { + "output" : "1" + }, + "capsOption" : { + "output" : "1" + }, + "keyCode" : 83, + "option" : { + "output" : "1" + }, + "shift" : { + "output" : "1" + }, + "shiftCaps" : { + "output" : "1" + }, + "shiftCapsOption" : { + "output" : "1" + }, + "shiftOption" : { + "output" : "1" + }, + "unmodified" : { + "output" : "1" + } + }, + "84" : { + "caps" : { + "output" : "2" + }, + "capsOption" : { + "output" : "2" + }, + "keyCode" : 84, + "option" : { + "output" : "2" + }, + "shift" : { + "output" : "2" + }, + "shiftCaps" : { + "output" : "2" + }, + "shiftCapsOption" : { + "output" : "2" + }, + "shiftOption" : { + "output" : "2" + }, + "unmodified" : { + "output" : "2" + } + }, + "85" : { + "caps" : { + "output" : "3" + }, + "capsOption" : { + "output" : "3" + }, + "keyCode" : 85, + "option" : { + "output" : "3" + }, + "shift" : { + "output" : "3" + }, + "shiftCaps" : { + "output" : "3" + }, + "shiftCapsOption" : { + "output" : "3" + }, + "shiftOption" : { + "output" : "3" + }, + "unmodified" : { + "output" : "3" + } + }, + "86" : { + "caps" : { + "output" : "4" + }, + "capsOption" : { + "output" : "4" + }, + "keyCode" : 86, + "option" : { + "output" : "4" + }, + "shift" : { + "output" : "4" + }, + "shiftCaps" : { + "output" : "4" + }, + "shiftCapsOption" : { + "output" : "4" + }, + "shiftOption" : { + "output" : "4" + }, + "unmodified" : { + "output" : "4" + } + }, + "87" : { + "caps" : { + "output" : "5" + }, + "capsOption" : { + "output" : "5" + }, + "keyCode" : 87, + "option" : { + "output" : "5" + }, + "shift" : { + "output" : "5" + }, + "shiftCaps" : { + "output" : "5" + }, + "shiftCapsOption" : { + "output" : "5" + }, + "shiftOption" : { + "output" : "5" + }, + "unmodified" : { + "output" : "5" + } + }, + "88" : { + "caps" : { + "output" : "6" + }, + "capsOption" : { + "output" : "6" + }, + "keyCode" : 88, + "option" : { + "output" : "6" + }, + "shift" : { + "output" : "6" + }, + "shiftCaps" : { + "output" : "6" + }, + "shiftCapsOption" : { + "output" : "6" + }, + "shiftOption" : { + "output" : "6" + }, + "unmodified" : { + "output" : "6" + } + }, + "89" : { + "caps" : { + "output" : "7" + }, + "capsOption" : { + "output" : "7" + }, + "keyCode" : 89, + "option" : { + "output" : "7" + }, + "shift" : { + "output" : "7" + }, + "shiftCaps" : { + "output" : "7" + }, + "shiftCapsOption" : { + "output" : "7" + }, + "shiftOption" : { + "output" : "7" + }, + "unmodified" : { + "output" : "7" + } + }, + "9" : { + "caps" : { + "output" : "K" + }, + "capsOption" : { + "output" : "˚" + }, + "keyCode" : 9, + "option" : { + "output" : "˚" + }, + "shift" : { + "output" : "K" + }, + "shiftCaps" : { + "output" : "K" + }, + "shiftCapsOption" : { + "rejection" : "privateUseOrSurrogate" + }, + "shiftOption" : { + "rejection" : "privateUseOrSurrogate" + }, + "unmodified" : { + "output" : "k" + } + }, + "90" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 90, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "91" : { + "caps" : { + "output" : "8" + }, + "capsOption" : { + "output" : "8" + }, + "keyCode" : 91, + "option" : { + "output" : "8" + }, + "shift" : { + "output" : "8" + }, + "shiftCaps" : { + "output" : "8" + }, + "shiftCapsOption" : { + "output" : "8" + }, + "shiftOption" : { + "output" : "8" + }, + "unmodified" : { + "output" : "8" + } + }, + "92" : { + "caps" : { + "output" : "9" + }, + "capsOption" : { + "output" : "9" + }, + "keyCode" : 92, + "option" : { + "output" : "9" + }, + "shift" : { + "output" : "9" + }, + "shiftCaps" : { + "output" : "9" + }, + "shiftCapsOption" : { + "output" : "9" + }, + "shiftOption" : { + "output" : "9" + }, + "unmodified" : { + "output" : "9" + } + }, + "93" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 93, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "94" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 94, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "95" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 95, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "96" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 96, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "97" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 97, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "98" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 98, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "99" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 99, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + } + }, + "keyboardType" : 37, + "osBuild" : "24G830", + "osVersion" : "15.7.9", + "schemaVersion" : 1, + "sourceID" : "com.apple.keylayout.Dvorak" +} \ No newline at end of file diff --git a/Benchmarks/layout-tables/v1/com.apple.keylayout.Dvorak@91.json b/Benchmarks/layout-tables/v1/com.apple.keylayout.Dvorak@91.json new file mode 100644 index 0000000..1ce6199 --- /dev/null +++ b/Benchmarks/layout-tables/v1/com.apple.keylayout.Dvorak@91.json @@ -0,0 +1,3465 @@ +{ + "keyOutputs" : { + "0" : { + "caps" : { + "output" : "A" + }, + "capsOption" : { + "output" : "Å" + }, + "keyCode" : 0, + "option" : { + "output" : "å" + }, + "shift" : { + "output" : "A" + }, + "shiftCaps" : { + "output" : "A" + }, + "shiftCapsOption" : { + "output" : "Å" + }, + "shiftOption" : { + "output" : "Å" + }, + "unmodified" : { + "output" : "a" + } + }, + "1" : { + "caps" : { + "output" : "O" + }, + "capsOption" : { + "output" : "Ø" + }, + "keyCode" : 1, + "option" : { + "output" : "ø" + }, + "shift" : { + "output" : "O" + }, + "shiftCaps" : { + "output" : "O" + }, + "shiftCapsOption" : { + "output" : "Ø" + }, + "shiftOption" : { + "output" : "Ø" + }, + "unmodified" : { + "output" : "o" + } + }, + "10" : { + "caps" : { + "output" : "§" + }, + "capsOption" : { + "output" : "§" + }, + "keyCode" : 10, + "option" : { + "output" : "§" + }, + "shift" : { + "output" : "±" + }, + "shiftCaps" : { + "output" : "±" + }, + "shiftCapsOption" : { + "output" : "±" + }, + "shiftOption" : { + "output" : "±" + }, + "unmodified" : { + "output" : "§" + } + }, + "100" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 100, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "101" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 101, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "102" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 102, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "103" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 103, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "104" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 104, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "105" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 105, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "106" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 106, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "107" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 107, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "108" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 108, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "109" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 109, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "11" : { + "caps" : { + "output" : "X" + }, + "capsOption" : { + "output" : "≈" + }, + "keyCode" : 11, + "option" : { + "output" : "≈" + }, + "shift" : { + "output" : "X" + }, + "shiftCaps" : { + "output" : "X" + }, + "shiftCapsOption" : { + "output" : "˛" + }, + "shiftOption" : { + "output" : "˛" + }, + "unmodified" : { + "output" : "x" + } + }, + "110" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 110, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "111" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 111, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "112" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 112, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "113" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 113, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "114" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 114, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "115" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 115, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "116" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 116, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "117" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 117, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "118" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 118, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "119" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 119, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "12" : { + "caps" : { + "output" : "'" + }, + "capsOption" : { + "output" : "Æ" + }, + "keyCode" : 12, + "option" : { + "output" : "æ" + }, + "shift" : { + "output" : "\"" + }, + "shiftCaps" : { + "output" : "\"" + }, + "shiftCapsOption" : { + "output" : "Æ" + }, + "shiftOption" : { + "output" : "Æ" + }, + "unmodified" : { + "output" : "'" + } + }, + "120" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 120, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "121" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 121, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "122" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 122, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "123" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 123, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "124" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 124, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "125" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 125, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "126" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 126, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "127" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 127, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "13" : { + "caps" : { + "output" : "," + }, + "capsOption" : { + "output" : "≤" + }, + "keyCode" : 13, + "option" : { + "output" : "≤" + }, + "shift" : { + "output" : "<" + }, + "shiftCaps" : { + "output" : "<" + }, + "shiftCapsOption" : { + "output" : "¯" + }, + "shiftOption" : { + "output" : "¯" + }, + "unmodified" : { + "output" : "," + } + }, + "14" : { + "caps" : { + "output" : "." + }, + "capsOption" : { + "output" : "≥" + }, + "keyCode" : 14, + "option" : { + "output" : "≥" + }, + "shift" : { + "output" : ">" + }, + "shiftCaps" : { + "output" : ">" + }, + "shiftCapsOption" : { + "output" : "˘" + }, + "shiftOption" : { + "output" : "˘" + }, + "unmodified" : { + "output" : "." + } + }, + "15" : { + "caps" : { + "output" : "P" + }, + "capsOption" : { + "output" : "∏" + }, + "keyCode" : 15, + "option" : { + "output" : "π" + }, + "shift" : { + "output" : "P" + }, + "shiftCaps" : { + "output" : "P" + }, + "shiftCapsOption" : { + "output" : "∏" + }, + "shiftOption" : { + "output" : "∏" + }, + "unmodified" : { + "output" : "p" + } + }, + "16" : { + "caps" : { + "output" : "F" + }, + "capsOption" : { + "output" : "Ï" + }, + "keyCode" : 16, + "option" : { + "output" : "ƒ" + }, + "shift" : { + "output" : "F" + }, + "shiftCaps" : { + "output" : "F" + }, + "shiftCapsOption" : { + "output" : "Ï" + }, + "shiftOption" : { + "output" : "Ï" + }, + "unmodified" : { + "output" : "f" + } + }, + "17" : { + "caps" : { + "output" : "Y" + }, + "capsOption" : { + "output" : "Á" + }, + "keyCode" : 17, + "option" : { + "output" : "¥" + }, + "shift" : { + "output" : "Y" + }, + "shiftCaps" : { + "output" : "Y" + }, + "shiftCapsOption" : { + "output" : "Á" + }, + "shiftOption" : { + "output" : "Á" + }, + "unmodified" : { + "output" : "y" + } + }, + "18" : { + "caps" : { + "output" : "1" + }, + "capsOption" : { + "output" : "¡" + }, + "keyCode" : 18, + "option" : { + "output" : "¡" + }, + "shift" : { + "output" : "!" + }, + "shiftCaps" : { + "output" : "!" + }, + "shiftCapsOption" : { + "output" : "⁄" + }, + "shiftOption" : { + "output" : "⁄" + }, + "unmodified" : { + "output" : "1" + } + }, + "19" : { + "caps" : { + "output" : "2" + }, + "capsOption" : { + "output" : "™" + }, + "keyCode" : 19, + "option" : { + "output" : "™" + }, + "shift" : { + "output" : "@" + }, + "shiftCaps" : { + "output" : "@" + }, + "shiftCapsOption" : { + "output" : "€" + }, + "shiftOption" : { + "output" : "€" + }, + "unmodified" : { + "output" : "2" + } + }, + "2" : { + "caps" : { + "output" : "E" + }, + "capsOption" : { + "output" : "´" + }, + "keyCode" : 2, + "option" : { + "rejection" : "deadKey" + }, + "shift" : { + "output" : "E" + }, + "shiftCaps" : { + "output" : "E" + }, + "shiftCapsOption" : { + "output" : "´" + }, + "shiftOption" : { + "output" : "´" + }, + "unmodified" : { + "output" : "e" + } + }, + "20" : { + "caps" : { + "output" : "3" + }, + "capsOption" : { + "output" : "£" + }, + "keyCode" : 20, + "option" : { + "output" : "£" + }, + "shift" : { + "output" : "#" + }, + "shiftCaps" : { + "output" : "#" + }, + "shiftCapsOption" : { + "output" : "‹" + }, + "shiftOption" : { + "output" : "‹" + }, + "unmodified" : { + "output" : "3" + } + }, + "21" : { + "caps" : { + "output" : "4" + }, + "capsOption" : { + "output" : "¢" + }, + "keyCode" : 21, + "option" : { + "output" : "¢" + }, + "shift" : { + "output" : "$" + }, + "shiftCaps" : { + "output" : "$" + }, + "shiftCapsOption" : { + "output" : "›" + }, + "shiftOption" : { + "output" : "›" + }, + "unmodified" : { + "output" : "4" + } + }, + "22" : { + "caps" : { + "output" : "6" + }, + "capsOption" : { + "output" : "§" + }, + "keyCode" : 22, + "option" : { + "output" : "§" + }, + "shift" : { + "output" : "^" + }, + "shiftCaps" : { + "output" : "^" + }, + "shiftCapsOption" : { + "output" : "fl" + }, + "shiftOption" : { + "output" : "fl" + }, + "unmodified" : { + "output" : "6" + } + }, + "23" : { + "caps" : { + "output" : "5" + }, + "capsOption" : { + "output" : "∞" + }, + "keyCode" : 23, + "option" : { + "output" : "∞" + }, + "shift" : { + "output" : "%" + }, + "shiftCaps" : { + "output" : "%" + }, + "shiftCapsOption" : { + "output" : "fi" + }, + "shiftOption" : { + "output" : "fi" + }, + "unmodified" : { + "output" : "5" + } + }, + "24" : { + "caps" : { + "output" : "]" + }, + "capsOption" : { + "output" : "‘" + }, + "keyCode" : 24, + "option" : { + "output" : "‘" + }, + "shift" : { + "output" : "}" + }, + "shiftCaps" : { + "output" : "}" + }, + "shiftCapsOption" : { + "output" : "’" + }, + "shiftOption" : { + "output" : "’" + }, + "unmodified" : { + "output" : "]" + } + }, + "25" : { + "caps" : { + "output" : "9" + }, + "capsOption" : { + "output" : "ª" + }, + "keyCode" : 25, + "option" : { + "output" : "ª" + }, + "shift" : { + "output" : "(" + }, + "shiftCaps" : { + "output" : "(" + }, + "shiftCapsOption" : { + "output" : "·" + }, + "shiftOption" : { + "output" : "·" + }, + "unmodified" : { + "output" : "9" + } + }, + "26" : { + "caps" : { + "output" : "7" + }, + "capsOption" : { + "output" : "¶" + }, + "keyCode" : 26, + "option" : { + "output" : "¶" + }, + "shift" : { + "output" : "&" + }, + "shiftCaps" : { + "output" : "&" + }, + "shiftCapsOption" : { + "output" : "‡" + }, + "shiftOption" : { + "output" : "‡" + }, + "unmodified" : { + "output" : "7" + } + }, + "27" : { + "caps" : { + "output" : "[" + }, + "capsOption" : { + "output" : "“" + }, + "keyCode" : 27, + "option" : { + "output" : "“" + }, + "shift" : { + "output" : "{" + }, + "shiftCaps" : { + "output" : "{" + }, + "shiftCapsOption" : { + "output" : "”" + }, + "shiftOption" : { + "output" : "”" + }, + "unmodified" : { + "output" : "[" + } + }, + "28" : { + "caps" : { + "output" : "8" + }, + "capsOption" : { + "output" : "•" + }, + "keyCode" : 28, + "option" : { + "output" : "•" + }, + "shift" : { + "output" : "*" + }, + "shiftCaps" : { + "output" : "*" + }, + "shiftCapsOption" : { + "output" : "°" + }, + "shiftOption" : { + "output" : "°" + }, + "unmodified" : { + "output" : "8" + } + }, + "29" : { + "caps" : { + "output" : "0" + }, + "capsOption" : { + "output" : "º" + }, + "keyCode" : 29, + "option" : { + "output" : "º" + }, + "shift" : { + "output" : ")" + }, + "shiftCaps" : { + "output" : ")" + }, + "shiftCapsOption" : { + "output" : "‚" + }, + "shiftOption" : { + "output" : "‚" + }, + "unmodified" : { + "output" : "0" + } + }, + "3" : { + "caps" : { + "output" : "U" + }, + "capsOption" : { + "output" : "¨" + }, + "keyCode" : 3, + "option" : { + "rejection" : "deadKey" + }, + "shift" : { + "output" : "U" + }, + "shiftCaps" : { + "output" : "U" + }, + "shiftCapsOption" : { + "output" : "¨" + }, + "shiftOption" : { + "output" : "¨" + }, + "unmodified" : { + "output" : "u" + } + }, + "30" : { + "caps" : { + "output" : "=" + }, + "capsOption" : { + "output" : "≠" + }, + "keyCode" : 30, + "option" : { + "output" : "≠" + }, + "shift" : { + "output" : "+" + }, + "shiftCaps" : { + "output" : "+" + }, + "shiftCapsOption" : { + "output" : "±" + }, + "shiftOption" : { + "output" : "±" + }, + "unmodified" : { + "output" : "=" + } + }, + "31" : { + "caps" : { + "output" : "R" + }, + "capsOption" : { + "output" : "®" + }, + "keyCode" : 31, + "option" : { + "output" : "®" + }, + "shift" : { + "output" : "R" + }, + "shiftCaps" : { + "output" : "R" + }, + "shiftCapsOption" : { + "output" : "‰" + }, + "shiftOption" : { + "output" : "‰" + }, + "unmodified" : { + "output" : "r" + } + }, + "32" : { + "caps" : { + "output" : "G" + }, + "capsOption" : { + "output" : "©" + }, + "keyCode" : 32, + "option" : { + "output" : "©" + }, + "shift" : { + "output" : "G" + }, + "shiftCaps" : { + "output" : "G" + }, + "shiftCapsOption" : { + "output" : "˝" + }, + "shiftOption" : { + "output" : "˝" + }, + "unmodified" : { + "output" : "g" + } + }, + "33" : { + "caps" : { + "output" : "\/" + }, + "capsOption" : { + "output" : "÷" + }, + "keyCode" : 33, + "option" : { + "output" : "÷" + }, + "shift" : { + "output" : "?" + }, + "shiftCaps" : { + "output" : "?" + }, + "shiftCapsOption" : { + "output" : "¿" + }, + "shiftOption" : { + "output" : "¿" + }, + "unmodified" : { + "output" : "\/" + } + }, + "34" : { + "caps" : { + "output" : "C" + }, + "capsOption" : { + "output" : "Ç" + }, + "keyCode" : 34, + "option" : { + "output" : "ç" + }, + "shift" : { + "output" : "C" + }, + "shiftCaps" : { + "output" : "C" + }, + "shiftCapsOption" : { + "output" : "Ç" + }, + "shiftOption" : { + "output" : "Ç" + }, + "unmodified" : { + "output" : "c" + } + }, + "35" : { + "caps" : { + "output" : "L" + }, + "capsOption" : { + "output" : "Ò" + }, + "keyCode" : 35, + "option" : { + "output" : "¬" + }, + "shift" : { + "output" : "L" + }, + "shiftCaps" : { + "output" : "L" + }, + "shiftCapsOption" : { + "output" : "Ò" + }, + "shiftOption" : { + "output" : "Ò" + }, + "unmodified" : { + "output" : "l" + } + }, + "36" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 36, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "37" : { + "caps" : { + "output" : "N" + }, + "capsOption" : { + "output" : "˜" + }, + "keyCode" : 37, + "option" : { + "rejection" : "deadKey" + }, + "shift" : { + "output" : "N" + }, + "shiftCaps" : { + "output" : "N" + }, + "shiftCapsOption" : { + "output" : "˜" + }, + "shiftOption" : { + "output" : "˜" + }, + "unmodified" : { + "output" : "n" + } + }, + "38" : { + "caps" : { + "output" : "H" + }, + "capsOption" : { + "output" : "Ó" + }, + "keyCode" : 38, + "option" : { + "output" : "˙" + }, + "shift" : { + "output" : "H" + }, + "shiftCaps" : { + "output" : "H" + }, + "shiftCapsOption" : { + "output" : "Ó" + }, + "shiftOption" : { + "output" : "Ó" + }, + "unmodified" : { + "output" : "h" + } + }, + "39" : { + "caps" : { + "output" : "-" + }, + "capsOption" : { + "output" : "–" + }, + "keyCode" : 39, + "option" : { + "output" : "–" + }, + "shift" : { + "output" : "_" + }, + "shiftCaps" : { + "output" : "_" + }, + "shiftCapsOption" : { + "output" : "—" + }, + "shiftOption" : { + "output" : "—" + }, + "unmodified" : { + "output" : "-" + } + }, + "4" : { + "caps" : { + "output" : "D" + }, + "capsOption" : { + "output" : "Î" + }, + "keyCode" : 4, + "option" : { + "output" : "∂" + }, + "shift" : { + "output" : "D" + }, + "shiftCaps" : { + "output" : "D" + }, + "shiftCapsOption" : { + "output" : "Î" + }, + "shiftOption" : { + "output" : "Î" + }, + "unmodified" : { + "output" : "d" + } + }, + "40" : { + "caps" : { + "output" : "T" + }, + "capsOption" : { + "output" : "†" + }, + "keyCode" : 40, + "option" : { + "output" : "†" + }, + "shift" : { + "output" : "T" + }, + "shiftCaps" : { + "output" : "T" + }, + "shiftCapsOption" : { + "output" : "ˇ" + }, + "shiftOption" : { + "output" : "ˇ" + }, + "unmodified" : { + "output" : "t" + } + }, + "41" : { + "caps" : { + "output" : "S" + }, + "capsOption" : { + "output" : "Í" + }, + "keyCode" : 41, + "option" : { + "output" : "ß" + }, + "shift" : { + "output" : "S" + }, + "shiftCaps" : { + "output" : "S" + }, + "shiftCapsOption" : { + "output" : "Í" + }, + "shiftOption" : { + "output" : "Í" + }, + "unmodified" : { + "output" : "s" + } + }, + "42" : { + "caps" : { + "output" : "\\" + }, + "capsOption" : { + "output" : "«" + }, + "keyCode" : 42, + "option" : { + "output" : "«" + }, + "shift" : { + "output" : "|" + }, + "shiftCaps" : { + "output" : "|" + }, + "shiftCapsOption" : { + "output" : "»" + }, + "shiftOption" : { + "output" : "»" + }, + "unmodified" : { + "output" : "\\" + } + }, + "43" : { + "caps" : { + "output" : "W" + }, + "capsOption" : { + "output" : "∑" + }, + "keyCode" : 43, + "option" : { + "output" : "∑" + }, + "shift" : { + "output" : "W" + }, + "shiftCaps" : { + "output" : "W" + }, + "shiftCapsOption" : { + "output" : "„" + }, + "shiftOption" : { + "output" : "„" + }, + "unmodified" : { + "output" : "w" + } + }, + "44" : { + "caps" : { + "output" : "Z" + }, + "capsOption" : { + "output" : "Ω" + }, + "keyCode" : 44, + "option" : { + "output" : "Ω" + }, + "shift" : { + "output" : "Z" + }, + "shiftCaps" : { + "output" : "Z" + }, + "shiftCapsOption" : { + "output" : "¸" + }, + "shiftOption" : { + "output" : "¸" + }, + "unmodified" : { + "output" : "z" + } + }, + "45" : { + "caps" : { + "output" : "B" + }, + "capsOption" : { + "output" : "ı" + }, + "keyCode" : 45, + "option" : { + "output" : "∫" + }, + "shift" : { + "output" : "B" + }, + "shiftCaps" : { + "output" : "B" + }, + "shiftCapsOption" : { + "output" : "ı" + }, + "shiftOption" : { + "output" : "ı" + }, + "unmodified" : { + "output" : "b" + } + }, + "46" : { + "caps" : { + "output" : "M" + }, + "capsOption" : { + "output" : "Â" + }, + "keyCode" : 46, + "option" : { + "output" : "µ" + }, + "shift" : { + "output" : "M" + }, + "shiftCaps" : { + "output" : "M" + }, + "shiftCapsOption" : { + "output" : "Â" + }, + "shiftOption" : { + "output" : "Â" + }, + "unmodified" : { + "output" : "m" + } + }, + "47" : { + "caps" : { + "output" : "V" + }, + "capsOption" : { + "output" : "√" + }, + "keyCode" : 47, + "option" : { + "output" : "√" + }, + "shift" : { + "output" : "V" + }, + "shiftCaps" : { + "output" : "V" + }, + "shiftCapsOption" : { + "output" : "◊" + }, + "shiftOption" : { + "output" : "◊" + }, + "unmodified" : { + "output" : "v" + } + }, + "48" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 48, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "49" : { + "caps" : { + "output" : " " + }, + "capsOption" : { + "output" : " " + }, + "keyCode" : 49, + "option" : { + "output" : " " + }, + "shift" : { + "output" : " " + }, + "shiftCaps" : { + "output" : " " + }, + "shiftCapsOption" : { + "output" : " " + }, + "shiftOption" : { + "output" : " " + }, + "unmodified" : { + "output" : " " + } + }, + "5" : { + "caps" : { + "output" : "I" + }, + "capsOption" : { + "output" : "ˆ" + }, + "keyCode" : 5, + "option" : { + "rejection" : "deadKey" + }, + "shift" : { + "output" : "I" + }, + "shiftCaps" : { + "output" : "I" + }, + "shiftCapsOption" : { + "output" : "ˆ" + }, + "shiftOption" : { + "output" : "ˆ" + }, + "unmodified" : { + "output" : "i" + } + }, + "50" : { + "caps" : { + "output" : "`" + }, + "capsOption" : { + "output" : "`" + }, + "keyCode" : 50, + "option" : { + "rejection" : "deadKey" + }, + "shift" : { + "output" : "~" + }, + "shiftCaps" : { + "output" : "~" + }, + "shiftCapsOption" : { + "output" : "`" + }, + "shiftOption" : { + "output" : "`" + }, + "unmodified" : { + "output" : "`" + } + }, + "51" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 51, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "52" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 52, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "53" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 53, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "54" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 54, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "55" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 55, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "56" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 56, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "57" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 57, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "58" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 58, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "59" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 59, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "6" : { + "caps" : { + "output" : ";" + }, + "capsOption" : { + "output" : "…" + }, + "keyCode" : 6, + "option" : { + "output" : "…" + }, + "shift" : { + "output" : ":" + }, + "shiftCaps" : { + "output" : ":" + }, + "shiftCapsOption" : { + "output" : "Ú" + }, + "shiftOption" : { + "output" : "Ú" + }, + "unmodified" : { + "output" : ";" + } + }, + "60" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 60, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "61" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 61, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "62" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 62, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "63" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 63, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "64" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 64, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "65" : { + "caps" : { + "output" : "." + }, + "capsOption" : { + "output" : "." + }, + "keyCode" : 65, + "option" : { + "output" : "." + }, + "shift" : { + "output" : "." + }, + "shiftCaps" : { + "output" : "." + }, + "shiftCapsOption" : { + "output" : "." + }, + "shiftOption" : { + "output" : "." + }, + "unmodified" : { + "output" : "." + } + }, + "66" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 66, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "*" + }, + "shiftCaps" : { + "output" : "*" + }, + "shiftCapsOption" : { + "output" : "*" + }, + "shiftOption" : { + "output" : "*" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "67" : { + "caps" : { + "output" : "*" + }, + "capsOption" : { + "output" : "*" + }, + "keyCode" : 67, + "option" : { + "output" : "*" + }, + "shift" : { + "output" : "*" + }, + "shiftCaps" : { + "output" : "*" + }, + "shiftCapsOption" : { + "output" : "*" + }, + "shiftOption" : { + "output" : "*" + }, + "unmodified" : { + "output" : "*" + } + }, + "68" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 68, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "69" : { + "caps" : { + "output" : "+" + }, + "capsOption" : { + "output" : "+" + }, + "keyCode" : 69, + "option" : { + "output" : "+" + }, + "shift" : { + "output" : "+" + }, + "shiftCaps" : { + "output" : "+" + }, + "shiftCapsOption" : { + "output" : "+" + }, + "shiftOption" : { + "output" : "+" + }, + "unmodified" : { + "output" : "+" + } + }, + "7" : { + "caps" : { + "output" : "Q" + }, + "capsOption" : { + "output" : "Œ" + }, + "keyCode" : 7, + "option" : { + "output" : "œ" + }, + "shift" : { + "output" : "Q" + }, + "shiftCaps" : { + "output" : "Q" + }, + "shiftCapsOption" : { + "output" : "Œ" + }, + "shiftOption" : { + "output" : "Œ" + }, + "unmodified" : { + "output" : "q" + } + }, + "70" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 70, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "+" + }, + "shiftCaps" : { + "output" : "+" + }, + "shiftCapsOption" : { + "output" : "+" + }, + "shiftOption" : { + "output" : "+" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "71" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 71, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "72" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 72, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "=" + }, + "shiftCaps" : { + "output" : "=" + }, + "shiftCapsOption" : { + "output" : "=" + }, + "shiftOption" : { + "output" : "=" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "73" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 73, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "74" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 74, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "75" : { + "caps" : { + "output" : "\/" + }, + "capsOption" : { + "output" : "\/" + }, + "keyCode" : 75, + "option" : { + "output" : "\/" + }, + "shift" : { + "output" : "\/" + }, + "shiftCaps" : { + "output" : "\/" + }, + "shiftCapsOption" : { + "output" : "\/" + }, + "shiftOption" : { + "output" : "\/" + }, + "unmodified" : { + "output" : "\/" + } + }, + "76" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 76, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "77" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 77, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "\/" + }, + "shiftCaps" : { + "output" : "\/" + }, + "shiftCapsOption" : { + "output" : "\/" + }, + "shiftOption" : { + "output" : "\/" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "78" : { + "caps" : { + "output" : "-" + }, + "capsOption" : { + "output" : "-" + }, + "keyCode" : 78, + "option" : { + "output" : "-" + }, + "shift" : { + "output" : "-" + }, + "shiftCaps" : { + "output" : "-" + }, + "shiftCapsOption" : { + "output" : "-" + }, + "shiftOption" : { + "output" : "-" + }, + "unmodified" : { + "output" : "-" + } + }, + "79" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 79, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "8" : { + "caps" : { + "output" : "J" + }, + "capsOption" : { + "output" : "Ô" + }, + "keyCode" : 8, + "option" : { + "output" : "∆" + }, + "shift" : { + "output" : "J" + }, + "shiftCaps" : { + "output" : "J" + }, + "shiftCapsOption" : { + "output" : "Ô" + }, + "shiftOption" : { + "output" : "Ô" + }, + "unmodified" : { + "output" : "j" + } + }, + "80" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 80, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "81" : { + "caps" : { + "output" : "=" + }, + "capsOption" : { + "output" : "=" + }, + "keyCode" : 81, + "option" : { + "output" : "=" + }, + "shift" : { + "output" : "=" + }, + "shiftCaps" : { + "output" : "=" + }, + "shiftCapsOption" : { + "output" : "=" + }, + "shiftOption" : { + "output" : "=" + }, + "unmodified" : { + "output" : "=" + } + }, + "82" : { + "caps" : { + "output" : "0" + }, + "capsOption" : { + "output" : "0" + }, + "keyCode" : 82, + "option" : { + "output" : "0" + }, + "shift" : { + "output" : "0" + }, + "shiftCaps" : { + "output" : "0" + }, + "shiftCapsOption" : { + "output" : "0" + }, + "shiftOption" : { + "output" : "0" + }, + "unmodified" : { + "output" : "0" + } + }, + "83" : { + "caps" : { + "output" : "1" + }, + "capsOption" : { + "output" : "1" + }, + "keyCode" : 83, + "option" : { + "output" : "1" + }, + "shift" : { + "output" : "1" + }, + "shiftCaps" : { + "output" : "1" + }, + "shiftCapsOption" : { + "output" : "1" + }, + "shiftOption" : { + "output" : "1" + }, + "unmodified" : { + "output" : "1" + } + }, + "84" : { + "caps" : { + "output" : "2" + }, + "capsOption" : { + "output" : "2" + }, + "keyCode" : 84, + "option" : { + "output" : "2" + }, + "shift" : { + "output" : "2" + }, + "shiftCaps" : { + "output" : "2" + }, + "shiftCapsOption" : { + "output" : "2" + }, + "shiftOption" : { + "output" : "2" + }, + "unmodified" : { + "output" : "2" + } + }, + "85" : { + "caps" : { + "output" : "3" + }, + "capsOption" : { + "output" : "3" + }, + "keyCode" : 85, + "option" : { + "output" : "3" + }, + "shift" : { + "output" : "3" + }, + "shiftCaps" : { + "output" : "3" + }, + "shiftCapsOption" : { + "output" : "3" + }, + "shiftOption" : { + "output" : "3" + }, + "unmodified" : { + "output" : "3" + } + }, + "86" : { + "caps" : { + "output" : "4" + }, + "capsOption" : { + "output" : "4" + }, + "keyCode" : 86, + "option" : { + "output" : "4" + }, + "shift" : { + "output" : "4" + }, + "shiftCaps" : { + "output" : "4" + }, + "shiftCapsOption" : { + "output" : "4" + }, + "shiftOption" : { + "output" : "4" + }, + "unmodified" : { + "output" : "4" + } + }, + "87" : { + "caps" : { + "output" : "5" + }, + "capsOption" : { + "output" : "5" + }, + "keyCode" : 87, + "option" : { + "output" : "5" + }, + "shift" : { + "output" : "5" + }, + "shiftCaps" : { + "output" : "5" + }, + "shiftCapsOption" : { + "output" : "5" + }, + "shiftOption" : { + "output" : "5" + }, + "unmodified" : { + "output" : "5" + } + }, + "88" : { + "caps" : { + "output" : "6" + }, + "capsOption" : { + "output" : "6" + }, + "keyCode" : 88, + "option" : { + "output" : "6" + }, + "shift" : { + "output" : "6" + }, + "shiftCaps" : { + "output" : "6" + }, + "shiftCapsOption" : { + "output" : "6" + }, + "shiftOption" : { + "output" : "6" + }, + "unmodified" : { + "output" : "6" + } + }, + "89" : { + "caps" : { + "output" : "7" + }, + "capsOption" : { + "output" : "7" + }, + "keyCode" : 89, + "option" : { + "output" : "7" + }, + "shift" : { + "output" : "7" + }, + "shiftCaps" : { + "output" : "7" + }, + "shiftCapsOption" : { + "output" : "7" + }, + "shiftOption" : { + "output" : "7" + }, + "unmodified" : { + "output" : "7" + } + }, + "9" : { + "caps" : { + "output" : "K" + }, + "capsOption" : { + "output" : "˚" + }, + "keyCode" : 9, + "option" : { + "output" : "˚" + }, + "shift" : { + "output" : "K" + }, + "shiftCaps" : { + "output" : "K" + }, + "shiftCapsOption" : { + "rejection" : "privateUseOrSurrogate" + }, + "shiftOption" : { + "rejection" : "privateUseOrSurrogate" + }, + "unmodified" : { + "output" : "k" + } + }, + "90" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 90, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "91" : { + "caps" : { + "output" : "8" + }, + "capsOption" : { + "output" : "8" + }, + "keyCode" : 91, + "option" : { + "output" : "8" + }, + "shift" : { + "output" : "8" + }, + "shiftCaps" : { + "output" : "8" + }, + "shiftCapsOption" : { + "output" : "8" + }, + "shiftOption" : { + "output" : "8" + }, + "unmodified" : { + "output" : "8" + } + }, + "92" : { + "caps" : { + "output" : "9" + }, + "capsOption" : { + "output" : "9" + }, + "keyCode" : 92, + "option" : { + "output" : "9" + }, + "shift" : { + "output" : "9" + }, + "shiftCaps" : { + "output" : "9" + }, + "shiftCapsOption" : { + "output" : "9" + }, + "shiftOption" : { + "output" : "9" + }, + "unmodified" : { + "output" : "9" + } + }, + "93" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 93, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "94" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 94, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "95" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 95, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "96" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 96, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "97" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 97, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "98" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 98, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "99" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 99, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + } + }, + "keyboardType" : 91, + "osBuild" : "24G830", + "osVersion" : "15.7.9", + "schemaVersion" : 1, + "sourceID" : "com.apple.keylayout.Dvorak" +} \ No newline at end of file diff --git a/Benchmarks/layout-tables/v1/com.apple.keylayout.Russian-Phonetic@37.json b/Benchmarks/layout-tables/v1/com.apple.keylayout.Russian-Phonetic@37.json new file mode 100644 index 0000000..0b76ea6 --- /dev/null +++ b/Benchmarks/layout-tables/v1/com.apple.keylayout.Russian-Phonetic@37.json @@ -0,0 +1,3465 @@ +{ + "keyOutputs" : { + "0" : { + "caps" : { + "output" : "А" + }, + "capsOption" : { + "output" : "ї" + }, + "keyCode" : 0, + "option" : { + "output" : "ї" + }, + "shift" : { + "output" : "А" + }, + "shiftCaps" : { + "output" : "А" + }, + "shiftCapsOption" : { + "output" : "Ї" + }, + "shiftOption" : { + "output" : "Ї" + }, + "unmodified" : { + "output" : "а" + } + }, + "1" : { + "caps" : { + "output" : "С" + }, + "capsOption" : { + "output" : "ѕ" + }, + "keyCode" : 1, + "option" : { + "output" : "ѕ" + }, + "shift" : { + "output" : "С" + }, + "shiftCaps" : { + "output" : "С" + }, + "shiftCapsOption" : { + "output" : "Ѕ" + }, + "shiftOption" : { + "output" : "Ѕ" + }, + "unmodified" : { + "output" : "с" + } + }, + "10" : { + "caps" : { + "output" : "<" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 10, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "output" : "<" + }, + "shiftCaps" : { + "output" : "<" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "output" : ">" + } + }, + "100" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 100, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "101" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 101, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "102" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 102, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "103" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 103, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "104" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 104, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "105" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 105, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "106" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 106, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "107" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 107, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "108" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 108, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "109" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 109, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "11" : { + "caps" : { + "output" : "Б" + }, + "capsOption" : { + "output" : "№" + }, + "keyCode" : 11, + "option" : { + "output" : "№" + }, + "shift" : { + "output" : "Б" + }, + "shiftCaps" : { + "output" : "Б" + }, + "shiftCapsOption" : { + "output" : "№" + }, + "shiftOption" : { + "output" : "№" + }, + "unmodified" : { + "output" : "б" + } + }, + "110" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 110, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "111" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 111, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "112" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 112, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "113" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 113, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "114" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 114, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "115" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 115, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "116" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 116, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "117" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 117, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "118" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 118, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "119" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 119, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "12" : { + "caps" : { + "output" : "Я" + }, + "capsOption" : { + "output" : "¬" + }, + "keyCode" : 12, + "option" : { + "output" : "¬" + }, + "shift" : { + "output" : "Я" + }, + "shiftCaps" : { + "output" : "Я" + }, + "shiftCapsOption" : { + "output" : "±" + }, + "shiftOption" : { + "output" : "±" + }, + "unmodified" : { + "output" : "я" + } + }, + "120" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 120, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "121" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 121, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "122" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 122, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "123" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 123, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "124" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 124, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "125" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 125, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "126" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 126, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "127" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 127, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "13" : { + "caps" : { + "output" : "Ш" + }, + "capsOption" : { + "output" : "щ" + }, + "keyCode" : 13, + "option" : { + "output" : "щ" + }, + "shift" : { + "output" : "Ш" + }, + "shiftCaps" : { + "output" : "Ш" + }, + "shiftCapsOption" : { + "output" : "Щ" + }, + "shiftOption" : { + "output" : "Щ" + }, + "unmodified" : { + "output" : "ш" + } + }, + "14" : { + "caps" : { + "output" : "Е" + }, + "capsOption" : { + "output" : "ё" + }, + "keyCode" : 14, + "option" : { + "output" : "ё" + }, + "shift" : { + "output" : "Е" + }, + "shiftCaps" : { + "output" : "Е" + }, + "shiftCapsOption" : { + "output" : "Ё" + }, + "shiftOption" : { + "output" : "Ё" + }, + "unmodified" : { + "output" : "е" + } + }, + "15" : { + "caps" : { + "output" : "Р" + }, + "capsOption" : { + "output" : "₽" + }, + "keyCode" : 15, + "option" : { + "output" : "₽" + }, + "shift" : { + "output" : "Р" + }, + "shiftCaps" : { + "output" : "Р" + }, + "shiftCapsOption" : { + "output" : "®" + }, + "shiftOption" : { + "output" : "®" + }, + "unmodified" : { + "output" : "р" + } + }, + "16" : { + "caps" : { + "output" : "Ы" + }, + "capsOption" : { + "output" : "™" + }, + "keyCode" : 16, + "option" : { + "output" : "™" + }, + "shift" : { + "output" : "Ы" + }, + "shiftCaps" : { + "output" : "Ы" + }, + "shiftCapsOption" : { + "output" : "™" + }, + "shiftOption" : { + "output" : "™" + }, + "unmodified" : { + "output" : "ы" + } + }, + "17" : { + "caps" : { + "output" : "Т" + }, + "capsOption" : { + "output" : "ћ" + }, + "keyCode" : 17, + "option" : { + "output" : "ћ" + }, + "shift" : { + "output" : "Т" + }, + "shiftCaps" : { + "output" : "Т" + }, + "shiftCapsOption" : { + "output" : "Ћ" + }, + "shiftOption" : { + "output" : "Ћ" + }, + "unmodified" : { + "output" : "т" + } + }, + "18" : { + "caps" : { + "output" : "!" + }, + "capsOption" : { + "output" : "ї" + }, + "keyCode" : 18, + "option" : { + "output" : "ї" + }, + "shift" : { + "output" : "!" + }, + "shiftCaps" : { + "output" : "!" + }, + "shiftCapsOption" : { + "output" : "Ї" + }, + "shiftOption" : { + "output" : "Ї" + }, + "unmodified" : { + "output" : "1" + } + }, + "19" : { + "caps" : { + "output" : "@" + }, + "capsOption" : { + "output" : "ё" + }, + "keyCode" : 19, + "option" : { + "output" : "ё" + }, + "shift" : { + "output" : "@" + }, + "shiftCaps" : { + "output" : "@" + }, + "shiftCapsOption" : { + "output" : "Ё" + }, + "shiftOption" : { + "output" : "Ё" + }, + "unmodified" : { + "output" : "2" + } + }, + "2" : { + "caps" : { + "output" : "Д" + }, + "capsOption" : { + "output" : "ђ" + }, + "keyCode" : 2, + "option" : { + "output" : "ђ" + }, + "shift" : { + "output" : "Д" + }, + "shiftCaps" : { + "output" : "Д" + }, + "shiftCapsOption" : { + "output" : "Ђ" + }, + "shiftOption" : { + "output" : "Ђ" + }, + "unmodified" : { + "output" : "д" + } + }, + "20" : { + "caps" : { + "output" : "#" + }, + "capsOption" : { + "output" : "є" + }, + "keyCode" : 20, + "option" : { + "output" : "є" + }, + "shift" : { + "output" : "#" + }, + "shiftCaps" : { + "output" : "#" + }, + "shiftCapsOption" : { + "output" : "Є" + }, + "shiftOption" : { + "output" : "Є" + }, + "unmodified" : { + "output" : "3" + } + }, + "21" : { + "caps" : { + "output" : "$" + }, + "capsOption" : { + "output" : "ґ" + }, + "keyCode" : 21, + "option" : { + "output" : "ґ" + }, + "shift" : { + "output" : "$" + }, + "shiftCaps" : { + "output" : "$" + }, + "shiftCapsOption" : { + "output" : "Ґ" + }, + "shiftOption" : { + "output" : "Ґ" + }, + "unmodified" : { + "output" : "4" + } + }, + "22" : { + "caps" : { + "output" : "^" + }, + "capsOption" : { + "output" : "§" + }, + "keyCode" : 22, + "option" : { + "output" : "§" + }, + "shift" : { + "output" : "^" + }, + "shiftCaps" : { + "output" : "^" + }, + "shiftCapsOption" : { + "output" : "&" + }, + "shiftOption" : { + "output" : "&" + }, + "unmodified" : { + "output" : "6" + } + }, + "23" : { + "caps" : { + "output" : "%" + }, + "capsOption" : { + "output" : "∞" + }, + "keyCode" : 23, + "option" : { + "output" : "∞" + }, + "shift" : { + "output" : "%" + }, + "shiftCaps" : { + "output" : "%" + }, + "shiftCapsOption" : { + "output" : "™" + }, + "shiftOption" : { + "output" : "™" + }, + "unmodified" : { + "output" : "5" + } + }, + "24" : { + "caps" : { + "output" : "Ъ" + }, + "capsOption" : { + "output" : "=" + }, + "keyCode" : 24, + "option" : { + "output" : "=" + }, + "shift" : { + "output" : "Ъ" + }, + "shiftCaps" : { + "output" : "Ъ" + }, + "shiftCapsOption" : { + "output" : "+" + }, + "shiftOption" : { + "output" : "+" + }, + "unmodified" : { + "output" : "ъ" + } + }, + "25" : { + "caps" : { + "output" : "(" + }, + "capsOption" : { + "output" : "[" + }, + "keyCode" : 25, + "option" : { + "output" : "[" + }, + "shift" : { + "output" : "(" + }, + "shiftCaps" : { + "output" : "(" + }, + "shiftCapsOption" : { + "output" : "{" + }, + "shiftOption" : { + "output" : "{" + }, + "unmodified" : { + "output" : "9" + } + }, + "26" : { + "caps" : { + "output" : "&" + }, + "capsOption" : { + "output" : "¶" + }, + "keyCode" : 26, + "option" : { + "output" : "¶" + }, + "shift" : { + "output" : "&" + }, + "shiftCaps" : { + "output" : "&" + }, + "shiftCapsOption" : { + "output" : "№" + }, + "shiftOption" : { + "output" : "№" + }, + "unmodified" : { + "output" : "7" + } + }, + "27" : { + "caps" : { + "output" : "Ь" + }, + "capsOption" : { + "output" : "-" + }, + "keyCode" : 27, + "option" : { + "output" : "-" + }, + "shift" : { + "output" : "Ь" + }, + "shiftCaps" : { + "output" : "Ь" + }, + "shiftCapsOption" : { + "output" : "—" + }, + "shiftOption" : { + "output" : "—" + }, + "unmodified" : { + "output" : "ь" + } + }, + "28" : { + "caps" : { + "output" : "*" + }, + "capsOption" : { + "output" : "•" + }, + "keyCode" : 28, + "option" : { + "output" : "•" + }, + "shift" : { + "output" : "*" + }, + "shiftCaps" : { + "output" : "*" + }, + "shiftCapsOption" : { + "output" : "°" + }, + "shiftOption" : { + "output" : "°" + }, + "unmodified" : { + "output" : "8" + } + }, + "29" : { + "caps" : { + "output" : ")" + }, + "capsOption" : { + "output" : "]" + }, + "keyCode" : 29, + "option" : { + "output" : "]" + }, + "shift" : { + "output" : ")" + }, + "shiftCaps" : { + "output" : ")" + }, + "shiftCapsOption" : { + "output" : "}" + }, + "shiftOption" : { + "output" : "}" + }, + "unmodified" : { + "output" : "0" + } + }, + "3" : { + "caps" : { + "output" : "Ф" + }, + "capsOption" : { + "output" : "ƒ" + }, + "keyCode" : 3, + "option" : { + "output" : "ƒ" + }, + "shift" : { + "output" : "Ф" + }, + "shiftCaps" : { + "output" : "Ф" + }, + "shiftCapsOption" : { + "output" : "∆" + }, + "shiftOption" : { + "output" : "∆" + }, + "unmodified" : { + "output" : "ф" + } + }, + "30" : { + "caps" : { + "output" : "Ж" + }, + "capsOption" : { + "output" : "џ" + }, + "keyCode" : 30, + "option" : { + "output" : "џ" + }, + "shift" : { + "output" : "Ж" + }, + "shiftCaps" : { + "output" : "Ж" + }, + "shiftCapsOption" : { + "output" : "Џ" + }, + "shiftOption" : { + "output" : "Џ" + }, + "unmodified" : { + "output" : "ж" + } + }, + "31" : { + "caps" : { + "output" : "О" + }, + "capsOption" : { + "output" : "«" + }, + "keyCode" : 31, + "option" : { + "output" : "«" + }, + "shift" : { + "output" : "О" + }, + "shiftCaps" : { + "output" : "О" + }, + "shiftCapsOption" : { + "output" : "‘" + }, + "shiftOption" : { + "output" : "‘" + }, + "unmodified" : { + "output" : "о" + } + }, + "32" : { + "caps" : { + "output" : "У" + }, + "capsOption" : { + "output" : "ў" + }, + "keyCode" : 32, + "option" : { + "output" : "ў" + }, + "shift" : { + "output" : "У" + }, + "shiftCaps" : { + "output" : "У" + }, + "shiftCapsOption" : { + "output" : "Ў" + }, + "shiftOption" : { + "output" : "Ў" + }, + "unmodified" : { + "output" : "у" + } + }, + "33" : { + "caps" : { + "output" : "Ю" + }, + "capsOption" : { + "output" : "“" + }, + "keyCode" : 33, + "option" : { + "output" : "“" + }, + "shift" : { + "output" : "Ю" + }, + "shiftCaps" : { + "output" : "Ю" + }, + "shiftCapsOption" : { + "output" : "”" + }, + "shiftOption" : { + "output" : "”" + }, + "unmodified" : { + "output" : "ю" + } + }, + "34" : { + "caps" : { + "output" : "И" + }, + "capsOption" : { + "output" : "і" + }, + "keyCode" : 34, + "option" : { + "output" : "і" + }, + "shift" : { + "output" : "И" + }, + "shiftCaps" : { + "output" : "И" + }, + "shiftCapsOption" : { + "output" : "І" + }, + "shiftOption" : { + "output" : "І" + }, + "unmodified" : { + "output" : "и" + } + }, + "35" : { + "caps" : { + "output" : "П" + }, + "capsOption" : { + "output" : "»" + }, + "keyCode" : 35, + "option" : { + "output" : "»" + }, + "shift" : { + "output" : "П" + }, + "shiftCaps" : { + "output" : "П" + }, + "shiftCapsOption" : { + "output" : "’" + }, + "shiftOption" : { + "output" : "’" + }, + "unmodified" : { + "output" : "п" + } + }, + "36" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 36, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "37" : { + "caps" : { + "output" : "Л" + }, + "capsOption" : { + "output" : "љ" + }, + "keyCode" : 37, + "option" : { + "output" : "љ" + }, + "shift" : { + "output" : "Л" + }, + "shiftCaps" : { + "output" : "Л" + }, + "shiftCapsOption" : { + "output" : "Љ" + }, + "shiftOption" : { + "output" : "Љ" + }, + "unmodified" : { + "output" : "л" + } + }, + "38" : { + "caps" : { + "output" : "Й" + }, + "capsOption" : { + "output" : "ј" + }, + "keyCode" : 38, + "option" : { + "output" : "ј" + }, + "shift" : { + "output" : "Й" + }, + "shiftCaps" : { + "output" : "Й" + }, + "shiftCapsOption" : { + "output" : "Ј" + }, + "shiftOption" : { + "output" : "Ј" + }, + "unmodified" : { + "output" : "й" + } + }, + "39" : { + "caps" : { + "output" : "\"" + }, + "capsOption" : { + "output" : "’" + }, + "keyCode" : 39, + "option" : { + "output" : "’" + }, + "shift" : { + "output" : "\"" + }, + "shiftCaps" : { + "output" : "\"" + }, + "shiftCapsOption" : { + "output" : "`" + }, + "shiftOption" : { + "output" : "`" + }, + "unmodified" : { + "output" : "'" + } + }, + "4" : { + "caps" : { + "output" : "Ч" + }, + "capsOption" : { + "output" : "ґ" + }, + "keyCode" : 4, + "option" : { + "output" : "ґ" + }, + "shift" : { + "output" : "Ч" + }, + "shiftCaps" : { + "output" : "Ч" + }, + "shiftCapsOption" : { + "output" : "Ґ" + }, + "shiftOption" : { + "output" : "Ґ" + }, + "unmodified" : { + "output" : "ч" + } + }, + "40" : { + "caps" : { + "output" : "К" + }, + "capsOption" : { + "output" : "ќ" + }, + "keyCode" : 40, + "option" : { + "output" : "ќ" + }, + "shift" : { + "output" : "К" + }, + "shiftCaps" : { + "output" : "К" + }, + "shiftCapsOption" : { + "output" : "Ќ" + }, + "shiftOption" : { + "output" : "Ќ" + }, + "unmodified" : { + "output" : "к" + } + }, + "41" : { + "caps" : { + "output" : ":" + }, + "capsOption" : { + "output" : "„" + }, + "keyCode" : 41, + "option" : { + "output" : "„" + }, + "shift" : { + "output" : ":" + }, + "shiftCaps" : { + "output" : ":" + }, + "shiftCapsOption" : { + "output" : "’" + }, + "shiftOption" : { + "output" : "’" + }, + "unmodified" : { + "output" : ";" + } + }, + "42" : { + "caps" : { + "output" : "Э" + }, + "capsOption" : { + "output" : "є" + }, + "keyCode" : 42, + "option" : { + "output" : "є" + }, + "shift" : { + "output" : "Э" + }, + "shiftCaps" : { + "output" : "Э" + }, + "shiftCapsOption" : { + "output" : "Є" + }, + "shiftOption" : { + "output" : "Є" + }, + "unmodified" : { + "output" : "э" + } + }, + "43" : { + "caps" : { + "output" : "<" + }, + "capsOption" : { + "output" : "≤" + }, + "keyCode" : 43, + "option" : { + "output" : "≤" + }, + "shift" : { + "output" : "<" + }, + "shiftCaps" : { + "output" : "<" + }, + "shiftCapsOption" : { + "output" : "÷" + }, + "shiftOption" : { + "output" : "÷" + }, + "unmodified" : { + "output" : "," + } + }, + "44" : { + "caps" : { + "output" : "?" + }, + "capsOption" : { + "output" : "÷" + }, + "keyCode" : 44, + "option" : { + "output" : "÷" + }, + "shift" : { + "output" : "?" + }, + "shiftCaps" : { + "output" : "?" + }, + "shiftCapsOption" : { + "output" : "\\" + }, + "shiftOption" : { + "output" : "\\" + }, + "unmodified" : { + "output" : "\/" + } + }, + "45" : { + "caps" : { + "output" : "Н" + }, + "capsOption" : { + "output" : "њ" + }, + "keyCode" : 45, + "option" : { + "output" : "њ" + }, + "shift" : { + "output" : "Н" + }, + "shiftCaps" : { + "output" : "Н" + }, + "shiftCapsOption" : { + "output" : "Њ" + }, + "shiftOption" : { + "output" : "Њ" + }, + "unmodified" : { + "output" : "н" + } + }, + "46" : { + "caps" : { + "output" : "М" + }, + "capsOption" : { + "output" : "µ" + }, + "keyCode" : 46, + "option" : { + "output" : "µ" + }, + "shift" : { + "output" : "М" + }, + "shiftCaps" : { + "output" : "М" + }, + "shiftCapsOption" : { + "output" : "µ" + }, + "shiftOption" : { + "output" : "µ" + }, + "unmodified" : { + "output" : "м" + } + }, + "47" : { + "caps" : { + "output" : ">" + }, + "capsOption" : { + "output" : "≥" + }, + "keyCode" : 47, + "option" : { + "output" : "≥" + }, + "shift" : { + "output" : ">" + }, + "shiftCaps" : { + "output" : ">" + }, + "shiftCapsOption" : { + "output" : "…" + }, + "shiftOption" : { + "output" : "…" + }, + "unmodified" : { + "output" : "." + } + }, + "48" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 48, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "49" : { + "caps" : { + "output" : " " + }, + "capsOption" : { + "output" : " " + }, + "keyCode" : 49, + "option" : { + "output" : " " + }, + "shift" : { + "output" : " " + }, + "shiftCaps" : { + "output" : " " + }, + "shiftCapsOption" : { + "output" : " " + }, + "shiftOption" : { + "output" : " " + }, + "unmodified" : { + "output" : " " + } + }, + "5" : { + "caps" : { + "output" : "Г" + }, + "capsOption" : { + "output" : "ѓ" + }, + "keyCode" : 5, + "option" : { + "output" : "ѓ" + }, + "shift" : { + "output" : "Г" + }, + "shiftCaps" : { + "output" : "Г" + }, + "shiftCapsOption" : { + "output" : "Ѓ" + }, + "shiftOption" : { + "output" : "Ѓ" + }, + "unmodified" : { + "output" : "г" + } + }, + "50" : { + "caps" : { + "output" : "Щ" + }, + "capsOption" : { + "output" : "†" + }, + "keyCode" : 50, + "option" : { + "output" : "†" + }, + "shift" : { + "output" : "Щ" + }, + "shiftCaps" : { + "output" : "Щ" + }, + "shiftCapsOption" : { + "output" : "`" + }, + "shiftOption" : { + "output" : "`" + }, + "unmodified" : { + "output" : "щ" + } + }, + "51" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 51, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "52" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 52, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "53" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 53, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "54" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 54, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "55" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 55, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "56" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 56, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "57" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 57, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "58" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 58, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "59" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 59, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "6" : { + "caps" : { + "output" : "З" + }, + "capsOption" : { + "output" : "џ" + }, + "keyCode" : 6, + "option" : { + "output" : "џ" + }, + "shift" : { + "output" : "З" + }, + "shiftCaps" : { + "output" : "З" + }, + "shiftCapsOption" : { + "output" : "Џ" + }, + "shiftOption" : { + "output" : "Џ" + }, + "unmodified" : { + "output" : "з" + } + }, + "60" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 60, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "61" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 61, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "62" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 62, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "63" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 63, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "64" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 64, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "65" : { + "caps" : { + "output" : "," + }, + "capsOption" : { + "output" : "," + }, + "keyCode" : 65, + "option" : { + "output" : "," + }, + "shift" : { + "output" : "," + }, + "shiftCaps" : { + "output" : "," + }, + "shiftCapsOption" : { + "output" : "," + }, + "shiftOption" : { + "output" : "," + }, + "unmodified" : { + "output" : "," + } + }, + "66" : { + "caps" : { + "output" : "*" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 66, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "*" + }, + "shiftCaps" : { + "output" : "*" + }, + "shiftCapsOption" : { + "output" : "*" + }, + "shiftOption" : { + "output" : "*" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "67" : { + "caps" : { + "output" : "*" + }, + "capsOption" : { + "output" : "*" + }, + "keyCode" : 67, + "option" : { + "output" : "*" + }, + "shift" : { + "output" : "*" + }, + "shiftCaps" : { + "output" : "*" + }, + "shiftCapsOption" : { + "output" : "*" + }, + "shiftOption" : { + "output" : "*" + }, + "unmodified" : { + "output" : "*" + } + }, + "68" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 68, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "69" : { + "caps" : { + "output" : "+" + }, + "capsOption" : { + "output" : "+" + }, + "keyCode" : 69, + "option" : { + "output" : "+" + }, + "shift" : { + "output" : "+" + }, + "shiftCaps" : { + "output" : "+" + }, + "shiftCapsOption" : { + "output" : "+" + }, + "shiftOption" : { + "output" : "+" + }, + "unmodified" : { + "output" : "+" + } + }, + "7" : { + "caps" : { + "output" : "Х" + }, + "capsOption" : { + "output" : "≈" + }, + "keyCode" : 7, + "option" : { + "output" : "≈" + }, + "shift" : { + "output" : "Х" + }, + "shiftCaps" : { + "output" : "Х" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "output" : "х" + } + }, + "70" : { + "caps" : { + "output" : "+" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 70, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "+" + }, + "shiftCaps" : { + "output" : "+" + }, + "shiftCapsOption" : { + "output" : "+" + }, + "shiftOption" : { + "output" : "+" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "71" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 71, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "72" : { + "caps" : { + "output" : "=" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 72, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "=" + }, + "shiftCaps" : { + "output" : "=" + }, + "shiftCapsOption" : { + "output" : "=" + }, + "shiftOption" : { + "output" : "=" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "73" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 73, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "74" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 74, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "75" : { + "caps" : { + "output" : "\/" + }, + "capsOption" : { + "output" : "\/" + }, + "keyCode" : 75, + "option" : { + "output" : "\/" + }, + "shift" : { + "output" : "\/" + }, + "shiftCaps" : { + "output" : "\/" + }, + "shiftCapsOption" : { + "output" : "\/" + }, + "shiftOption" : { + "output" : "\/" + }, + "unmodified" : { + "output" : "\/" + } + }, + "76" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 76, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "77" : { + "caps" : { + "output" : "\/" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 77, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "\/" + }, + "shiftCaps" : { + "output" : "\/" + }, + "shiftCapsOption" : { + "output" : "\/" + }, + "shiftOption" : { + "output" : "\/" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "78" : { + "caps" : { + "output" : "-" + }, + "capsOption" : { + "output" : "-" + }, + "keyCode" : 78, + "option" : { + "output" : "-" + }, + "shift" : { + "output" : "-" + }, + "shiftCaps" : { + "output" : "-" + }, + "shiftCapsOption" : { + "output" : "-" + }, + "shiftOption" : { + "output" : "-" + }, + "unmodified" : { + "output" : "-" + } + }, + "79" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 79, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "8" : { + "caps" : { + "output" : "Ц" + }, + "capsOption" : { + "output" : "©" + }, + "keyCode" : 8, + "option" : { + "output" : "©" + }, + "shift" : { + "output" : "Ц" + }, + "shiftCaps" : { + "output" : "Ц" + }, + "shiftCapsOption" : { + "output" : "©" + }, + "shiftOption" : { + "output" : "©" + }, + "unmodified" : { + "output" : "ц" + } + }, + "80" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 80, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "81" : { + "caps" : { + "output" : "=" + }, + "capsOption" : { + "output" : "=" + }, + "keyCode" : 81, + "option" : { + "output" : "=" + }, + "shift" : { + "output" : "=" + }, + "shiftCaps" : { + "output" : "=" + }, + "shiftCapsOption" : { + "output" : "=" + }, + "shiftOption" : { + "output" : "=" + }, + "unmodified" : { + "output" : "=" + } + }, + "82" : { + "caps" : { + "output" : "0" + }, + "capsOption" : { + "output" : "0" + }, + "keyCode" : 82, + "option" : { + "output" : "0" + }, + "shift" : { + "output" : "0" + }, + "shiftCaps" : { + "output" : "0" + }, + "shiftCapsOption" : { + "output" : "0" + }, + "shiftOption" : { + "output" : "0" + }, + "unmodified" : { + "output" : "0" + } + }, + "83" : { + "caps" : { + "output" : "1" + }, + "capsOption" : { + "output" : "1" + }, + "keyCode" : 83, + "option" : { + "output" : "1" + }, + "shift" : { + "output" : "1" + }, + "shiftCaps" : { + "output" : "1" + }, + "shiftCapsOption" : { + "output" : "1" + }, + "shiftOption" : { + "output" : "1" + }, + "unmodified" : { + "output" : "1" + } + }, + "84" : { + "caps" : { + "output" : "2" + }, + "capsOption" : { + "output" : "2" + }, + "keyCode" : 84, + "option" : { + "output" : "2" + }, + "shift" : { + "output" : "2" + }, + "shiftCaps" : { + "output" : "2" + }, + "shiftCapsOption" : { + "output" : "2" + }, + "shiftOption" : { + "output" : "2" + }, + "unmodified" : { + "output" : "2" + } + }, + "85" : { + "caps" : { + "output" : "3" + }, + "capsOption" : { + "output" : "3" + }, + "keyCode" : 85, + "option" : { + "output" : "3" + }, + "shift" : { + "output" : "3" + }, + "shiftCaps" : { + "output" : "3" + }, + "shiftCapsOption" : { + "output" : "3" + }, + "shiftOption" : { + "output" : "3" + }, + "unmodified" : { + "output" : "3" + } + }, + "86" : { + "caps" : { + "output" : "4" + }, + "capsOption" : { + "output" : "4" + }, + "keyCode" : 86, + "option" : { + "output" : "4" + }, + "shift" : { + "output" : "4" + }, + "shiftCaps" : { + "output" : "4" + }, + "shiftCapsOption" : { + "output" : "4" + }, + "shiftOption" : { + "output" : "4" + }, + "unmodified" : { + "output" : "4" + } + }, + "87" : { + "caps" : { + "output" : "5" + }, + "capsOption" : { + "output" : "5" + }, + "keyCode" : 87, + "option" : { + "output" : "5" + }, + "shift" : { + "output" : "5" + }, + "shiftCaps" : { + "output" : "5" + }, + "shiftCapsOption" : { + "output" : "5" + }, + "shiftOption" : { + "output" : "5" + }, + "unmodified" : { + "output" : "5" + } + }, + "88" : { + "caps" : { + "output" : "6" + }, + "capsOption" : { + "output" : "6" + }, + "keyCode" : 88, + "option" : { + "output" : "6" + }, + "shift" : { + "output" : "6" + }, + "shiftCaps" : { + "output" : "6" + }, + "shiftCapsOption" : { + "output" : "6" + }, + "shiftOption" : { + "output" : "6" + }, + "unmodified" : { + "output" : "6" + } + }, + "89" : { + "caps" : { + "output" : "7" + }, + "capsOption" : { + "output" : "7" + }, + "keyCode" : 89, + "option" : { + "output" : "7" + }, + "shift" : { + "output" : "7" + }, + "shiftCaps" : { + "output" : "7" + }, + "shiftCapsOption" : { + "output" : "7" + }, + "shiftOption" : { + "output" : "7" + }, + "unmodified" : { + "output" : "7" + } + }, + "9" : { + "caps" : { + "output" : "В" + }, + "capsOption" : { + "output" : "√" + }, + "keyCode" : 9, + "option" : { + "output" : "√" + }, + "shift" : { + "output" : "В" + }, + "shiftCaps" : { + "output" : "В" + }, + "shiftCapsOption" : { + "output" : "†" + }, + "shiftOption" : { + "output" : "†" + }, + "unmodified" : { + "output" : "в" + } + }, + "90" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 90, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "91" : { + "caps" : { + "output" : "8" + }, + "capsOption" : { + "output" : "8" + }, + "keyCode" : 91, + "option" : { + "output" : "8" + }, + "shift" : { + "output" : "8" + }, + "shiftCaps" : { + "output" : "8" + }, + "shiftCapsOption" : { + "output" : "8" + }, + "shiftOption" : { + "output" : "8" + }, + "unmodified" : { + "output" : "8" + } + }, + "92" : { + "caps" : { + "output" : "9" + }, + "capsOption" : { + "output" : "9" + }, + "keyCode" : 92, + "option" : { + "output" : "9" + }, + "shift" : { + "output" : "9" + }, + "shiftCaps" : { + "output" : "9" + }, + "shiftCapsOption" : { + "output" : "9" + }, + "shiftOption" : { + "output" : "9" + }, + "unmodified" : { + "output" : "9" + } + }, + "93" : { + "caps" : { + "output" : "<" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 93, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "output" : "<" + }, + "shiftCaps" : { + "output" : "<" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "output" : ">" + } + }, + "94" : { + "caps" : { + "output" : "Щ" + }, + "capsOption" : { + "output" : "†" + }, + "keyCode" : 94, + "option" : { + "output" : "†" + }, + "shift" : { + "output" : "Щ" + }, + "shiftCaps" : { + "output" : "Щ" + }, + "shiftCapsOption" : { + "output" : "`" + }, + "shiftOption" : { + "output" : "`" + }, + "unmodified" : { + "output" : "щ" + } + }, + "95" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 95, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "96" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 96, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "97" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 97, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "98" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 98, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "99" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 99, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + } + }, + "keyboardType" : 37, + "osBuild" : "24G830", + "osVersion" : "15.7.9", + "schemaVersion" : 1, + "sourceID" : "com.apple.keylayout.Russian-Phonetic" +} \ No newline at end of file diff --git a/Benchmarks/layout-tables/v1/com.apple.keylayout.Russian-Phonetic@91.json b/Benchmarks/layout-tables/v1/com.apple.keylayout.Russian-Phonetic@91.json new file mode 100644 index 0000000..e2a25ed --- /dev/null +++ b/Benchmarks/layout-tables/v1/com.apple.keylayout.Russian-Phonetic@91.json @@ -0,0 +1,3465 @@ +{ + "keyOutputs" : { + "0" : { + "caps" : { + "output" : "А" + }, + "capsOption" : { + "output" : "ї" + }, + "keyCode" : 0, + "option" : { + "output" : "ї" + }, + "shift" : { + "output" : "А" + }, + "shiftCaps" : { + "output" : "А" + }, + "shiftCapsOption" : { + "output" : "Ї" + }, + "shiftOption" : { + "output" : "Ї" + }, + "unmodified" : { + "output" : "а" + } + }, + "1" : { + "caps" : { + "output" : "С" + }, + "capsOption" : { + "output" : "ѕ" + }, + "keyCode" : 1, + "option" : { + "output" : "ѕ" + }, + "shift" : { + "output" : "С" + }, + "shiftCaps" : { + "output" : "С" + }, + "shiftCapsOption" : { + "output" : "Ѕ" + }, + "shiftOption" : { + "output" : "Ѕ" + }, + "unmodified" : { + "output" : "с" + } + }, + "10" : { + "caps" : { + "output" : "<" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 10, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "output" : "<" + }, + "shiftCaps" : { + "output" : "<" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "output" : ">" + } + }, + "100" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 100, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "101" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 101, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "102" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 102, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "103" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 103, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "104" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 104, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "105" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 105, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "106" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 106, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "107" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 107, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "108" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 108, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "109" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 109, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "11" : { + "caps" : { + "output" : "Б" + }, + "capsOption" : { + "output" : "№" + }, + "keyCode" : 11, + "option" : { + "output" : "№" + }, + "shift" : { + "output" : "Б" + }, + "shiftCaps" : { + "output" : "Б" + }, + "shiftCapsOption" : { + "output" : "№" + }, + "shiftOption" : { + "output" : "№" + }, + "unmodified" : { + "output" : "б" + } + }, + "110" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 110, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "111" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 111, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "112" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 112, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "113" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 113, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "114" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 114, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "115" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 115, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "116" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 116, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "117" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 117, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "118" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 118, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "119" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 119, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "12" : { + "caps" : { + "output" : "Я" + }, + "capsOption" : { + "output" : "¬" + }, + "keyCode" : 12, + "option" : { + "output" : "¬" + }, + "shift" : { + "output" : "Я" + }, + "shiftCaps" : { + "output" : "Я" + }, + "shiftCapsOption" : { + "output" : "±" + }, + "shiftOption" : { + "output" : "±" + }, + "unmodified" : { + "output" : "я" + } + }, + "120" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 120, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "121" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 121, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "122" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 122, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "123" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 123, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "124" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 124, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "125" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 125, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "126" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 126, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "127" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 127, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "13" : { + "caps" : { + "output" : "Ш" + }, + "capsOption" : { + "output" : "щ" + }, + "keyCode" : 13, + "option" : { + "output" : "щ" + }, + "shift" : { + "output" : "Ш" + }, + "shiftCaps" : { + "output" : "Ш" + }, + "shiftCapsOption" : { + "output" : "Щ" + }, + "shiftOption" : { + "output" : "Щ" + }, + "unmodified" : { + "output" : "ш" + } + }, + "14" : { + "caps" : { + "output" : "Е" + }, + "capsOption" : { + "output" : "ё" + }, + "keyCode" : 14, + "option" : { + "output" : "ё" + }, + "shift" : { + "output" : "Е" + }, + "shiftCaps" : { + "output" : "Е" + }, + "shiftCapsOption" : { + "output" : "Ё" + }, + "shiftOption" : { + "output" : "Ё" + }, + "unmodified" : { + "output" : "е" + } + }, + "15" : { + "caps" : { + "output" : "Р" + }, + "capsOption" : { + "output" : "₽" + }, + "keyCode" : 15, + "option" : { + "output" : "₽" + }, + "shift" : { + "output" : "Р" + }, + "shiftCaps" : { + "output" : "Р" + }, + "shiftCapsOption" : { + "output" : "®" + }, + "shiftOption" : { + "output" : "®" + }, + "unmodified" : { + "output" : "р" + } + }, + "16" : { + "caps" : { + "output" : "Ы" + }, + "capsOption" : { + "output" : "™" + }, + "keyCode" : 16, + "option" : { + "output" : "™" + }, + "shift" : { + "output" : "Ы" + }, + "shiftCaps" : { + "output" : "Ы" + }, + "shiftCapsOption" : { + "output" : "™" + }, + "shiftOption" : { + "output" : "™" + }, + "unmodified" : { + "output" : "ы" + } + }, + "17" : { + "caps" : { + "output" : "Т" + }, + "capsOption" : { + "output" : "ћ" + }, + "keyCode" : 17, + "option" : { + "output" : "ћ" + }, + "shift" : { + "output" : "Т" + }, + "shiftCaps" : { + "output" : "Т" + }, + "shiftCapsOption" : { + "output" : "Ћ" + }, + "shiftOption" : { + "output" : "Ћ" + }, + "unmodified" : { + "output" : "т" + } + }, + "18" : { + "caps" : { + "output" : "!" + }, + "capsOption" : { + "output" : "ї" + }, + "keyCode" : 18, + "option" : { + "output" : "ї" + }, + "shift" : { + "output" : "!" + }, + "shiftCaps" : { + "output" : "!" + }, + "shiftCapsOption" : { + "output" : "Ї" + }, + "shiftOption" : { + "output" : "Ї" + }, + "unmodified" : { + "output" : "1" + } + }, + "19" : { + "caps" : { + "output" : "@" + }, + "capsOption" : { + "output" : "ё" + }, + "keyCode" : 19, + "option" : { + "output" : "ё" + }, + "shift" : { + "output" : "@" + }, + "shiftCaps" : { + "output" : "@" + }, + "shiftCapsOption" : { + "output" : "Ё" + }, + "shiftOption" : { + "output" : "Ё" + }, + "unmodified" : { + "output" : "2" + } + }, + "2" : { + "caps" : { + "output" : "Д" + }, + "capsOption" : { + "output" : "ђ" + }, + "keyCode" : 2, + "option" : { + "output" : "ђ" + }, + "shift" : { + "output" : "Д" + }, + "shiftCaps" : { + "output" : "Д" + }, + "shiftCapsOption" : { + "output" : "Ђ" + }, + "shiftOption" : { + "output" : "Ђ" + }, + "unmodified" : { + "output" : "д" + } + }, + "20" : { + "caps" : { + "output" : "#" + }, + "capsOption" : { + "output" : "є" + }, + "keyCode" : 20, + "option" : { + "output" : "є" + }, + "shift" : { + "output" : "#" + }, + "shiftCaps" : { + "output" : "#" + }, + "shiftCapsOption" : { + "output" : "Є" + }, + "shiftOption" : { + "output" : "Є" + }, + "unmodified" : { + "output" : "3" + } + }, + "21" : { + "caps" : { + "output" : "$" + }, + "capsOption" : { + "output" : "ґ" + }, + "keyCode" : 21, + "option" : { + "output" : "ґ" + }, + "shift" : { + "output" : "$" + }, + "shiftCaps" : { + "output" : "$" + }, + "shiftCapsOption" : { + "output" : "Ґ" + }, + "shiftOption" : { + "output" : "Ґ" + }, + "unmodified" : { + "output" : "4" + } + }, + "22" : { + "caps" : { + "output" : "^" + }, + "capsOption" : { + "output" : "§" + }, + "keyCode" : 22, + "option" : { + "output" : "§" + }, + "shift" : { + "output" : "^" + }, + "shiftCaps" : { + "output" : "^" + }, + "shiftCapsOption" : { + "output" : "&" + }, + "shiftOption" : { + "output" : "&" + }, + "unmodified" : { + "output" : "6" + } + }, + "23" : { + "caps" : { + "output" : "%" + }, + "capsOption" : { + "output" : "∞" + }, + "keyCode" : 23, + "option" : { + "output" : "∞" + }, + "shift" : { + "output" : "%" + }, + "shiftCaps" : { + "output" : "%" + }, + "shiftCapsOption" : { + "output" : "™" + }, + "shiftOption" : { + "output" : "™" + }, + "unmodified" : { + "output" : "5" + } + }, + "24" : { + "caps" : { + "output" : "Ъ" + }, + "capsOption" : { + "output" : "=" + }, + "keyCode" : 24, + "option" : { + "output" : "=" + }, + "shift" : { + "output" : "Ъ" + }, + "shiftCaps" : { + "output" : "Ъ" + }, + "shiftCapsOption" : { + "output" : "+" + }, + "shiftOption" : { + "output" : "+" + }, + "unmodified" : { + "output" : "ъ" + } + }, + "25" : { + "caps" : { + "output" : "(" + }, + "capsOption" : { + "output" : "[" + }, + "keyCode" : 25, + "option" : { + "output" : "[" + }, + "shift" : { + "output" : "(" + }, + "shiftCaps" : { + "output" : "(" + }, + "shiftCapsOption" : { + "output" : "{" + }, + "shiftOption" : { + "output" : "{" + }, + "unmodified" : { + "output" : "9" + } + }, + "26" : { + "caps" : { + "output" : "&" + }, + "capsOption" : { + "output" : "¶" + }, + "keyCode" : 26, + "option" : { + "output" : "¶" + }, + "shift" : { + "output" : "&" + }, + "shiftCaps" : { + "output" : "&" + }, + "shiftCapsOption" : { + "output" : "№" + }, + "shiftOption" : { + "output" : "№" + }, + "unmodified" : { + "output" : "7" + } + }, + "27" : { + "caps" : { + "output" : "Ь" + }, + "capsOption" : { + "output" : "-" + }, + "keyCode" : 27, + "option" : { + "output" : "-" + }, + "shift" : { + "output" : "Ь" + }, + "shiftCaps" : { + "output" : "Ь" + }, + "shiftCapsOption" : { + "output" : "—" + }, + "shiftOption" : { + "output" : "—" + }, + "unmodified" : { + "output" : "ь" + } + }, + "28" : { + "caps" : { + "output" : "*" + }, + "capsOption" : { + "output" : "•" + }, + "keyCode" : 28, + "option" : { + "output" : "•" + }, + "shift" : { + "output" : "*" + }, + "shiftCaps" : { + "output" : "*" + }, + "shiftCapsOption" : { + "output" : "°" + }, + "shiftOption" : { + "output" : "°" + }, + "unmodified" : { + "output" : "8" + } + }, + "29" : { + "caps" : { + "output" : ")" + }, + "capsOption" : { + "output" : "]" + }, + "keyCode" : 29, + "option" : { + "output" : "]" + }, + "shift" : { + "output" : ")" + }, + "shiftCaps" : { + "output" : ")" + }, + "shiftCapsOption" : { + "output" : "}" + }, + "shiftOption" : { + "output" : "}" + }, + "unmodified" : { + "output" : "0" + } + }, + "3" : { + "caps" : { + "output" : "Ф" + }, + "capsOption" : { + "output" : "ƒ" + }, + "keyCode" : 3, + "option" : { + "output" : "ƒ" + }, + "shift" : { + "output" : "Ф" + }, + "shiftCaps" : { + "output" : "Ф" + }, + "shiftCapsOption" : { + "output" : "∆" + }, + "shiftOption" : { + "output" : "∆" + }, + "unmodified" : { + "output" : "ф" + } + }, + "30" : { + "caps" : { + "output" : "Ж" + }, + "capsOption" : { + "output" : "џ" + }, + "keyCode" : 30, + "option" : { + "output" : "џ" + }, + "shift" : { + "output" : "Ж" + }, + "shiftCaps" : { + "output" : "Ж" + }, + "shiftCapsOption" : { + "output" : "Џ" + }, + "shiftOption" : { + "output" : "Џ" + }, + "unmodified" : { + "output" : "ж" + } + }, + "31" : { + "caps" : { + "output" : "О" + }, + "capsOption" : { + "output" : "«" + }, + "keyCode" : 31, + "option" : { + "output" : "«" + }, + "shift" : { + "output" : "О" + }, + "shiftCaps" : { + "output" : "О" + }, + "shiftCapsOption" : { + "output" : "‘" + }, + "shiftOption" : { + "output" : "‘" + }, + "unmodified" : { + "output" : "о" + } + }, + "32" : { + "caps" : { + "output" : "У" + }, + "capsOption" : { + "output" : "ў" + }, + "keyCode" : 32, + "option" : { + "output" : "ў" + }, + "shift" : { + "output" : "У" + }, + "shiftCaps" : { + "output" : "У" + }, + "shiftCapsOption" : { + "output" : "Ў" + }, + "shiftOption" : { + "output" : "Ў" + }, + "unmodified" : { + "output" : "у" + } + }, + "33" : { + "caps" : { + "output" : "Ю" + }, + "capsOption" : { + "output" : "“" + }, + "keyCode" : 33, + "option" : { + "output" : "“" + }, + "shift" : { + "output" : "Ю" + }, + "shiftCaps" : { + "output" : "Ю" + }, + "shiftCapsOption" : { + "output" : "”" + }, + "shiftOption" : { + "output" : "”" + }, + "unmodified" : { + "output" : "ю" + } + }, + "34" : { + "caps" : { + "output" : "И" + }, + "capsOption" : { + "output" : "і" + }, + "keyCode" : 34, + "option" : { + "output" : "і" + }, + "shift" : { + "output" : "И" + }, + "shiftCaps" : { + "output" : "И" + }, + "shiftCapsOption" : { + "output" : "І" + }, + "shiftOption" : { + "output" : "І" + }, + "unmodified" : { + "output" : "и" + } + }, + "35" : { + "caps" : { + "output" : "П" + }, + "capsOption" : { + "output" : "»" + }, + "keyCode" : 35, + "option" : { + "output" : "»" + }, + "shift" : { + "output" : "П" + }, + "shiftCaps" : { + "output" : "П" + }, + "shiftCapsOption" : { + "output" : "’" + }, + "shiftOption" : { + "output" : "’" + }, + "unmodified" : { + "output" : "п" + } + }, + "36" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 36, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "37" : { + "caps" : { + "output" : "Л" + }, + "capsOption" : { + "output" : "љ" + }, + "keyCode" : 37, + "option" : { + "output" : "љ" + }, + "shift" : { + "output" : "Л" + }, + "shiftCaps" : { + "output" : "Л" + }, + "shiftCapsOption" : { + "output" : "Љ" + }, + "shiftOption" : { + "output" : "Љ" + }, + "unmodified" : { + "output" : "л" + } + }, + "38" : { + "caps" : { + "output" : "Й" + }, + "capsOption" : { + "output" : "ј" + }, + "keyCode" : 38, + "option" : { + "output" : "ј" + }, + "shift" : { + "output" : "Й" + }, + "shiftCaps" : { + "output" : "Й" + }, + "shiftCapsOption" : { + "output" : "Ј" + }, + "shiftOption" : { + "output" : "Ј" + }, + "unmodified" : { + "output" : "й" + } + }, + "39" : { + "caps" : { + "output" : "\"" + }, + "capsOption" : { + "output" : "’" + }, + "keyCode" : 39, + "option" : { + "output" : "’" + }, + "shift" : { + "output" : "\"" + }, + "shiftCaps" : { + "output" : "\"" + }, + "shiftCapsOption" : { + "output" : "`" + }, + "shiftOption" : { + "output" : "`" + }, + "unmodified" : { + "output" : "'" + } + }, + "4" : { + "caps" : { + "output" : "Ч" + }, + "capsOption" : { + "output" : "ґ" + }, + "keyCode" : 4, + "option" : { + "output" : "ґ" + }, + "shift" : { + "output" : "Ч" + }, + "shiftCaps" : { + "output" : "Ч" + }, + "shiftCapsOption" : { + "output" : "Ґ" + }, + "shiftOption" : { + "output" : "Ґ" + }, + "unmodified" : { + "output" : "ч" + } + }, + "40" : { + "caps" : { + "output" : "К" + }, + "capsOption" : { + "output" : "ќ" + }, + "keyCode" : 40, + "option" : { + "output" : "ќ" + }, + "shift" : { + "output" : "К" + }, + "shiftCaps" : { + "output" : "К" + }, + "shiftCapsOption" : { + "output" : "Ќ" + }, + "shiftOption" : { + "output" : "Ќ" + }, + "unmodified" : { + "output" : "к" + } + }, + "41" : { + "caps" : { + "output" : ":" + }, + "capsOption" : { + "output" : "„" + }, + "keyCode" : 41, + "option" : { + "output" : "„" + }, + "shift" : { + "output" : ":" + }, + "shiftCaps" : { + "output" : ":" + }, + "shiftCapsOption" : { + "output" : "’" + }, + "shiftOption" : { + "output" : "’" + }, + "unmodified" : { + "output" : ";" + } + }, + "42" : { + "caps" : { + "output" : "Э" + }, + "capsOption" : { + "output" : "є" + }, + "keyCode" : 42, + "option" : { + "output" : "є" + }, + "shift" : { + "output" : "Э" + }, + "shiftCaps" : { + "output" : "Э" + }, + "shiftCapsOption" : { + "output" : "Є" + }, + "shiftOption" : { + "output" : "Є" + }, + "unmodified" : { + "output" : "э" + } + }, + "43" : { + "caps" : { + "output" : "<" + }, + "capsOption" : { + "output" : "≤" + }, + "keyCode" : 43, + "option" : { + "output" : "≤" + }, + "shift" : { + "output" : "<" + }, + "shiftCaps" : { + "output" : "<" + }, + "shiftCapsOption" : { + "output" : "÷" + }, + "shiftOption" : { + "output" : "÷" + }, + "unmodified" : { + "output" : "," + } + }, + "44" : { + "caps" : { + "output" : "?" + }, + "capsOption" : { + "output" : "÷" + }, + "keyCode" : 44, + "option" : { + "output" : "÷" + }, + "shift" : { + "output" : "?" + }, + "shiftCaps" : { + "output" : "?" + }, + "shiftCapsOption" : { + "output" : "\\" + }, + "shiftOption" : { + "output" : "\\" + }, + "unmodified" : { + "output" : "\/" + } + }, + "45" : { + "caps" : { + "output" : "Н" + }, + "capsOption" : { + "output" : "њ" + }, + "keyCode" : 45, + "option" : { + "output" : "њ" + }, + "shift" : { + "output" : "Н" + }, + "shiftCaps" : { + "output" : "Н" + }, + "shiftCapsOption" : { + "output" : "Њ" + }, + "shiftOption" : { + "output" : "Њ" + }, + "unmodified" : { + "output" : "н" + } + }, + "46" : { + "caps" : { + "output" : "М" + }, + "capsOption" : { + "output" : "µ" + }, + "keyCode" : 46, + "option" : { + "output" : "µ" + }, + "shift" : { + "output" : "М" + }, + "shiftCaps" : { + "output" : "М" + }, + "shiftCapsOption" : { + "output" : "µ" + }, + "shiftOption" : { + "output" : "µ" + }, + "unmodified" : { + "output" : "м" + } + }, + "47" : { + "caps" : { + "output" : ">" + }, + "capsOption" : { + "output" : "≥" + }, + "keyCode" : 47, + "option" : { + "output" : "≥" + }, + "shift" : { + "output" : ">" + }, + "shiftCaps" : { + "output" : ">" + }, + "shiftCapsOption" : { + "output" : "…" + }, + "shiftOption" : { + "output" : "…" + }, + "unmodified" : { + "output" : "." + } + }, + "48" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 48, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "49" : { + "caps" : { + "output" : " " + }, + "capsOption" : { + "output" : " " + }, + "keyCode" : 49, + "option" : { + "output" : " " + }, + "shift" : { + "output" : " " + }, + "shiftCaps" : { + "output" : " " + }, + "shiftCapsOption" : { + "output" : " " + }, + "shiftOption" : { + "output" : " " + }, + "unmodified" : { + "output" : " " + } + }, + "5" : { + "caps" : { + "output" : "Г" + }, + "capsOption" : { + "output" : "ѓ" + }, + "keyCode" : 5, + "option" : { + "output" : "ѓ" + }, + "shift" : { + "output" : "Г" + }, + "shiftCaps" : { + "output" : "Г" + }, + "shiftCapsOption" : { + "output" : "Ѓ" + }, + "shiftOption" : { + "output" : "Ѓ" + }, + "unmodified" : { + "output" : "г" + } + }, + "50" : { + "caps" : { + "output" : "Щ" + }, + "capsOption" : { + "output" : "†" + }, + "keyCode" : 50, + "option" : { + "output" : "†" + }, + "shift" : { + "output" : "Щ" + }, + "shiftCaps" : { + "output" : "Щ" + }, + "shiftCapsOption" : { + "output" : "`" + }, + "shiftOption" : { + "output" : "`" + }, + "unmodified" : { + "output" : "щ" + } + }, + "51" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 51, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "52" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 52, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "53" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 53, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "54" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 54, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "55" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 55, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "56" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 56, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "57" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 57, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "58" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 58, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "59" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 59, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "6" : { + "caps" : { + "output" : "З" + }, + "capsOption" : { + "output" : "џ" + }, + "keyCode" : 6, + "option" : { + "output" : "џ" + }, + "shift" : { + "output" : "З" + }, + "shiftCaps" : { + "output" : "З" + }, + "shiftCapsOption" : { + "output" : "Џ" + }, + "shiftOption" : { + "output" : "Џ" + }, + "unmodified" : { + "output" : "з" + } + }, + "60" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 60, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "61" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 61, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "62" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 62, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "63" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 63, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "64" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 64, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "65" : { + "caps" : { + "output" : "," + }, + "capsOption" : { + "output" : "," + }, + "keyCode" : 65, + "option" : { + "output" : "," + }, + "shift" : { + "output" : "," + }, + "shiftCaps" : { + "output" : "," + }, + "shiftCapsOption" : { + "output" : "," + }, + "shiftOption" : { + "output" : "," + }, + "unmodified" : { + "output" : "," + } + }, + "66" : { + "caps" : { + "output" : "*" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 66, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "*" + }, + "shiftCaps" : { + "output" : "*" + }, + "shiftCapsOption" : { + "output" : "*" + }, + "shiftOption" : { + "output" : "*" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "67" : { + "caps" : { + "output" : "*" + }, + "capsOption" : { + "output" : "*" + }, + "keyCode" : 67, + "option" : { + "output" : "*" + }, + "shift" : { + "output" : "*" + }, + "shiftCaps" : { + "output" : "*" + }, + "shiftCapsOption" : { + "output" : "*" + }, + "shiftOption" : { + "output" : "*" + }, + "unmodified" : { + "output" : "*" + } + }, + "68" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 68, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "69" : { + "caps" : { + "output" : "+" + }, + "capsOption" : { + "output" : "+" + }, + "keyCode" : 69, + "option" : { + "output" : "+" + }, + "shift" : { + "output" : "+" + }, + "shiftCaps" : { + "output" : "+" + }, + "shiftCapsOption" : { + "output" : "+" + }, + "shiftOption" : { + "output" : "+" + }, + "unmodified" : { + "output" : "+" + } + }, + "7" : { + "caps" : { + "output" : "Х" + }, + "capsOption" : { + "output" : "≈" + }, + "keyCode" : 7, + "option" : { + "output" : "≈" + }, + "shift" : { + "output" : "Х" + }, + "shiftCaps" : { + "output" : "Х" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "output" : "х" + } + }, + "70" : { + "caps" : { + "output" : "+" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 70, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "+" + }, + "shiftCaps" : { + "output" : "+" + }, + "shiftCapsOption" : { + "output" : "+" + }, + "shiftOption" : { + "output" : "+" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "71" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 71, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "72" : { + "caps" : { + "output" : "=" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 72, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "=" + }, + "shiftCaps" : { + "output" : "=" + }, + "shiftCapsOption" : { + "output" : "=" + }, + "shiftOption" : { + "output" : "=" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "73" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 73, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "74" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 74, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "75" : { + "caps" : { + "output" : "\/" + }, + "capsOption" : { + "output" : "\/" + }, + "keyCode" : 75, + "option" : { + "output" : "\/" + }, + "shift" : { + "output" : "\/" + }, + "shiftCaps" : { + "output" : "\/" + }, + "shiftCapsOption" : { + "output" : "\/" + }, + "shiftOption" : { + "output" : "\/" + }, + "unmodified" : { + "output" : "\/" + } + }, + "76" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 76, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "77" : { + "caps" : { + "output" : "\/" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 77, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "\/" + }, + "shiftCaps" : { + "output" : "\/" + }, + "shiftCapsOption" : { + "output" : "\/" + }, + "shiftOption" : { + "output" : "\/" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "78" : { + "caps" : { + "output" : "-" + }, + "capsOption" : { + "output" : "-" + }, + "keyCode" : 78, + "option" : { + "output" : "-" + }, + "shift" : { + "output" : "-" + }, + "shiftCaps" : { + "output" : "-" + }, + "shiftCapsOption" : { + "output" : "-" + }, + "shiftOption" : { + "output" : "-" + }, + "unmodified" : { + "output" : "-" + } + }, + "79" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 79, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "8" : { + "caps" : { + "output" : "Ц" + }, + "capsOption" : { + "output" : "©" + }, + "keyCode" : 8, + "option" : { + "output" : "©" + }, + "shift" : { + "output" : "Ц" + }, + "shiftCaps" : { + "output" : "Ц" + }, + "shiftCapsOption" : { + "output" : "©" + }, + "shiftOption" : { + "output" : "©" + }, + "unmodified" : { + "output" : "ц" + } + }, + "80" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 80, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "81" : { + "caps" : { + "output" : "=" + }, + "capsOption" : { + "output" : "=" + }, + "keyCode" : 81, + "option" : { + "output" : "=" + }, + "shift" : { + "output" : "=" + }, + "shiftCaps" : { + "output" : "=" + }, + "shiftCapsOption" : { + "output" : "=" + }, + "shiftOption" : { + "output" : "=" + }, + "unmodified" : { + "output" : "=" + } + }, + "82" : { + "caps" : { + "output" : "0" + }, + "capsOption" : { + "output" : "0" + }, + "keyCode" : 82, + "option" : { + "output" : "0" + }, + "shift" : { + "output" : "0" + }, + "shiftCaps" : { + "output" : "0" + }, + "shiftCapsOption" : { + "output" : "0" + }, + "shiftOption" : { + "output" : "0" + }, + "unmodified" : { + "output" : "0" + } + }, + "83" : { + "caps" : { + "output" : "1" + }, + "capsOption" : { + "output" : "1" + }, + "keyCode" : 83, + "option" : { + "output" : "1" + }, + "shift" : { + "output" : "1" + }, + "shiftCaps" : { + "output" : "1" + }, + "shiftCapsOption" : { + "output" : "1" + }, + "shiftOption" : { + "output" : "1" + }, + "unmodified" : { + "output" : "1" + } + }, + "84" : { + "caps" : { + "output" : "2" + }, + "capsOption" : { + "output" : "2" + }, + "keyCode" : 84, + "option" : { + "output" : "2" + }, + "shift" : { + "output" : "2" + }, + "shiftCaps" : { + "output" : "2" + }, + "shiftCapsOption" : { + "output" : "2" + }, + "shiftOption" : { + "output" : "2" + }, + "unmodified" : { + "output" : "2" + } + }, + "85" : { + "caps" : { + "output" : "3" + }, + "capsOption" : { + "output" : "3" + }, + "keyCode" : 85, + "option" : { + "output" : "3" + }, + "shift" : { + "output" : "3" + }, + "shiftCaps" : { + "output" : "3" + }, + "shiftCapsOption" : { + "output" : "3" + }, + "shiftOption" : { + "output" : "3" + }, + "unmodified" : { + "output" : "3" + } + }, + "86" : { + "caps" : { + "output" : "4" + }, + "capsOption" : { + "output" : "4" + }, + "keyCode" : 86, + "option" : { + "output" : "4" + }, + "shift" : { + "output" : "4" + }, + "shiftCaps" : { + "output" : "4" + }, + "shiftCapsOption" : { + "output" : "4" + }, + "shiftOption" : { + "output" : "4" + }, + "unmodified" : { + "output" : "4" + } + }, + "87" : { + "caps" : { + "output" : "5" + }, + "capsOption" : { + "output" : "5" + }, + "keyCode" : 87, + "option" : { + "output" : "5" + }, + "shift" : { + "output" : "5" + }, + "shiftCaps" : { + "output" : "5" + }, + "shiftCapsOption" : { + "output" : "5" + }, + "shiftOption" : { + "output" : "5" + }, + "unmodified" : { + "output" : "5" + } + }, + "88" : { + "caps" : { + "output" : "6" + }, + "capsOption" : { + "output" : "6" + }, + "keyCode" : 88, + "option" : { + "output" : "6" + }, + "shift" : { + "output" : "6" + }, + "shiftCaps" : { + "output" : "6" + }, + "shiftCapsOption" : { + "output" : "6" + }, + "shiftOption" : { + "output" : "6" + }, + "unmodified" : { + "output" : "6" + } + }, + "89" : { + "caps" : { + "output" : "7" + }, + "capsOption" : { + "output" : "7" + }, + "keyCode" : 89, + "option" : { + "output" : "7" + }, + "shift" : { + "output" : "7" + }, + "shiftCaps" : { + "output" : "7" + }, + "shiftCapsOption" : { + "output" : "7" + }, + "shiftOption" : { + "output" : "7" + }, + "unmodified" : { + "output" : "7" + } + }, + "9" : { + "caps" : { + "output" : "В" + }, + "capsOption" : { + "output" : "√" + }, + "keyCode" : 9, + "option" : { + "output" : "√" + }, + "shift" : { + "output" : "В" + }, + "shiftCaps" : { + "output" : "В" + }, + "shiftCapsOption" : { + "output" : "†" + }, + "shiftOption" : { + "output" : "†" + }, + "unmodified" : { + "output" : "в" + } + }, + "90" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 90, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "91" : { + "caps" : { + "output" : "8" + }, + "capsOption" : { + "output" : "8" + }, + "keyCode" : 91, + "option" : { + "output" : "8" + }, + "shift" : { + "output" : "8" + }, + "shiftCaps" : { + "output" : "8" + }, + "shiftCapsOption" : { + "output" : "8" + }, + "shiftOption" : { + "output" : "8" + }, + "unmodified" : { + "output" : "8" + } + }, + "92" : { + "caps" : { + "output" : "9" + }, + "capsOption" : { + "output" : "9" + }, + "keyCode" : 92, + "option" : { + "output" : "9" + }, + "shift" : { + "output" : "9" + }, + "shiftCaps" : { + "output" : "9" + }, + "shiftCapsOption" : { + "output" : "9" + }, + "shiftOption" : { + "output" : "9" + }, + "unmodified" : { + "output" : "9" + } + }, + "93" : { + "caps" : { + "output" : "<" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 93, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "output" : "<" + }, + "shiftCaps" : { + "output" : "<" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "output" : ">" + } + }, + "94" : { + "caps" : { + "output" : "Щ" + }, + "capsOption" : { + "output" : "†" + }, + "keyCode" : 94, + "option" : { + "output" : "†" + }, + "shift" : { + "output" : "Щ" + }, + "shiftCaps" : { + "output" : "Щ" + }, + "shiftCapsOption" : { + "output" : "`" + }, + "shiftOption" : { + "output" : "`" + }, + "unmodified" : { + "output" : "щ" + } + }, + "95" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 95, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "96" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 96, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "97" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 97, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "98" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 98, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "99" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 99, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + } + }, + "keyboardType" : 91, + "osBuild" : "24G830", + "osVersion" : "15.7.9", + "schemaVersion" : 1, + "sourceID" : "com.apple.keylayout.Russian-Phonetic" +} \ No newline at end of file diff --git a/Benchmarks/layout-tables/v1/com.apple.keylayout.Russian@37.json b/Benchmarks/layout-tables/v1/com.apple.keylayout.Russian@37.json new file mode 100644 index 0000000..e5aeb11 --- /dev/null +++ b/Benchmarks/layout-tables/v1/com.apple.keylayout.Russian@37.json @@ -0,0 +1,3465 @@ +{ + "keyOutputs" : { + "0" : { + "caps" : { + "output" : "Ф" + }, + "capsOption" : { + "output" : "ƒ" + }, + "keyCode" : 0, + "option" : { + "output" : "ƒ" + }, + "shift" : { + "output" : "Ф" + }, + "shiftCaps" : { + "output" : "Ф" + }, + "shiftCapsOption" : { + "output" : "ƒ" + }, + "shiftOption" : { + "output" : "ƒ" + }, + "unmodified" : { + "output" : "ф" + } + }, + "1" : { + "caps" : { + "output" : "Ы" + }, + "capsOption" : { + "output" : "ы" + }, + "keyCode" : 1, + "option" : { + "output" : "ы" + }, + "shift" : { + "output" : "Ы" + }, + "shiftCaps" : { + "output" : "Ы" + }, + "shiftCapsOption" : { + "output" : "Ы" + }, + "shiftOption" : { + "output" : "Ы" + }, + "unmodified" : { + "output" : "ы" + } + }, + "10" : { + "caps" : { + "output" : ">" + }, + "capsOption" : { + "output" : "§" + }, + "keyCode" : 10, + "option" : { + "output" : "§" + }, + "shift" : { + "output" : "<" + }, + "shiftCaps" : { + "output" : "<" + }, + "shiftCapsOption" : { + "output" : "±" + }, + "shiftOption" : { + "output" : "±" + }, + "unmodified" : { + "output" : ">" + } + }, + "100" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 100, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "101" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 101, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "102" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 102, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "103" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 103, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "104" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 104, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "105" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 105, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "106" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 106, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "107" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 107, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "108" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 108, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "109" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 109, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "11" : { + "caps" : { + "output" : "И" + }, + "capsOption" : { + "output" : "и" + }, + "keyCode" : 11, + "option" : { + "output" : "и" + }, + "shift" : { + "output" : "И" + }, + "shiftCaps" : { + "output" : "И" + }, + "shiftCapsOption" : { + "output" : "И" + }, + "shiftOption" : { + "output" : "И" + }, + "unmodified" : { + "output" : "и" + } + }, + "110" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 110, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "111" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 111, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "112" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 112, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "113" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 113, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "114" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 114, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "115" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 115, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "116" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 116, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "117" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 117, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "118" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 118, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "119" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 119, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "12" : { + "caps" : { + "output" : "Й" + }, + "capsOption" : { + "output" : "ј" + }, + "keyCode" : 12, + "option" : { + "output" : "ј" + }, + "shift" : { + "output" : "Й" + }, + "shiftCaps" : { + "output" : "Й" + }, + "shiftCapsOption" : { + "output" : "Ј" + }, + "shiftOption" : { + "output" : "Ј" + }, + "unmodified" : { + "output" : "й" + } + }, + "120" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 120, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "121" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 121, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "122" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 122, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "123" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 123, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "124" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 124, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "125" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 125, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "126" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 126, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "127" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 127, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "13" : { + "caps" : { + "output" : "Ц" + }, + "capsOption" : { + "output" : "џ" + }, + "keyCode" : 13, + "option" : { + "output" : "џ" + }, + "shift" : { + "output" : "Ц" + }, + "shiftCaps" : { + "output" : "Ц" + }, + "shiftCapsOption" : { + "output" : "Џ" + }, + "shiftOption" : { + "output" : "Џ" + }, + "unmodified" : { + "output" : "ц" + } + }, + "14" : { + "caps" : { + "output" : "У" + }, + "capsOption" : { + "output" : "ќ" + }, + "keyCode" : 14, + "option" : { + "output" : "ќ" + }, + "shift" : { + "output" : "У" + }, + "shiftCaps" : { + "output" : "У" + }, + "shiftCapsOption" : { + "output" : "Ќ" + }, + "shiftOption" : { + "output" : "Ќ" + }, + "unmodified" : { + "output" : "у" + } + }, + "15" : { + "caps" : { + "output" : "К" + }, + "capsOption" : { + "output" : "®" + }, + "keyCode" : 15, + "option" : { + "output" : "®" + }, + "shift" : { + "output" : "К" + }, + "shiftCaps" : { + "output" : "К" + }, + "shiftCapsOption" : { + "output" : "®" + }, + "shiftOption" : { + "output" : "®" + }, + "unmodified" : { + "output" : "к" + } + }, + "16" : { + "caps" : { + "output" : "Н" + }, + "capsOption" : { + "output" : "њ" + }, + "keyCode" : 16, + "option" : { + "output" : "њ" + }, + "shift" : { + "output" : "Н" + }, + "shiftCaps" : { + "output" : "Н" + }, + "shiftCapsOption" : { + "output" : "Њ" + }, + "shiftOption" : { + "output" : "Њ" + }, + "unmodified" : { + "output" : "н" + } + }, + "17" : { + "caps" : { + "output" : "Е" + }, + "capsOption" : { + "output" : "†" + }, + "keyCode" : 17, + "option" : { + "output" : "†" + }, + "shift" : { + "output" : "Е" + }, + "shiftCaps" : { + "output" : "Е" + }, + "shiftCapsOption" : { + "output" : "†" + }, + "shiftOption" : { + "output" : "†" + }, + "unmodified" : { + "output" : "е" + } + }, + "18" : { + "caps" : { + "output" : "1" + }, + "capsOption" : { + "output" : "!" + }, + "keyCode" : 18, + "option" : { + "output" : "!" + }, + "shift" : { + "output" : "!" + }, + "shiftCaps" : { + "output" : "!" + }, + "shiftCapsOption" : { + "output" : "|" + }, + "shiftOption" : { + "output" : "|" + }, + "unmodified" : { + "output" : "1" + } + }, + "19" : { + "caps" : { + "output" : "2" + }, + "capsOption" : { + "output" : "@" + }, + "keyCode" : 19, + "option" : { + "output" : "@" + }, + "shift" : { + "output" : "\"" + }, + "shiftCaps" : { + "output" : "\"" + }, + "shiftCapsOption" : { + "output" : "\"" + }, + "shiftOption" : { + "output" : "\"" + }, + "unmodified" : { + "output" : "2" + } + }, + "2" : { + "caps" : { + "output" : "В" + }, + "capsOption" : { + "output" : "ћ" + }, + "keyCode" : 2, + "option" : { + "output" : "ћ" + }, + "shift" : { + "output" : "В" + }, + "shiftCaps" : { + "output" : "В" + }, + "shiftCapsOption" : { + "output" : "Ћ" + }, + "shiftOption" : { + "output" : "Ћ" + }, + "unmodified" : { + "output" : "в" + } + }, + "20" : { + "caps" : { + "output" : "3" + }, + "capsOption" : { + "output" : "#" + }, + "keyCode" : 20, + "option" : { + "output" : "#" + }, + "shift" : { + "output" : "№" + }, + "shiftCaps" : { + "output" : "№" + }, + "shiftCapsOption" : { + "output" : "£" + }, + "shiftOption" : { + "output" : "£" + }, + "unmodified" : { + "output" : "3" + } + }, + "21" : { + "caps" : { + "output" : "4" + }, + "capsOption" : { + "output" : "$" + }, + "keyCode" : 21, + "option" : { + "output" : "$" + }, + "shift" : { + "output" : "%" + }, + "shiftCaps" : { + "output" : "%" + }, + "shiftCapsOption" : { + "output" : "€" + }, + "shiftOption" : { + "output" : "€" + }, + "unmodified" : { + "output" : "4" + } + }, + "22" : { + "caps" : { + "output" : "6" + }, + "capsOption" : { + "output" : "^" + }, + "keyCode" : 22, + "option" : { + "output" : "^" + }, + "shift" : { + "output" : "," + }, + "shiftCaps" : { + "output" : "," + }, + "shiftCapsOption" : { + "output" : "¬" + }, + "shiftOption" : { + "output" : "¬" + }, + "unmodified" : { + "output" : "6" + } + }, + "23" : { + "caps" : { + "output" : "5" + }, + "capsOption" : { + "output" : "%" + }, + "keyCode" : 23, + "option" : { + "output" : "%" + }, + "shift" : { + "output" : ":" + }, + "shiftCaps" : { + "output" : ":" + }, + "shiftCapsOption" : { + "output" : "∞" + }, + "shiftOption" : { + "output" : "∞" + }, + "unmodified" : { + "output" : "5" + } + }, + "24" : { + "caps" : { + "output" : "=" + }, + "capsOption" : { + "output" : "»" + }, + "keyCode" : 24, + "option" : { + "output" : "»" + }, + "shift" : { + "output" : "+" + }, + "shiftCaps" : { + "output" : "+" + }, + "shiftCapsOption" : { + "output" : "«" + }, + "shiftOption" : { + "output" : "«" + }, + "unmodified" : { + "output" : "=" + } + }, + "25" : { + "caps" : { + "output" : "9" + }, + "capsOption" : { + "output" : "{" + }, + "keyCode" : 25, + "option" : { + "output" : "{" + }, + "shift" : { + "output" : "(" + }, + "shiftCaps" : { + "output" : "(" + }, + "shiftCapsOption" : { + "output" : "'" + }, + "shiftOption" : { + "output" : "'" + }, + "unmodified" : { + "output" : "9" + } + }, + "26" : { + "caps" : { + "output" : "7" + }, + "capsOption" : { + "output" : "&" + }, + "keyCode" : 26, + "option" : { + "output" : "&" + }, + "shift" : { + "output" : "." + }, + "shiftCaps" : { + "output" : "." + }, + "shiftCapsOption" : { + "output" : "¶" + }, + "shiftOption" : { + "output" : "¶" + }, + "unmodified" : { + "output" : "7" + } + }, + "27" : { + "caps" : { + "output" : "-" + }, + "capsOption" : { + "output" : "–" + }, + "keyCode" : 27, + "option" : { + "output" : "–" + }, + "shift" : { + "output" : "_" + }, + "shiftCaps" : { + "output" : "_" + }, + "shiftCapsOption" : { + "output" : "—" + }, + "shiftOption" : { + "output" : "—" + }, + "unmodified" : { + "output" : "-" + } + }, + "28" : { + "caps" : { + "output" : "8" + }, + "capsOption" : { + "output" : "*" + }, + "keyCode" : 28, + "option" : { + "output" : "*" + }, + "shift" : { + "output" : ";" + }, + "shiftCaps" : { + "output" : ";" + }, + "shiftCapsOption" : { + "output" : "√" + }, + "shiftOption" : { + "output" : "√" + }, + "unmodified" : { + "output" : "8" + } + }, + "29" : { + "caps" : { + "output" : "0" + }, + "capsOption" : { + "output" : "}" + }, + "keyCode" : 29, + "option" : { + "output" : "}" + }, + "shift" : { + "output" : ")" + }, + "shiftCaps" : { + "output" : ")" + }, + "shiftCapsOption" : { + "output" : "`" + }, + "shiftOption" : { + "output" : "`" + }, + "unmodified" : { + "output" : "0" + } + }, + "3" : { + "caps" : { + "output" : "А" + }, + "capsOption" : { + "output" : "÷" + }, + "keyCode" : 3, + "option" : { + "output" : "÷" + }, + "shift" : { + "output" : "А" + }, + "shiftCaps" : { + "output" : "А" + }, + "shiftCapsOption" : { + "output" : "÷" + }, + "shiftOption" : { + "output" : "÷" + }, + "unmodified" : { + "output" : "а" + } + }, + "30" : { + "caps" : { + "output" : "Ъ" + }, + "capsOption" : { + "output" : "ъ" + }, + "keyCode" : 30, + "option" : { + "output" : "ъ" + }, + "shift" : { + "output" : "Ъ" + }, + "shiftCaps" : { + "output" : "Ъ" + }, + "shiftCapsOption" : { + "output" : "Ъ" + }, + "shiftOption" : { + "output" : "Ъ" + }, + "unmodified" : { + "output" : "ъ" + } + }, + "31" : { + "caps" : { + "output" : "Щ" + }, + "capsOption" : { + "output" : "ў" + }, + "keyCode" : 31, + "option" : { + "output" : "ў" + }, + "shift" : { + "output" : "Щ" + }, + "shiftCaps" : { + "output" : "Щ" + }, + "shiftCapsOption" : { + "output" : "Ў" + }, + "shiftOption" : { + "output" : "Ў" + }, + "unmodified" : { + "output" : "щ" + } + }, + "32" : { + "caps" : { + "output" : "Г" + }, + "capsOption" : { + "output" : "ѓ" + }, + "keyCode" : 32, + "option" : { + "output" : "ѓ" + }, + "shift" : { + "output" : "Г" + }, + "shiftCaps" : { + "output" : "Г" + }, + "shiftCapsOption" : { + "output" : "Ѓ" + }, + "shiftOption" : { + "output" : "Ѓ" + }, + "unmodified" : { + "output" : "г" + } + }, + "33" : { + "caps" : { + "output" : "Х" + }, + "capsOption" : { + "output" : "“" + }, + "keyCode" : 33, + "option" : { + "output" : "“" + }, + "shift" : { + "output" : "Х" + }, + "shiftCaps" : { + "output" : "Х" + }, + "shiftCapsOption" : { + "output" : "”" + }, + "shiftOption" : { + "output" : "”" + }, + "unmodified" : { + "output" : "х" + } + }, + "34" : { + "caps" : { + "output" : "Ш" + }, + "capsOption" : { + "output" : "ѕ" + }, + "keyCode" : 34, + "option" : { + "output" : "ѕ" + }, + "shift" : { + "output" : "Ш" + }, + "shiftCaps" : { + "output" : "Ш" + }, + "shiftCapsOption" : { + "output" : "Ѕ" + }, + "shiftOption" : { + "output" : "Ѕ" + }, + "unmodified" : { + "output" : "ш" + } + }, + "35" : { + "caps" : { + "output" : "З" + }, + "capsOption" : { + "output" : "‘" + }, + "keyCode" : 35, + "option" : { + "output" : "‘" + }, + "shift" : { + "output" : "З" + }, + "shiftCaps" : { + "output" : "З" + }, + "shiftCapsOption" : { + "output" : "’" + }, + "shiftOption" : { + "output" : "’" + }, + "unmodified" : { + "output" : "з" + } + }, + "36" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 36, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "37" : { + "caps" : { + "output" : "Д" + }, + "capsOption" : { + "output" : "∆" + }, + "keyCode" : 37, + "option" : { + "output" : "∆" + }, + "shift" : { + "output" : "Д" + }, + "shiftCaps" : { + "output" : "Д" + }, + "shiftCapsOption" : { + "output" : "∆" + }, + "shiftOption" : { + "output" : "∆" + }, + "unmodified" : { + "output" : "д" + } + }, + "38" : { + "caps" : { + "output" : "О" + }, + "capsOption" : { + "output" : "°" + }, + "keyCode" : 38, + "option" : { + "output" : "°" + }, + "shift" : { + "output" : "О" + }, + "shiftCaps" : { + "output" : "О" + }, + "shiftCapsOption" : { + "output" : "•" + }, + "shiftOption" : { + "output" : "•" + }, + "unmodified" : { + "output" : "о" + } + }, + "39" : { + "caps" : { + "output" : "Э" + }, + "capsOption" : { + "output" : "э" + }, + "keyCode" : 39, + "option" : { + "output" : "э" + }, + "shift" : { + "output" : "Э" + }, + "shiftCaps" : { + "output" : "Э" + }, + "shiftCapsOption" : { + "output" : "Э" + }, + "shiftOption" : { + "output" : "Э" + }, + "unmodified" : { + "output" : "э" + } + }, + "4" : { + "caps" : { + "output" : "Р" + }, + "capsOption" : { + "output" : "₽" + }, + "keyCode" : 4, + "option" : { + "output" : "₽" + }, + "shift" : { + "output" : "Р" + }, + "shiftCaps" : { + "output" : "Р" + }, + "shiftCapsOption" : { + "output" : "₽" + }, + "shiftOption" : { + "output" : "₽" + }, + "unmodified" : { + "output" : "р" + } + }, + "40" : { + "caps" : { + "output" : "Л" + }, + "capsOption" : { + "output" : "љ" + }, + "keyCode" : 40, + "option" : { + "output" : "љ" + }, + "shift" : { + "output" : "Л" + }, + "shiftCaps" : { + "output" : "Л" + }, + "shiftCapsOption" : { + "output" : "Љ" + }, + "shiftOption" : { + "output" : "Љ" + }, + "unmodified" : { + "output" : "л" + } + }, + "41" : { + "caps" : { + "output" : "Ж" + }, + "capsOption" : { + "output" : "…" + }, + "keyCode" : 41, + "option" : { + "output" : "…" + }, + "shift" : { + "output" : "Ж" + }, + "shiftCaps" : { + "output" : "Ж" + }, + "shiftCapsOption" : { + "output" : "…" + }, + "shiftOption" : { + "output" : "…" + }, + "unmodified" : { + "output" : "ж" + } + }, + "42" : { + "caps" : { + "output" : "Ё" + }, + "capsOption" : { + "output" : "ё" + }, + "keyCode" : 42, + "option" : { + "output" : "ё" + }, + "shift" : { + "output" : "Ё" + }, + "shiftCaps" : { + "output" : "Ё" + }, + "shiftCapsOption" : { + "output" : "Ё" + }, + "shiftOption" : { + "output" : "Ё" + }, + "unmodified" : { + "output" : "ё" + } + }, + "43" : { + "caps" : { + "output" : "Б" + }, + "capsOption" : { + "output" : "≤" + }, + "keyCode" : 43, + "option" : { + "output" : "≤" + }, + "shift" : { + "output" : "Б" + }, + "shiftCaps" : { + "output" : "Б" + }, + "shiftCapsOption" : { + "output" : "<" + }, + "shiftOption" : { + "output" : "<" + }, + "unmodified" : { + "output" : "б" + } + }, + "44" : { + "caps" : { + "output" : "\/" + }, + "capsOption" : { + "output" : "“" + }, + "keyCode" : 44, + "option" : { + "output" : "“" + }, + "shift" : { + "output" : "?" + }, + "shiftCaps" : { + "output" : "?" + }, + "shiftCapsOption" : { + "output" : "„" + }, + "shiftOption" : { + "output" : "„" + }, + "unmodified" : { + "output" : "\/" + } + }, + "45" : { + "caps" : { + "output" : "Т" + }, + "capsOption" : { + "output" : "™" + }, + "keyCode" : 45, + "option" : { + "output" : "™" + }, + "shift" : { + "output" : "Т" + }, + "shiftCaps" : { + "output" : "Т" + }, + "shiftCapsOption" : { + "output" : "™" + }, + "shiftOption" : { + "output" : "™" + }, + "unmodified" : { + "output" : "т" + } + }, + "46" : { + "caps" : { + "output" : "Ь" + }, + "capsOption" : { + "output" : "~" + }, + "keyCode" : 46, + "option" : { + "output" : "~" + }, + "shift" : { + "output" : "Ь" + }, + "shiftCaps" : { + "output" : "Ь" + }, + "shiftCapsOption" : { + "output" : "~" + }, + "shiftOption" : { + "output" : "~" + }, + "unmodified" : { + "output" : "ь" + } + }, + "47" : { + "caps" : { + "output" : "Ю" + }, + "capsOption" : { + "output" : "≥" + }, + "keyCode" : 47, + "option" : { + "output" : "≥" + }, + "shift" : { + "output" : "Ю" + }, + "shiftCaps" : { + "output" : "Ю" + }, + "shiftCapsOption" : { + "output" : ">" + }, + "shiftOption" : { + "output" : ">" + }, + "unmodified" : { + "output" : "ю" + } + }, + "48" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 48, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "49" : { + "caps" : { + "output" : " " + }, + "capsOption" : { + "output" : " " + }, + "keyCode" : 49, + "option" : { + "output" : " " + }, + "shift" : { + "output" : " " + }, + "shiftCaps" : { + "output" : " " + }, + "shiftCapsOption" : { + "output" : " " + }, + "shiftOption" : { + "output" : " " + }, + "unmodified" : { + "output" : " " + } + }, + "5" : { + "caps" : { + "output" : "П" + }, + "capsOption" : { + "output" : "©" + }, + "keyCode" : 5, + "option" : { + "output" : "©" + }, + "shift" : { + "output" : "П" + }, + "shiftCaps" : { + "output" : "П" + }, + "shiftCapsOption" : { + "output" : "©" + }, + "shiftOption" : { + "output" : "©" + }, + "unmodified" : { + "output" : "п" + } + }, + "50" : { + "caps" : { + "output" : "]" + }, + "capsOption" : { + "output" : "]" + }, + "keyCode" : 50, + "option" : { + "output" : "]" + }, + "shift" : { + "output" : "[" + }, + "shiftCaps" : { + "output" : "[" + }, + "shiftCapsOption" : { + "output" : "[" + }, + "shiftOption" : { + "output" : "[" + }, + "unmodified" : { + "output" : "]" + } + }, + "51" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 51, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "52" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 52, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "53" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 53, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "54" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 54, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "55" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 55, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "56" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 56, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "57" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 57, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "58" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 58, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "59" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 59, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "6" : { + "caps" : { + "output" : "Я" + }, + "capsOption" : { + "output" : "ђ" + }, + "keyCode" : 6, + "option" : { + "output" : "ђ" + }, + "shift" : { + "output" : "Я" + }, + "shiftCaps" : { + "output" : "Я" + }, + "shiftCapsOption" : { + "output" : "Ђ" + }, + "shiftOption" : { + "output" : "Ђ" + }, + "unmodified" : { + "output" : "я" + } + }, + "60" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 60, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "61" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 61, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "62" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 62, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "63" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 63, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "64" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 64, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "65" : { + "caps" : { + "output" : "," + }, + "capsOption" : { + "output" : "," + }, + "keyCode" : 65, + "option" : { + "output" : "," + }, + "shift" : { + "output" : "." + }, + "shiftCaps" : { + "output" : "." + }, + "shiftCapsOption" : { + "output" : "," + }, + "shiftOption" : { + "output" : "," + }, + "unmodified" : { + "output" : "," + } + }, + "66" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 66, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "*" + }, + "shiftCaps" : { + "output" : "*" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "67" : { + "caps" : { + "output" : "*" + }, + "capsOption" : { + "output" : "*" + }, + "keyCode" : 67, + "option" : { + "output" : "*" + }, + "shift" : { + "output" : "*" + }, + "shiftCaps" : { + "output" : "*" + }, + "shiftCapsOption" : { + "output" : "*" + }, + "shiftOption" : { + "output" : "*" + }, + "unmodified" : { + "output" : "*" + } + }, + "68" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 68, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "69" : { + "caps" : { + "output" : "+" + }, + "capsOption" : { + "output" : "+" + }, + "keyCode" : 69, + "option" : { + "output" : "+" + }, + "shift" : { + "output" : "+" + }, + "shiftCaps" : { + "output" : "+" + }, + "shiftCapsOption" : { + "output" : "+" + }, + "shiftOption" : { + "output" : "+" + }, + "unmodified" : { + "output" : "+" + } + }, + "7" : { + "caps" : { + "output" : "Ч" + }, + "capsOption" : { + "output" : "≈" + }, + "keyCode" : 7, + "option" : { + "output" : "≈" + }, + "shift" : { + "output" : "Ч" + }, + "shiftCaps" : { + "output" : "Ч" + }, + "shiftCapsOption" : { + "output" : "≈" + }, + "shiftOption" : { + "output" : "≈" + }, + "unmodified" : { + "output" : "ч" + } + }, + "70" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 70, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "+" + }, + "shiftCaps" : { + "output" : "+" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "71" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 71, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "72" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 72, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "=" + }, + "shiftCaps" : { + "output" : "=" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "73" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 73, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "74" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 74, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "75" : { + "caps" : { + "output" : "\/" + }, + "capsOption" : { + "output" : "\/" + }, + "keyCode" : 75, + "option" : { + "output" : "\/" + }, + "shift" : { + "output" : "\/" + }, + "shiftCaps" : { + "output" : "\/" + }, + "shiftCapsOption" : { + "output" : "\/" + }, + "shiftOption" : { + "output" : "\/" + }, + "unmodified" : { + "output" : "\/" + } + }, + "76" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 76, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "77" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 77, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "\/" + }, + "shiftCaps" : { + "output" : "\/" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "78" : { + "caps" : { + "output" : "-" + }, + "capsOption" : { + "output" : "-" + }, + "keyCode" : 78, + "option" : { + "output" : "-" + }, + "shift" : { + "output" : "-" + }, + "shiftCaps" : { + "output" : "-" + }, + "shiftCapsOption" : { + "output" : "-" + }, + "shiftOption" : { + "output" : "-" + }, + "unmodified" : { + "output" : "-" + } + }, + "79" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 79, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "8" : { + "caps" : { + "output" : "С" + }, + "capsOption" : { + "output" : "≠" + }, + "keyCode" : 8, + "option" : { + "output" : "≠" + }, + "shift" : { + "output" : "С" + }, + "shiftCaps" : { + "output" : "С" + }, + "shiftCapsOption" : { + "output" : "≠" + }, + "shiftOption" : { + "output" : "≠" + }, + "unmodified" : { + "output" : "с" + } + }, + "80" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 80, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "81" : { + "caps" : { + "output" : "=" + }, + "capsOption" : { + "output" : "=" + }, + "keyCode" : 81, + "option" : { + "output" : "=" + }, + "shift" : { + "output" : "=" + }, + "shiftCaps" : { + "output" : "=" + }, + "shiftCapsOption" : { + "output" : "=" + }, + "shiftOption" : { + "output" : "=" + }, + "unmodified" : { + "output" : "=" + } + }, + "82" : { + "caps" : { + "output" : "0" + }, + "capsOption" : { + "output" : "0" + }, + "keyCode" : 82, + "option" : { + "output" : "0" + }, + "shift" : { + "output" : "0" + }, + "shiftCaps" : { + "output" : "0" + }, + "shiftCapsOption" : { + "output" : "0" + }, + "shiftOption" : { + "output" : "0" + }, + "unmodified" : { + "output" : "0" + } + }, + "83" : { + "caps" : { + "output" : "1" + }, + "capsOption" : { + "output" : "1" + }, + "keyCode" : 83, + "option" : { + "output" : "1" + }, + "shift" : { + "output" : "1" + }, + "shiftCaps" : { + "output" : "1" + }, + "shiftCapsOption" : { + "output" : "1" + }, + "shiftOption" : { + "output" : "1" + }, + "unmodified" : { + "output" : "1" + } + }, + "84" : { + "caps" : { + "output" : "2" + }, + "capsOption" : { + "output" : "2" + }, + "keyCode" : 84, + "option" : { + "output" : "2" + }, + "shift" : { + "output" : "2" + }, + "shiftCaps" : { + "output" : "2" + }, + "shiftCapsOption" : { + "output" : "2" + }, + "shiftOption" : { + "output" : "2" + }, + "unmodified" : { + "output" : "2" + } + }, + "85" : { + "caps" : { + "output" : "3" + }, + "capsOption" : { + "output" : "3" + }, + "keyCode" : 85, + "option" : { + "output" : "3" + }, + "shift" : { + "output" : "3" + }, + "shiftCaps" : { + "output" : "3" + }, + "shiftCapsOption" : { + "output" : "3" + }, + "shiftOption" : { + "output" : "3" + }, + "unmodified" : { + "output" : "3" + } + }, + "86" : { + "caps" : { + "output" : "4" + }, + "capsOption" : { + "output" : "4" + }, + "keyCode" : 86, + "option" : { + "output" : "4" + }, + "shift" : { + "output" : "4" + }, + "shiftCaps" : { + "output" : "4" + }, + "shiftCapsOption" : { + "output" : "4" + }, + "shiftOption" : { + "output" : "4" + }, + "unmodified" : { + "output" : "4" + } + }, + "87" : { + "caps" : { + "output" : "5" + }, + "capsOption" : { + "output" : "5" + }, + "keyCode" : 87, + "option" : { + "output" : "5" + }, + "shift" : { + "output" : "5" + }, + "shiftCaps" : { + "output" : "5" + }, + "shiftCapsOption" : { + "output" : "5" + }, + "shiftOption" : { + "output" : "5" + }, + "unmodified" : { + "output" : "5" + } + }, + "88" : { + "caps" : { + "output" : "6" + }, + "capsOption" : { + "output" : "6" + }, + "keyCode" : 88, + "option" : { + "output" : "6" + }, + "shift" : { + "output" : "6" + }, + "shiftCaps" : { + "output" : "6" + }, + "shiftCapsOption" : { + "output" : "6" + }, + "shiftOption" : { + "output" : "6" + }, + "unmodified" : { + "output" : "6" + } + }, + "89" : { + "caps" : { + "output" : "7" + }, + "capsOption" : { + "output" : "7" + }, + "keyCode" : 89, + "option" : { + "output" : "7" + }, + "shift" : { + "output" : "7" + }, + "shiftCaps" : { + "output" : "7" + }, + "shiftCapsOption" : { + "output" : "7" + }, + "shiftOption" : { + "output" : "7" + }, + "unmodified" : { + "output" : "7" + } + }, + "9" : { + "caps" : { + "output" : "М" + }, + "capsOption" : { + "output" : "µ" + }, + "keyCode" : 9, + "option" : { + "output" : "µ" + }, + "shift" : { + "output" : "М" + }, + "shiftCaps" : { + "output" : "М" + }, + "shiftCapsOption" : { + "output" : "µ" + }, + "shiftOption" : { + "output" : "µ" + }, + "unmodified" : { + "output" : "м" + } + }, + "90" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 90, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "91" : { + "caps" : { + "output" : "8" + }, + "capsOption" : { + "output" : "8" + }, + "keyCode" : 91, + "option" : { + "output" : "8" + }, + "shift" : { + "output" : "8" + }, + "shiftCaps" : { + "output" : "8" + }, + "shiftCapsOption" : { + "output" : "8" + }, + "shiftOption" : { + "output" : "8" + }, + "unmodified" : { + "output" : "8" + } + }, + "92" : { + "caps" : { + "output" : "9" + }, + "capsOption" : { + "output" : "9" + }, + "keyCode" : 92, + "option" : { + "output" : "9" + }, + "shift" : { + "output" : "9" + }, + "shiftCaps" : { + "output" : "9" + }, + "shiftCapsOption" : { + "output" : "9" + }, + "shiftOption" : { + "output" : "9" + }, + "unmodified" : { + "output" : "9" + } + }, + "93" : { + "caps" : { + "output" : ">" + }, + "capsOption" : { + "output" : "§" + }, + "keyCode" : 93, + "option" : { + "output" : "§" + }, + "shift" : { + "output" : "<" + }, + "shiftCaps" : { + "output" : "<" + }, + "shiftCapsOption" : { + "output" : "±" + }, + "shiftOption" : { + "output" : "±" + }, + "unmodified" : { + "output" : ">" + } + }, + "94" : { + "caps" : { + "output" : "]" + }, + "capsOption" : { + "output" : "]" + }, + "keyCode" : 94, + "option" : { + "output" : "]" + }, + "shift" : { + "output" : "[" + }, + "shiftCaps" : { + "output" : "[" + }, + "shiftCapsOption" : { + "output" : "[" + }, + "shiftOption" : { + "output" : "[" + }, + "unmodified" : { + "output" : "]" + } + }, + "95" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 95, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "96" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 96, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "97" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 97, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "98" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 98, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "99" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 99, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + } + }, + "keyboardType" : 37, + "osBuild" : "24G830", + "osVersion" : "15.7.9", + "schemaVersion" : 1, + "sourceID" : "com.apple.keylayout.Russian" +} \ No newline at end of file diff --git a/Benchmarks/layout-tables/v1/com.apple.keylayout.Russian@91.json b/Benchmarks/layout-tables/v1/com.apple.keylayout.Russian@91.json new file mode 100644 index 0000000..0b8a942 --- /dev/null +++ b/Benchmarks/layout-tables/v1/com.apple.keylayout.Russian@91.json @@ -0,0 +1,3465 @@ +{ + "keyOutputs" : { + "0" : { + "caps" : { + "output" : "Ф" + }, + "capsOption" : { + "output" : "ƒ" + }, + "keyCode" : 0, + "option" : { + "output" : "ƒ" + }, + "shift" : { + "output" : "Ф" + }, + "shiftCaps" : { + "output" : "Ф" + }, + "shiftCapsOption" : { + "output" : "ƒ" + }, + "shiftOption" : { + "output" : "ƒ" + }, + "unmodified" : { + "output" : "ф" + } + }, + "1" : { + "caps" : { + "output" : "Ы" + }, + "capsOption" : { + "output" : "ы" + }, + "keyCode" : 1, + "option" : { + "output" : "ы" + }, + "shift" : { + "output" : "Ы" + }, + "shiftCaps" : { + "output" : "Ы" + }, + "shiftCapsOption" : { + "output" : "Ы" + }, + "shiftOption" : { + "output" : "Ы" + }, + "unmodified" : { + "output" : "ы" + } + }, + "10" : { + "caps" : { + "output" : ">" + }, + "capsOption" : { + "output" : "§" + }, + "keyCode" : 10, + "option" : { + "output" : "§" + }, + "shift" : { + "output" : "<" + }, + "shiftCaps" : { + "output" : "<" + }, + "shiftCapsOption" : { + "output" : "±" + }, + "shiftOption" : { + "output" : "±" + }, + "unmodified" : { + "output" : ">" + } + }, + "100" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 100, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "101" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 101, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "102" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 102, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "103" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 103, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "104" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 104, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "105" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 105, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "106" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 106, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "107" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 107, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "108" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 108, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "109" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 109, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "11" : { + "caps" : { + "output" : "И" + }, + "capsOption" : { + "output" : "и" + }, + "keyCode" : 11, + "option" : { + "output" : "и" + }, + "shift" : { + "output" : "И" + }, + "shiftCaps" : { + "output" : "И" + }, + "shiftCapsOption" : { + "output" : "И" + }, + "shiftOption" : { + "output" : "И" + }, + "unmodified" : { + "output" : "и" + } + }, + "110" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 110, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "111" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 111, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "112" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 112, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "113" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 113, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "114" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 114, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "115" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 115, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "116" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 116, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "117" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 117, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "118" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 118, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "119" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 119, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "12" : { + "caps" : { + "output" : "Й" + }, + "capsOption" : { + "output" : "ј" + }, + "keyCode" : 12, + "option" : { + "output" : "ј" + }, + "shift" : { + "output" : "Й" + }, + "shiftCaps" : { + "output" : "Й" + }, + "shiftCapsOption" : { + "output" : "Ј" + }, + "shiftOption" : { + "output" : "Ј" + }, + "unmodified" : { + "output" : "й" + } + }, + "120" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 120, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "121" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 121, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "122" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 122, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "123" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 123, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "124" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 124, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "125" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 125, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "126" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 126, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "127" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 127, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "13" : { + "caps" : { + "output" : "Ц" + }, + "capsOption" : { + "output" : "џ" + }, + "keyCode" : 13, + "option" : { + "output" : "џ" + }, + "shift" : { + "output" : "Ц" + }, + "shiftCaps" : { + "output" : "Ц" + }, + "shiftCapsOption" : { + "output" : "Џ" + }, + "shiftOption" : { + "output" : "Џ" + }, + "unmodified" : { + "output" : "ц" + } + }, + "14" : { + "caps" : { + "output" : "У" + }, + "capsOption" : { + "output" : "ќ" + }, + "keyCode" : 14, + "option" : { + "output" : "ќ" + }, + "shift" : { + "output" : "У" + }, + "shiftCaps" : { + "output" : "У" + }, + "shiftCapsOption" : { + "output" : "Ќ" + }, + "shiftOption" : { + "output" : "Ќ" + }, + "unmodified" : { + "output" : "у" + } + }, + "15" : { + "caps" : { + "output" : "К" + }, + "capsOption" : { + "output" : "®" + }, + "keyCode" : 15, + "option" : { + "output" : "®" + }, + "shift" : { + "output" : "К" + }, + "shiftCaps" : { + "output" : "К" + }, + "shiftCapsOption" : { + "output" : "®" + }, + "shiftOption" : { + "output" : "®" + }, + "unmodified" : { + "output" : "к" + } + }, + "16" : { + "caps" : { + "output" : "Н" + }, + "capsOption" : { + "output" : "њ" + }, + "keyCode" : 16, + "option" : { + "output" : "њ" + }, + "shift" : { + "output" : "Н" + }, + "shiftCaps" : { + "output" : "Н" + }, + "shiftCapsOption" : { + "output" : "Њ" + }, + "shiftOption" : { + "output" : "Њ" + }, + "unmodified" : { + "output" : "н" + } + }, + "17" : { + "caps" : { + "output" : "Е" + }, + "capsOption" : { + "output" : "†" + }, + "keyCode" : 17, + "option" : { + "output" : "†" + }, + "shift" : { + "output" : "Е" + }, + "shiftCaps" : { + "output" : "Е" + }, + "shiftCapsOption" : { + "output" : "†" + }, + "shiftOption" : { + "output" : "†" + }, + "unmodified" : { + "output" : "е" + } + }, + "18" : { + "caps" : { + "output" : "1" + }, + "capsOption" : { + "output" : "!" + }, + "keyCode" : 18, + "option" : { + "output" : "!" + }, + "shift" : { + "output" : "!" + }, + "shiftCaps" : { + "output" : "!" + }, + "shiftCapsOption" : { + "output" : "|" + }, + "shiftOption" : { + "output" : "|" + }, + "unmodified" : { + "output" : "1" + } + }, + "19" : { + "caps" : { + "output" : "2" + }, + "capsOption" : { + "output" : "@" + }, + "keyCode" : 19, + "option" : { + "output" : "@" + }, + "shift" : { + "output" : "\"" + }, + "shiftCaps" : { + "output" : "\"" + }, + "shiftCapsOption" : { + "output" : "\"" + }, + "shiftOption" : { + "output" : "\"" + }, + "unmodified" : { + "output" : "2" + } + }, + "2" : { + "caps" : { + "output" : "В" + }, + "capsOption" : { + "output" : "ћ" + }, + "keyCode" : 2, + "option" : { + "output" : "ћ" + }, + "shift" : { + "output" : "В" + }, + "shiftCaps" : { + "output" : "В" + }, + "shiftCapsOption" : { + "output" : "Ћ" + }, + "shiftOption" : { + "output" : "Ћ" + }, + "unmodified" : { + "output" : "в" + } + }, + "20" : { + "caps" : { + "output" : "3" + }, + "capsOption" : { + "output" : "#" + }, + "keyCode" : 20, + "option" : { + "output" : "#" + }, + "shift" : { + "output" : "№" + }, + "shiftCaps" : { + "output" : "№" + }, + "shiftCapsOption" : { + "output" : "£" + }, + "shiftOption" : { + "output" : "£" + }, + "unmodified" : { + "output" : "3" + } + }, + "21" : { + "caps" : { + "output" : "4" + }, + "capsOption" : { + "output" : "$" + }, + "keyCode" : 21, + "option" : { + "output" : "$" + }, + "shift" : { + "output" : "%" + }, + "shiftCaps" : { + "output" : "%" + }, + "shiftCapsOption" : { + "output" : "€" + }, + "shiftOption" : { + "output" : "€" + }, + "unmodified" : { + "output" : "4" + } + }, + "22" : { + "caps" : { + "output" : "6" + }, + "capsOption" : { + "output" : "^" + }, + "keyCode" : 22, + "option" : { + "output" : "^" + }, + "shift" : { + "output" : "," + }, + "shiftCaps" : { + "output" : "," + }, + "shiftCapsOption" : { + "output" : "¬" + }, + "shiftOption" : { + "output" : "¬" + }, + "unmodified" : { + "output" : "6" + } + }, + "23" : { + "caps" : { + "output" : "5" + }, + "capsOption" : { + "output" : "%" + }, + "keyCode" : 23, + "option" : { + "output" : "%" + }, + "shift" : { + "output" : ":" + }, + "shiftCaps" : { + "output" : ":" + }, + "shiftCapsOption" : { + "output" : "∞" + }, + "shiftOption" : { + "output" : "∞" + }, + "unmodified" : { + "output" : "5" + } + }, + "24" : { + "caps" : { + "output" : "=" + }, + "capsOption" : { + "output" : "»" + }, + "keyCode" : 24, + "option" : { + "output" : "»" + }, + "shift" : { + "output" : "+" + }, + "shiftCaps" : { + "output" : "+" + }, + "shiftCapsOption" : { + "output" : "«" + }, + "shiftOption" : { + "output" : "«" + }, + "unmodified" : { + "output" : "=" + } + }, + "25" : { + "caps" : { + "output" : "9" + }, + "capsOption" : { + "output" : "{" + }, + "keyCode" : 25, + "option" : { + "output" : "{" + }, + "shift" : { + "output" : "(" + }, + "shiftCaps" : { + "output" : "(" + }, + "shiftCapsOption" : { + "output" : "'" + }, + "shiftOption" : { + "output" : "'" + }, + "unmodified" : { + "output" : "9" + } + }, + "26" : { + "caps" : { + "output" : "7" + }, + "capsOption" : { + "output" : "&" + }, + "keyCode" : 26, + "option" : { + "output" : "&" + }, + "shift" : { + "output" : "." + }, + "shiftCaps" : { + "output" : "." + }, + "shiftCapsOption" : { + "output" : "¶" + }, + "shiftOption" : { + "output" : "¶" + }, + "unmodified" : { + "output" : "7" + } + }, + "27" : { + "caps" : { + "output" : "-" + }, + "capsOption" : { + "output" : "–" + }, + "keyCode" : 27, + "option" : { + "output" : "–" + }, + "shift" : { + "output" : "_" + }, + "shiftCaps" : { + "output" : "_" + }, + "shiftCapsOption" : { + "output" : "—" + }, + "shiftOption" : { + "output" : "—" + }, + "unmodified" : { + "output" : "-" + } + }, + "28" : { + "caps" : { + "output" : "8" + }, + "capsOption" : { + "output" : "*" + }, + "keyCode" : 28, + "option" : { + "output" : "*" + }, + "shift" : { + "output" : ";" + }, + "shiftCaps" : { + "output" : ";" + }, + "shiftCapsOption" : { + "output" : "√" + }, + "shiftOption" : { + "output" : "√" + }, + "unmodified" : { + "output" : "8" + } + }, + "29" : { + "caps" : { + "output" : "0" + }, + "capsOption" : { + "output" : "}" + }, + "keyCode" : 29, + "option" : { + "output" : "}" + }, + "shift" : { + "output" : ")" + }, + "shiftCaps" : { + "output" : ")" + }, + "shiftCapsOption" : { + "output" : "`" + }, + "shiftOption" : { + "output" : "`" + }, + "unmodified" : { + "output" : "0" + } + }, + "3" : { + "caps" : { + "output" : "А" + }, + "capsOption" : { + "output" : "÷" + }, + "keyCode" : 3, + "option" : { + "output" : "÷" + }, + "shift" : { + "output" : "А" + }, + "shiftCaps" : { + "output" : "А" + }, + "shiftCapsOption" : { + "output" : "÷" + }, + "shiftOption" : { + "output" : "÷" + }, + "unmodified" : { + "output" : "а" + } + }, + "30" : { + "caps" : { + "output" : "Ъ" + }, + "capsOption" : { + "output" : "ъ" + }, + "keyCode" : 30, + "option" : { + "output" : "ъ" + }, + "shift" : { + "output" : "Ъ" + }, + "shiftCaps" : { + "output" : "Ъ" + }, + "shiftCapsOption" : { + "output" : "Ъ" + }, + "shiftOption" : { + "output" : "Ъ" + }, + "unmodified" : { + "output" : "ъ" + } + }, + "31" : { + "caps" : { + "output" : "Щ" + }, + "capsOption" : { + "output" : "ў" + }, + "keyCode" : 31, + "option" : { + "output" : "ў" + }, + "shift" : { + "output" : "Щ" + }, + "shiftCaps" : { + "output" : "Щ" + }, + "shiftCapsOption" : { + "output" : "Ў" + }, + "shiftOption" : { + "output" : "Ў" + }, + "unmodified" : { + "output" : "щ" + } + }, + "32" : { + "caps" : { + "output" : "Г" + }, + "capsOption" : { + "output" : "ѓ" + }, + "keyCode" : 32, + "option" : { + "output" : "ѓ" + }, + "shift" : { + "output" : "Г" + }, + "shiftCaps" : { + "output" : "Г" + }, + "shiftCapsOption" : { + "output" : "Ѓ" + }, + "shiftOption" : { + "output" : "Ѓ" + }, + "unmodified" : { + "output" : "г" + } + }, + "33" : { + "caps" : { + "output" : "Х" + }, + "capsOption" : { + "output" : "“" + }, + "keyCode" : 33, + "option" : { + "output" : "“" + }, + "shift" : { + "output" : "Х" + }, + "shiftCaps" : { + "output" : "Х" + }, + "shiftCapsOption" : { + "output" : "”" + }, + "shiftOption" : { + "output" : "”" + }, + "unmodified" : { + "output" : "х" + } + }, + "34" : { + "caps" : { + "output" : "Ш" + }, + "capsOption" : { + "output" : "ѕ" + }, + "keyCode" : 34, + "option" : { + "output" : "ѕ" + }, + "shift" : { + "output" : "Ш" + }, + "shiftCaps" : { + "output" : "Ш" + }, + "shiftCapsOption" : { + "output" : "Ѕ" + }, + "shiftOption" : { + "output" : "Ѕ" + }, + "unmodified" : { + "output" : "ш" + } + }, + "35" : { + "caps" : { + "output" : "З" + }, + "capsOption" : { + "output" : "‘" + }, + "keyCode" : 35, + "option" : { + "output" : "‘" + }, + "shift" : { + "output" : "З" + }, + "shiftCaps" : { + "output" : "З" + }, + "shiftCapsOption" : { + "output" : "’" + }, + "shiftOption" : { + "output" : "’" + }, + "unmodified" : { + "output" : "з" + } + }, + "36" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 36, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "37" : { + "caps" : { + "output" : "Д" + }, + "capsOption" : { + "output" : "∆" + }, + "keyCode" : 37, + "option" : { + "output" : "∆" + }, + "shift" : { + "output" : "Д" + }, + "shiftCaps" : { + "output" : "Д" + }, + "shiftCapsOption" : { + "output" : "∆" + }, + "shiftOption" : { + "output" : "∆" + }, + "unmodified" : { + "output" : "д" + } + }, + "38" : { + "caps" : { + "output" : "О" + }, + "capsOption" : { + "output" : "°" + }, + "keyCode" : 38, + "option" : { + "output" : "°" + }, + "shift" : { + "output" : "О" + }, + "shiftCaps" : { + "output" : "О" + }, + "shiftCapsOption" : { + "output" : "•" + }, + "shiftOption" : { + "output" : "•" + }, + "unmodified" : { + "output" : "о" + } + }, + "39" : { + "caps" : { + "output" : "Э" + }, + "capsOption" : { + "output" : "э" + }, + "keyCode" : 39, + "option" : { + "output" : "э" + }, + "shift" : { + "output" : "Э" + }, + "shiftCaps" : { + "output" : "Э" + }, + "shiftCapsOption" : { + "output" : "Э" + }, + "shiftOption" : { + "output" : "Э" + }, + "unmodified" : { + "output" : "э" + } + }, + "4" : { + "caps" : { + "output" : "Р" + }, + "capsOption" : { + "output" : "₽" + }, + "keyCode" : 4, + "option" : { + "output" : "₽" + }, + "shift" : { + "output" : "Р" + }, + "shiftCaps" : { + "output" : "Р" + }, + "shiftCapsOption" : { + "output" : "₽" + }, + "shiftOption" : { + "output" : "₽" + }, + "unmodified" : { + "output" : "р" + } + }, + "40" : { + "caps" : { + "output" : "Л" + }, + "capsOption" : { + "output" : "љ" + }, + "keyCode" : 40, + "option" : { + "output" : "љ" + }, + "shift" : { + "output" : "Л" + }, + "shiftCaps" : { + "output" : "Л" + }, + "shiftCapsOption" : { + "output" : "Љ" + }, + "shiftOption" : { + "output" : "Љ" + }, + "unmodified" : { + "output" : "л" + } + }, + "41" : { + "caps" : { + "output" : "Ж" + }, + "capsOption" : { + "output" : "…" + }, + "keyCode" : 41, + "option" : { + "output" : "…" + }, + "shift" : { + "output" : "Ж" + }, + "shiftCaps" : { + "output" : "Ж" + }, + "shiftCapsOption" : { + "output" : "…" + }, + "shiftOption" : { + "output" : "…" + }, + "unmodified" : { + "output" : "ж" + } + }, + "42" : { + "caps" : { + "output" : "Ё" + }, + "capsOption" : { + "output" : "ё" + }, + "keyCode" : 42, + "option" : { + "output" : "ё" + }, + "shift" : { + "output" : "Ё" + }, + "shiftCaps" : { + "output" : "Ё" + }, + "shiftCapsOption" : { + "output" : "Ё" + }, + "shiftOption" : { + "output" : "Ё" + }, + "unmodified" : { + "output" : "ё" + } + }, + "43" : { + "caps" : { + "output" : "Б" + }, + "capsOption" : { + "output" : "≤" + }, + "keyCode" : 43, + "option" : { + "output" : "≤" + }, + "shift" : { + "output" : "Б" + }, + "shiftCaps" : { + "output" : "Б" + }, + "shiftCapsOption" : { + "output" : "<" + }, + "shiftOption" : { + "output" : "<" + }, + "unmodified" : { + "output" : "б" + } + }, + "44" : { + "caps" : { + "output" : "\/" + }, + "capsOption" : { + "output" : "“" + }, + "keyCode" : 44, + "option" : { + "output" : "“" + }, + "shift" : { + "output" : "?" + }, + "shiftCaps" : { + "output" : "?" + }, + "shiftCapsOption" : { + "output" : "„" + }, + "shiftOption" : { + "output" : "„" + }, + "unmodified" : { + "output" : "\/" + } + }, + "45" : { + "caps" : { + "output" : "Т" + }, + "capsOption" : { + "output" : "™" + }, + "keyCode" : 45, + "option" : { + "output" : "™" + }, + "shift" : { + "output" : "Т" + }, + "shiftCaps" : { + "output" : "Т" + }, + "shiftCapsOption" : { + "output" : "™" + }, + "shiftOption" : { + "output" : "™" + }, + "unmodified" : { + "output" : "т" + } + }, + "46" : { + "caps" : { + "output" : "Ь" + }, + "capsOption" : { + "output" : "~" + }, + "keyCode" : 46, + "option" : { + "output" : "~" + }, + "shift" : { + "output" : "Ь" + }, + "shiftCaps" : { + "output" : "Ь" + }, + "shiftCapsOption" : { + "output" : "~" + }, + "shiftOption" : { + "output" : "~" + }, + "unmodified" : { + "output" : "ь" + } + }, + "47" : { + "caps" : { + "output" : "Ю" + }, + "capsOption" : { + "output" : "≥" + }, + "keyCode" : 47, + "option" : { + "output" : "≥" + }, + "shift" : { + "output" : "Ю" + }, + "shiftCaps" : { + "output" : "Ю" + }, + "shiftCapsOption" : { + "output" : ">" + }, + "shiftOption" : { + "output" : ">" + }, + "unmodified" : { + "output" : "ю" + } + }, + "48" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 48, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "49" : { + "caps" : { + "output" : " " + }, + "capsOption" : { + "output" : " " + }, + "keyCode" : 49, + "option" : { + "output" : " " + }, + "shift" : { + "output" : " " + }, + "shiftCaps" : { + "output" : " " + }, + "shiftCapsOption" : { + "output" : " " + }, + "shiftOption" : { + "output" : " " + }, + "unmodified" : { + "output" : " " + } + }, + "5" : { + "caps" : { + "output" : "П" + }, + "capsOption" : { + "output" : "©" + }, + "keyCode" : 5, + "option" : { + "output" : "©" + }, + "shift" : { + "output" : "П" + }, + "shiftCaps" : { + "output" : "П" + }, + "shiftCapsOption" : { + "output" : "©" + }, + "shiftOption" : { + "output" : "©" + }, + "unmodified" : { + "output" : "п" + } + }, + "50" : { + "caps" : { + "output" : "]" + }, + "capsOption" : { + "output" : "]" + }, + "keyCode" : 50, + "option" : { + "output" : "]" + }, + "shift" : { + "output" : "[" + }, + "shiftCaps" : { + "output" : "[" + }, + "shiftCapsOption" : { + "output" : "[" + }, + "shiftOption" : { + "output" : "[" + }, + "unmodified" : { + "output" : "]" + } + }, + "51" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 51, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "52" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 52, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "53" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 53, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "54" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 54, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "55" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 55, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "56" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 56, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "57" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 57, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "58" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 58, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "59" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 59, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "6" : { + "caps" : { + "output" : "Я" + }, + "capsOption" : { + "output" : "ђ" + }, + "keyCode" : 6, + "option" : { + "output" : "ђ" + }, + "shift" : { + "output" : "Я" + }, + "shiftCaps" : { + "output" : "Я" + }, + "shiftCapsOption" : { + "output" : "Ђ" + }, + "shiftOption" : { + "output" : "Ђ" + }, + "unmodified" : { + "output" : "я" + } + }, + "60" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 60, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "61" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 61, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "62" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 62, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "63" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 63, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "64" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 64, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "65" : { + "caps" : { + "output" : "," + }, + "capsOption" : { + "output" : "," + }, + "keyCode" : 65, + "option" : { + "output" : "," + }, + "shift" : { + "output" : "." + }, + "shiftCaps" : { + "output" : "." + }, + "shiftCapsOption" : { + "output" : "," + }, + "shiftOption" : { + "output" : "," + }, + "unmodified" : { + "output" : "," + } + }, + "66" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 66, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "*" + }, + "shiftCaps" : { + "output" : "*" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "67" : { + "caps" : { + "output" : "*" + }, + "capsOption" : { + "output" : "*" + }, + "keyCode" : 67, + "option" : { + "output" : "*" + }, + "shift" : { + "output" : "*" + }, + "shiftCaps" : { + "output" : "*" + }, + "shiftCapsOption" : { + "output" : "*" + }, + "shiftOption" : { + "output" : "*" + }, + "unmodified" : { + "output" : "*" + } + }, + "68" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 68, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "69" : { + "caps" : { + "output" : "+" + }, + "capsOption" : { + "output" : "+" + }, + "keyCode" : 69, + "option" : { + "output" : "+" + }, + "shift" : { + "output" : "+" + }, + "shiftCaps" : { + "output" : "+" + }, + "shiftCapsOption" : { + "output" : "+" + }, + "shiftOption" : { + "output" : "+" + }, + "unmodified" : { + "output" : "+" + } + }, + "7" : { + "caps" : { + "output" : "Ч" + }, + "capsOption" : { + "output" : "≈" + }, + "keyCode" : 7, + "option" : { + "output" : "≈" + }, + "shift" : { + "output" : "Ч" + }, + "shiftCaps" : { + "output" : "Ч" + }, + "shiftCapsOption" : { + "output" : "≈" + }, + "shiftOption" : { + "output" : "≈" + }, + "unmodified" : { + "output" : "ч" + } + }, + "70" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 70, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "+" + }, + "shiftCaps" : { + "output" : "+" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "71" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 71, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "72" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 72, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "=" + }, + "shiftCaps" : { + "output" : "=" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "73" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 73, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "74" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 74, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "75" : { + "caps" : { + "output" : "\/" + }, + "capsOption" : { + "output" : "\/" + }, + "keyCode" : 75, + "option" : { + "output" : "\/" + }, + "shift" : { + "output" : "\/" + }, + "shiftCaps" : { + "output" : "\/" + }, + "shiftCapsOption" : { + "output" : "\/" + }, + "shiftOption" : { + "output" : "\/" + }, + "unmodified" : { + "output" : "\/" + } + }, + "76" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 76, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "77" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 77, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "\/" + }, + "shiftCaps" : { + "output" : "\/" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "78" : { + "caps" : { + "output" : "-" + }, + "capsOption" : { + "output" : "-" + }, + "keyCode" : 78, + "option" : { + "output" : "-" + }, + "shift" : { + "output" : "-" + }, + "shiftCaps" : { + "output" : "-" + }, + "shiftCapsOption" : { + "output" : "-" + }, + "shiftOption" : { + "output" : "-" + }, + "unmodified" : { + "output" : "-" + } + }, + "79" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 79, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "8" : { + "caps" : { + "output" : "С" + }, + "capsOption" : { + "output" : "≠" + }, + "keyCode" : 8, + "option" : { + "output" : "≠" + }, + "shift" : { + "output" : "С" + }, + "shiftCaps" : { + "output" : "С" + }, + "shiftCapsOption" : { + "output" : "≠" + }, + "shiftOption" : { + "output" : "≠" + }, + "unmodified" : { + "output" : "с" + } + }, + "80" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 80, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "81" : { + "caps" : { + "output" : "=" + }, + "capsOption" : { + "output" : "=" + }, + "keyCode" : 81, + "option" : { + "output" : "=" + }, + "shift" : { + "output" : "=" + }, + "shiftCaps" : { + "output" : "=" + }, + "shiftCapsOption" : { + "output" : "=" + }, + "shiftOption" : { + "output" : "=" + }, + "unmodified" : { + "output" : "=" + } + }, + "82" : { + "caps" : { + "output" : "0" + }, + "capsOption" : { + "output" : "0" + }, + "keyCode" : 82, + "option" : { + "output" : "0" + }, + "shift" : { + "output" : "0" + }, + "shiftCaps" : { + "output" : "0" + }, + "shiftCapsOption" : { + "output" : "0" + }, + "shiftOption" : { + "output" : "0" + }, + "unmodified" : { + "output" : "0" + } + }, + "83" : { + "caps" : { + "output" : "1" + }, + "capsOption" : { + "output" : "1" + }, + "keyCode" : 83, + "option" : { + "output" : "1" + }, + "shift" : { + "output" : "1" + }, + "shiftCaps" : { + "output" : "1" + }, + "shiftCapsOption" : { + "output" : "1" + }, + "shiftOption" : { + "output" : "1" + }, + "unmodified" : { + "output" : "1" + } + }, + "84" : { + "caps" : { + "output" : "2" + }, + "capsOption" : { + "output" : "2" + }, + "keyCode" : 84, + "option" : { + "output" : "2" + }, + "shift" : { + "output" : "2" + }, + "shiftCaps" : { + "output" : "2" + }, + "shiftCapsOption" : { + "output" : "2" + }, + "shiftOption" : { + "output" : "2" + }, + "unmodified" : { + "output" : "2" + } + }, + "85" : { + "caps" : { + "output" : "3" + }, + "capsOption" : { + "output" : "3" + }, + "keyCode" : 85, + "option" : { + "output" : "3" + }, + "shift" : { + "output" : "3" + }, + "shiftCaps" : { + "output" : "3" + }, + "shiftCapsOption" : { + "output" : "3" + }, + "shiftOption" : { + "output" : "3" + }, + "unmodified" : { + "output" : "3" + } + }, + "86" : { + "caps" : { + "output" : "4" + }, + "capsOption" : { + "output" : "4" + }, + "keyCode" : 86, + "option" : { + "output" : "4" + }, + "shift" : { + "output" : "4" + }, + "shiftCaps" : { + "output" : "4" + }, + "shiftCapsOption" : { + "output" : "4" + }, + "shiftOption" : { + "output" : "4" + }, + "unmodified" : { + "output" : "4" + } + }, + "87" : { + "caps" : { + "output" : "5" + }, + "capsOption" : { + "output" : "5" + }, + "keyCode" : 87, + "option" : { + "output" : "5" + }, + "shift" : { + "output" : "5" + }, + "shiftCaps" : { + "output" : "5" + }, + "shiftCapsOption" : { + "output" : "5" + }, + "shiftOption" : { + "output" : "5" + }, + "unmodified" : { + "output" : "5" + } + }, + "88" : { + "caps" : { + "output" : "6" + }, + "capsOption" : { + "output" : "6" + }, + "keyCode" : 88, + "option" : { + "output" : "6" + }, + "shift" : { + "output" : "6" + }, + "shiftCaps" : { + "output" : "6" + }, + "shiftCapsOption" : { + "output" : "6" + }, + "shiftOption" : { + "output" : "6" + }, + "unmodified" : { + "output" : "6" + } + }, + "89" : { + "caps" : { + "output" : "7" + }, + "capsOption" : { + "output" : "7" + }, + "keyCode" : 89, + "option" : { + "output" : "7" + }, + "shift" : { + "output" : "7" + }, + "shiftCaps" : { + "output" : "7" + }, + "shiftCapsOption" : { + "output" : "7" + }, + "shiftOption" : { + "output" : "7" + }, + "unmodified" : { + "output" : "7" + } + }, + "9" : { + "caps" : { + "output" : "М" + }, + "capsOption" : { + "output" : "µ" + }, + "keyCode" : 9, + "option" : { + "output" : "µ" + }, + "shift" : { + "output" : "М" + }, + "shiftCaps" : { + "output" : "М" + }, + "shiftCapsOption" : { + "output" : "µ" + }, + "shiftOption" : { + "output" : "µ" + }, + "unmodified" : { + "output" : "м" + } + }, + "90" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 90, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "91" : { + "caps" : { + "output" : "8" + }, + "capsOption" : { + "output" : "8" + }, + "keyCode" : 91, + "option" : { + "output" : "8" + }, + "shift" : { + "output" : "8" + }, + "shiftCaps" : { + "output" : "8" + }, + "shiftCapsOption" : { + "output" : "8" + }, + "shiftOption" : { + "output" : "8" + }, + "unmodified" : { + "output" : "8" + } + }, + "92" : { + "caps" : { + "output" : "9" + }, + "capsOption" : { + "output" : "9" + }, + "keyCode" : 92, + "option" : { + "output" : "9" + }, + "shift" : { + "output" : "9" + }, + "shiftCaps" : { + "output" : "9" + }, + "shiftCapsOption" : { + "output" : "9" + }, + "shiftOption" : { + "output" : "9" + }, + "unmodified" : { + "output" : "9" + } + }, + "93" : { + "caps" : { + "output" : ">" + }, + "capsOption" : { + "output" : "§" + }, + "keyCode" : 93, + "option" : { + "output" : "§" + }, + "shift" : { + "output" : "<" + }, + "shiftCaps" : { + "output" : "<" + }, + "shiftCapsOption" : { + "output" : "±" + }, + "shiftOption" : { + "output" : "±" + }, + "unmodified" : { + "output" : ">" + } + }, + "94" : { + "caps" : { + "output" : "]" + }, + "capsOption" : { + "output" : "]" + }, + "keyCode" : 94, + "option" : { + "output" : "]" + }, + "shift" : { + "output" : "[" + }, + "shiftCaps" : { + "output" : "[" + }, + "shiftCapsOption" : { + "output" : "[" + }, + "shiftOption" : { + "output" : "[" + }, + "unmodified" : { + "output" : "]" + } + }, + "95" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 95, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "96" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 96, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "97" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 97, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "98" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 98, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "99" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 99, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + } + }, + "keyboardType" : 91, + "osBuild" : "24G830", + "osVersion" : "15.7.9", + "schemaVersion" : 1, + "sourceID" : "com.apple.keylayout.Russian" +} \ No newline at end of file diff --git a/Benchmarks/layout-tables/v1/com.apple.keylayout.RussianWin@37.json b/Benchmarks/layout-tables/v1/com.apple.keylayout.RussianWin@37.json new file mode 100644 index 0000000..d0392d3 --- /dev/null +++ b/Benchmarks/layout-tables/v1/com.apple.keylayout.RussianWin@37.json @@ -0,0 +1,3465 @@ +{ + "keyOutputs" : { + "0" : { + "caps" : { + "output" : "Ф" + }, + "capsOption" : { + "output" : "d" + }, + "keyCode" : 0, + "option" : { + "output" : "d" + }, + "shift" : { + "output" : "Ф" + }, + "shiftCaps" : { + "output" : "Ф" + }, + "shiftCapsOption" : { + "output" : "d" + }, + "shiftOption" : { + "output" : "d" + }, + "unmodified" : { + "output" : "ф" + } + }, + "1" : { + "caps" : { + "output" : "Ы" + }, + "capsOption" : { + "output" : "Ѕ" + }, + "keyCode" : 1, + "option" : { + "output" : "z" + }, + "shift" : { + "output" : "Ы" + }, + "shiftCaps" : { + "output" : "Ы" + }, + "shiftCapsOption" : { + "output" : "Z" + }, + "shiftOption" : { + "output" : "Z" + }, + "unmodified" : { + "output" : "ы" + } + }, + "10" : { + "caps" : { + "output" : "Ё" + }, + "capsOption" : { + "output" : "§" + }, + "keyCode" : 10, + "option" : { + "output" : "§" + }, + "shift" : { + "output" : "Ё" + }, + "shiftCaps" : { + "output" : "Ё" + }, + "shiftCapsOption" : { + "output" : "±" + }, + "shiftOption" : { + "output" : "±" + }, + "unmodified" : { + "output" : "ё" + } + }, + "100" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 100, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "101" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 101, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "102" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 102, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "103" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 103, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "104" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 104, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "105" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 105, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "106" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 106, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "107" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 107, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "108" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 108, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "109" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 109, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "11" : { + "caps" : { + "output" : "И" + }, + "capsOption" : { + "output" : "І" + }, + "keyCode" : 11, + "option" : { + "output" : "і" + }, + "shift" : { + "output" : "И" + }, + "shiftCaps" : { + "output" : "И" + }, + "shiftCapsOption" : { + "output" : "І" + }, + "shiftOption" : { + "output" : "І" + }, + "unmodified" : { + "output" : "и" + } + }, + "110" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 110, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "111" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 111, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "112" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 112, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "113" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 113, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "114" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 114, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "115" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 115, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "116" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 116, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "117" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 117, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "118" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 118, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "119" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 119, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "12" : { + "caps" : { + "output" : "Й" + }, + "capsOption" : { + "output" : "Ј" + }, + "keyCode" : 12, + "option" : { + "output" : "ј" + }, + "shift" : { + "output" : "Й" + }, + "shiftCaps" : { + "output" : "Й" + }, + "shiftCapsOption" : { + "output" : "Ј" + }, + "shiftOption" : { + "output" : "Ј" + }, + "unmodified" : { + "output" : "й" + } + }, + "120" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 120, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "121" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 121, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "122" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 122, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "123" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 123, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "124" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 124, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "125" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 125, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "126" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 126, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "127" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 127, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "13" : { + "caps" : { + "output" : "Ц" + }, + "capsOption" : { + "output" : "Џ" + }, + "keyCode" : 13, + "option" : { + "output" : "џ" + }, + "shift" : { + "output" : "Ц" + }, + "shiftCaps" : { + "output" : "Ц" + }, + "shiftCapsOption" : { + "output" : "Џ" + }, + "shiftOption" : { + "output" : "Џ" + }, + "unmodified" : { + "output" : "ц" + } + }, + "14" : { + "caps" : { + "output" : "У" + }, + "capsOption" : { + "output" : "Ў" + }, + "keyCode" : 14, + "option" : { + "output" : "ў" + }, + "shift" : { + "output" : "У" + }, + "shiftCaps" : { + "output" : "У" + }, + "shiftCapsOption" : { + "output" : "Ў" + }, + "shiftOption" : { + "output" : "Ў" + }, + "unmodified" : { + "output" : "у" + } + }, + "15" : { + "caps" : { + "output" : "К" + }, + "capsOption" : { + "output" : "Ќ" + }, + "keyCode" : 15, + "option" : { + "output" : "ќ" + }, + "shift" : { + "output" : "К" + }, + "shiftCaps" : { + "output" : "К" + }, + "shiftCapsOption" : { + "output" : "Ќ" + }, + "shiftOption" : { + "output" : "Ќ" + }, + "unmodified" : { + "output" : "к" + } + }, + "16" : { + "caps" : { + "output" : "Н" + }, + "capsOption" : { + "output" : "Њ" + }, + "keyCode" : 16, + "option" : { + "output" : "њ" + }, + "shift" : { + "output" : "Н" + }, + "shiftCaps" : { + "output" : "Н" + }, + "shiftCapsOption" : { + "output" : "Њ" + }, + "shiftOption" : { + "output" : "Њ" + }, + "unmodified" : { + "output" : "н" + } + }, + "17" : { + "caps" : { + "output" : "Е" + }, + "capsOption" : { + "output" : "†" + }, + "keyCode" : 17, + "option" : { + "output" : "†" + }, + "shift" : { + "output" : "Е" + }, + "shiftCaps" : { + "output" : "Е" + }, + "shiftCapsOption" : { + "output" : "†" + }, + "shiftOption" : { + "output" : "†" + }, + "unmodified" : { + "output" : "е" + } + }, + "18" : { + "caps" : { + "output" : "1" + }, + "capsOption" : { + "output" : "!" + }, + "keyCode" : 18, + "option" : { + "output" : "!" + }, + "shift" : { + "output" : "!" + }, + "shiftCaps" : { + "output" : "!" + }, + "shiftCapsOption" : { + "output" : "™" + }, + "shiftOption" : { + "output" : "™" + }, + "unmodified" : { + "output" : "1" + } + }, + "19" : { + "caps" : { + "output" : "2" + }, + "capsOption" : { + "output" : "@" + }, + "keyCode" : 19, + "option" : { + "output" : "@" + }, + "shift" : { + "output" : "\"" + }, + "shiftCaps" : { + "output" : "\"" + }, + "shiftCapsOption" : { + "output" : "§" + }, + "shiftOption" : { + "output" : "§" + }, + "unmodified" : { + "output" : "2" + } + }, + "2" : { + "caps" : { + "output" : "В" + }, + "capsOption" : { + "output" : "Ћ" + }, + "keyCode" : 2, + "option" : { + "output" : "ћ" + }, + "shift" : { + "output" : "В" + }, + "shiftCaps" : { + "output" : "В" + }, + "shiftCapsOption" : { + "output" : "Ћ" + }, + "shiftOption" : { + "output" : "Ћ" + }, + "unmodified" : { + "output" : "в" + } + }, + "20" : { + "caps" : { + "output" : "3" + }, + "capsOption" : { + "output" : "#" + }, + "keyCode" : 20, + "option" : { + "output" : "#" + }, + "shift" : { + "output" : "№" + }, + "shiftCaps" : { + "output" : "№" + }, + "shiftCapsOption" : { + "output" : "£" + }, + "shiftOption" : { + "output" : "£" + }, + "unmodified" : { + "output" : "3" + } + }, + "21" : { + "caps" : { + "output" : "4" + }, + "capsOption" : { + "output" : "$" + }, + "keyCode" : 21, + "option" : { + "output" : "$" + }, + "shift" : { + "output" : ";" + }, + "shiftCaps" : { + "output" : ";" + }, + "shiftCapsOption" : { + "output" : "€" + }, + "shiftOption" : { + "output" : "€" + }, + "unmodified" : { + "output" : "4" + } + }, + "22" : { + "caps" : { + "output" : "6" + }, + "capsOption" : { + "output" : "^" + }, + "keyCode" : 22, + "option" : { + "output" : "^" + }, + "shift" : { + "output" : ":" + }, + "shiftCaps" : { + "output" : ":" + }, + "shiftCapsOption" : { + "output" : "¬" + }, + "shiftOption" : { + "output" : "¬" + }, + "unmodified" : { + "output" : "6" + } + }, + "23" : { + "caps" : { + "output" : "5" + }, + "capsOption" : { + "output" : "©" + }, + "keyCode" : 23, + "option" : { + "output" : "©" + }, + "shift" : { + "output" : "%" + }, + "shiftCaps" : { + "output" : "%" + }, + "shiftCapsOption" : { + "output" : "®" + }, + "shiftOption" : { + "output" : "®" + }, + "unmodified" : { + "output" : "5" + } + }, + "24" : { + "caps" : { + "output" : "=" + }, + "capsOption" : { + "output" : "≠" + }, + "keyCode" : 24, + "option" : { + "output" : "≠" + }, + "shift" : { + "output" : "+" + }, + "shiftCaps" : { + "output" : "+" + }, + "shiftCapsOption" : { + "output" : "≈" + }, + "shiftOption" : { + "output" : "≈" + }, + "unmodified" : { + "output" : "=" + } + }, + "25" : { + "caps" : { + "output" : "9" + }, + "capsOption" : { + "output" : "(" + }, + "keyCode" : 25, + "option" : { + "output" : "(" + }, + "shift" : { + "output" : "(" + }, + "shiftCaps" : { + "output" : "(" + }, + "shiftCapsOption" : { + "output" : "{" + }, + "shiftOption" : { + "output" : "{" + }, + "unmodified" : { + "output" : "9" + } + }, + "26" : { + "caps" : { + "output" : "7" + }, + "capsOption" : { + "output" : "&" + }, + "keyCode" : 26, + "option" : { + "output" : "&" + }, + "shift" : { + "output" : "?" + }, + "shiftCaps" : { + "output" : "?" + }, + "shiftCapsOption" : { + "output" : "¶" + }, + "shiftOption" : { + "output" : "¶" + }, + "unmodified" : { + "output" : "7" + } + }, + "27" : { + "caps" : { + "output" : "-" + }, + "capsOption" : { + "output" : "–" + }, + "keyCode" : 27, + "option" : { + "output" : "–" + }, + "shift" : { + "output" : "_" + }, + "shiftCaps" : { + "output" : "_" + }, + "shiftCapsOption" : { + "output" : "—" + }, + "shiftOption" : { + "output" : "—" + }, + "unmodified" : { + "output" : "-" + } + }, + "28" : { + "caps" : { + "output" : "8" + }, + "capsOption" : { + "output" : "*" + }, + "keyCode" : 28, + "option" : { + "output" : "₽" + }, + "shift" : { + "output" : "*" + }, + "shiftCaps" : { + "output" : "*" + }, + "shiftCapsOption" : { + "output" : "°" + }, + "shiftOption" : { + "output" : "°" + }, + "unmodified" : { + "output" : "8" + } + }, + "29" : { + "caps" : { + "output" : "0" + }, + "capsOption" : { + "output" : ")" + }, + "keyCode" : 29, + "option" : { + "output" : ")" + }, + "shift" : { + "output" : ")" + }, + "shiftCaps" : { + "output" : ")" + }, + "shiftCapsOption" : { + "output" : "}" + }, + "shiftOption" : { + "output" : "}" + }, + "unmodified" : { + "output" : "0" + } + }, + "3" : { + "caps" : { + "output" : "А" + }, + "capsOption" : { + "output" : "±" + }, + "keyCode" : 3, + "option" : { + "output" : "÷" + }, + "shift" : { + "output" : "А" + }, + "shiftCaps" : { + "output" : "А" + }, + "shiftCapsOption" : { + "output" : "±" + }, + "shiftOption" : { + "output" : "±" + }, + "unmodified" : { + "output" : "а" + } + }, + "30" : { + "caps" : { + "output" : "Ъ" + }, + "capsOption" : { + "output" : "Ґ" + }, + "keyCode" : 30, + "option" : { + "output" : "«" + }, + "shift" : { + "output" : "Ъ" + }, + "shiftCaps" : { + "output" : "Ъ" + }, + "shiftCapsOption" : { + "output" : "»" + }, + "shiftOption" : { + "output" : "»" + }, + "unmodified" : { + "output" : "ъ" + } + }, + "31" : { + "caps" : { + "output" : "Щ" + }, + "capsOption" : { + "output" : "„" + }, + "keyCode" : 31, + "option" : { + "output" : "'" + }, + "shift" : { + "output" : "Щ" + }, + "shiftCaps" : { + "output" : "Щ" + }, + "shiftCapsOption" : { + "output" : "„" + }, + "shiftOption" : { + "output" : "„" + }, + "unmodified" : { + "output" : "щ" + } + }, + "32" : { + "caps" : { + "output" : "Г" + }, + "capsOption" : { + "output" : "Ѓ" + }, + "keyCode" : 32, + "option" : { + "output" : "ѓ" + }, + "shift" : { + "output" : "Г" + }, + "shiftCaps" : { + "output" : "Г" + }, + "shiftCapsOption" : { + "output" : "Ѓ" + }, + "shiftOption" : { + "output" : "Ѓ" + }, + "unmodified" : { + "output" : "г" + } + }, + "33" : { + "caps" : { + "output" : "Х" + }, + "capsOption" : { + "output" : "”" + }, + "keyCode" : 33, + "option" : { + "output" : "“" + }, + "shift" : { + "output" : "Х" + }, + "shiftCaps" : { + "output" : "Х" + }, + "shiftCapsOption" : { + "output" : "”" + }, + "shiftOption" : { + "output" : "”" + }, + "unmodified" : { + "output" : "х" + } + }, + "34" : { + "caps" : { + "output" : "Ш" + }, + "capsOption" : { + "output" : "Ѕ" + }, + "keyCode" : 34, + "option" : { + "output" : "ѕ" + }, + "shift" : { + "output" : "Ш" + }, + "shiftCaps" : { + "output" : "Ш" + }, + "shiftCapsOption" : { + "output" : "Ѕ" + }, + "shiftOption" : { + "output" : "Ѕ" + }, + "unmodified" : { + "output" : "ш" + } + }, + "35" : { + "caps" : { + "output" : "З" + }, + "capsOption" : { + "output" : "’" + }, + "keyCode" : 35, + "option" : { + "output" : "‘" + }, + "shift" : { + "output" : "З" + }, + "shiftCaps" : { + "output" : "З" + }, + "shiftCapsOption" : { + "output" : "’" + }, + "shiftOption" : { + "output" : "’" + }, + "unmodified" : { + "output" : "з" + } + }, + "36" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 36, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "37" : { + "caps" : { + "output" : "Д" + }, + "capsOption" : { + "output" : "L" + }, + "keyCode" : 37, + "option" : { + "output" : "l" + }, + "shift" : { + "output" : "Д" + }, + "shiftCaps" : { + "output" : "Д" + }, + "shiftCapsOption" : { + "output" : "L" + }, + "shiftOption" : { + "output" : "L" + }, + "unmodified" : { + "output" : "д" + } + }, + "38" : { + "caps" : { + "output" : "О" + }, + "capsOption" : { + "output" : "µ" + }, + "keyCode" : 38, + "option" : { + "output" : "∆" + }, + "shift" : { + "output" : "О" + }, + "shiftCaps" : { + "output" : "О" + }, + "shiftCapsOption" : { + "output" : "µ" + }, + "shiftOption" : { + "output" : "µ" + }, + "unmodified" : { + "output" : "о" + } + }, + "39" : { + "caps" : { + "output" : "Э" + }, + "capsOption" : { + "output" : "Є" + }, + "keyCode" : 39, + "option" : { + "output" : "є" + }, + "shift" : { + "output" : "Э" + }, + "shiftCaps" : { + "output" : "Э" + }, + "shiftCapsOption" : { + "output" : "Є" + }, + "shiftOption" : { + "output" : "Є" + }, + "unmodified" : { + "output" : "э" + } + }, + "4" : { + "caps" : { + "output" : "Р" + }, + "capsOption" : { + "output" : "∞" + }, + "keyCode" : 4, + "option" : { + "output" : "•" + }, + "shift" : { + "output" : "Р" + }, + "shiftCaps" : { + "output" : "Р" + }, + "shiftCapsOption" : { + "output" : "∞" + }, + "shiftOption" : { + "output" : "∞" + }, + "unmodified" : { + "output" : "р" + } + }, + "40" : { + "caps" : { + "output" : "Л" + }, + "capsOption" : { + "output" : "Љ" + }, + "keyCode" : 40, + "option" : { + "output" : "љ" + }, + "shift" : { + "output" : "Л" + }, + "shiftCaps" : { + "output" : "Л" + }, + "shiftCapsOption" : { + "output" : "Љ" + }, + "shiftOption" : { + "output" : "Љ" + }, + "unmodified" : { + "output" : "л" + } + }, + "41" : { + "caps" : { + "output" : "Ж" + }, + "capsOption" : { + "output" : "»" + }, + "keyCode" : 41, + "option" : { + "output" : "«" + }, + "shift" : { + "output" : "Ж" + }, + "shiftCaps" : { + "output" : "Ж" + }, + "shiftCapsOption" : { + "output" : "»" + }, + "shiftOption" : { + "output" : "»" + }, + "unmodified" : { + "output" : "ж" + } + }, + "42" : { + "caps" : { + "output" : "\\" + }, + "capsOption" : { + "output" : "|" + }, + "keyCode" : 42, + "option" : { + "output" : "\\" + }, + "shift" : { + "output" : "\/" + }, + "shiftCaps" : { + "output" : "\/" + }, + "shiftCapsOption" : { + "output" : "|" + }, + "shiftOption" : { + "output" : "|" + }, + "unmodified" : { + "output" : "\\" + } + }, + "43" : { + "caps" : { + "output" : "Б" + }, + "capsOption" : { + "output" : "<" + }, + "keyCode" : 43, + "option" : { + "output" : "≤" + }, + "shift" : { + "output" : "Б" + }, + "shiftCaps" : { + "output" : "Б" + }, + "shiftCapsOption" : { + "output" : "<" + }, + "shiftOption" : { + "output" : "<" + }, + "unmodified" : { + "output" : "б" + } + }, + "44" : { + "caps" : { + "output" : "." + }, + "capsOption" : { + "output" : "Ї" + }, + "keyCode" : 44, + "option" : { + "output" : "ї" + }, + "shift" : { + "output" : "," + }, + "shiftCaps" : { + "output" : "," + }, + "shiftCapsOption" : { + "output" : "Ї" + }, + "shiftOption" : { + "output" : "Ї" + }, + "unmodified" : { + "output" : "." + } + }, + "45" : { + "caps" : { + "output" : "Т" + }, + "capsOption" : { + "output" : "ƒ" + }, + "keyCode" : 45, + "option" : { + "output" : "ƒ" + }, + "shift" : { + "output" : "Т" + }, + "shiftCaps" : { + "output" : "Т" + }, + "shiftCapsOption" : { + "output" : "ƒ" + }, + "shiftOption" : { + "output" : "ƒ" + }, + "unmodified" : { + "output" : "т" + } + }, + "46" : { + "caps" : { + "output" : "Ь" + }, + "capsOption" : { + "output" : "M" + }, + "keyCode" : 46, + "option" : { + "output" : "m" + }, + "shift" : { + "output" : "Ь" + }, + "shiftCaps" : { + "output" : "Ь" + }, + "shiftCapsOption" : { + "output" : "M" + }, + "shiftOption" : { + "output" : "M" + }, + "unmodified" : { + "output" : "ь" + } + }, + "47" : { + "caps" : { + "output" : "Ю" + }, + "capsOption" : { + "output" : ">" + }, + "keyCode" : 47, + "option" : { + "output" : "≥" + }, + "shift" : { + "output" : "Ю" + }, + "shiftCaps" : { + "output" : "Ю" + }, + "shiftCapsOption" : { + "output" : ">" + }, + "shiftOption" : { + "output" : ">" + }, + "unmodified" : { + "output" : "ю" + } + }, + "48" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 48, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "49" : { + "caps" : { + "output" : " " + }, + "capsOption" : { + "output" : " " + }, + "keyCode" : 49, + "option" : { + "output" : " " + }, + "shift" : { + "output" : " " + }, + "shiftCaps" : { + "output" : " " + }, + "shiftCapsOption" : { + "output" : " " + }, + "shiftOption" : { + "output" : " " + }, + "unmodified" : { + "output" : " " + } + }, + "5" : { + "caps" : { + "output" : "П" + }, + "capsOption" : { + "output" : "√" + }, + "keyCode" : 5, + "option" : { + "output" : "…" + }, + "shift" : { + "output" : "П" + }, + "shiftCaps" : { + "output" : "П" + }, + "shiftCapsOption" : { + "output" : "√" + }, + "shiftOption" : { + "output" : "√" + }, + "unmodified" : { + "output" : "п" + } + }, + "50" : { + "caps" : { + "output" : "Ë" + }, + "capsOption" : { + "output" : "*" + }, + "keyCode" : 50, + "option" : { + "output" : "[" + }, + "shift" : { + "output" : "Ë" + }, + "shiftCaps" : { + "output" : "Ë" + }, + "shiftCapsOption" : { + "output" : "]" + }, + "shiftOption" : { + "output" : "]" + }, + "unmodified" : { + "output" : "ё" + } + }, + "51" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 51, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "52" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 52, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "53" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 53, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "54" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 54, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "55" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 55, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "56" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 56, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "57" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 57, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "58" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 58, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "59" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 59, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "6" : { + "caps" : { + "output" : "Я" + }, + "capsOption" : { + "output" : "Ђ" + }, + "keyCode" : 6, + "option" : { + "output" : "ђ" + }, + "shift" : { + "output" : "Я" + }, + "shiftCaps" : { + "output" : "Я" + }, + "shiftCapsOption" : { + "output" : "Ђ" + }, + "shiftOption" : { + "output" : "Ђ" + }, + "unmodified" : { + "output" : "я" + } + }, + "60" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 60, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "61" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 61, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "62" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 62, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "63" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 63, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "64" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 64, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "65" : { + "caps" : { + "output" : "," + }, + "capsOption" : { + "output" : "," + }, + "keyCode" : 65, + "option" : { + "output" : "," + }, + "shift" : { + "output" : "," + }, + "shiftCaps" : { + "output" : "," + }, + "shiftCapsOption" : { + "output" : "," + }, + "shiftOption" : { + "output" : "," + }, + "unmodified" : { + "output" : "," + } + }, + "66" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "output" : "*" + }, + "keyCode" : 66, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "*" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "output" : "*" + }, + "shiftOption" : { + "output" : "*" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "67" : { + "caps" : { + "output" : "*" + }, + "capsOption" : { + "output" : "*" + }, + "keyCode" : 67, + "option" : { + "output" : "*" + }, + "shift" : { + "output" : "*" + }, + "shiftCaps" : { + "output" : "*" + }, + "shiftCapsOption" : { + "output" : "*" + }, + "shiftOption" : { + "output" : "*" + }, + "unmodified" : { + "output" : "*" + } + }, + "68" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 68, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "69" : { + "caps" : { + "output" : "+" + }, + "capsOption" : { + "output" : "+" + }, + "keyCode" : 69, + "option" : { + "output" : "+" + }, + "shift" : { + "output" : "+" + }, + "shiftCaps" : { + "output" : "+" + }, + "shiftCapsOption" : { + "output" : "+" + }, + "shiftOption" : { + "output" : "+" + }, + "unmodified" : { + "output" : "+" + } + }, + "7" : { + "caps" : { + "output" : "Ч" + }, + "capsOption" : { + "output" : "X" + }, + "keyCode" : 7, + "option" : { + "output" : "x" + }, + "shift" : { + "output" : "Ч" + }, + "shiftCaps" : { + "output" : "Ч" + }, + "shiftCapsOption" : { + "output" : "X" + }, + "shiftOption" : { + "output" : "X" + }, + "unmodified" : { + "output" : "ч" + } + }, + "70" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "output" : "+" + }, + "keyCode" : 70, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "+" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "output" : "+" + }, + "shiftOption" : { + "output" : "+" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "71" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 71, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "72" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "output" : "=" + }, + "keyCode" : 72, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "=" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "output" : "=" + }, + "shiftOption" : { + "output" : "=" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "73" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 73, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "74" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 74, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "75" : { + "caps" : { + "output" : "\/" + }, + "capsOption" : { + "output" : "\/" + }, + "keyCode" : 75, + "option" : { + "output" : "\/" + }, + "shift" : { + "output" : "\/" + }, + "shiftCaps" : { + "output" : "\/" + }, + "shiftCapsOption" : { + "output" : "\/" + }, + "shiftOption" : { + "output" : "\/" + }, + "unmodified" : { + "output" : "\/" + } + }, + "76" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 76, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "77" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "output" : "\/" + }, + "keyCode" : 77, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "\/" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "output" : "\/" + }, + "shiftOption" : { + "output" : "\/" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "78" : { + "caps" : { + "output" : "-" + }, + "capsOption" : { + "output" : "-" + }, + "keyCode" : 78, + "option" : { + "output" : "-" + }, + "shift" : { + "output" : "-" + }, + "shiftCaps" : { + "output" : "-" + }, + "shiftCapsOption" : { + "output" : "-" + }, + "shiftOption" : { + "output" : "-" + }, + "unmodified" : { + "output" : "-" + } + }, + "79" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 79, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "8" : { + "caps" : { + "output" : "С" + }, + "capsOption" : { + "output" : "C" + }, + "keyCode" : 8, + "option" : { + "output" : "c" + }, + "shift" : { + "output" : "С" + }, + "shiftCaps" : { + "output" : "С" + }, + "shiftCapsOption" : { + "output" : "C" + }, + "shiftOption" : { + "output" : "C" + }, + "unmodified" : { + "output" : "с" + } + }, + "80" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 80, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "81" : { + "caps" : { + "output" : "=" + }, + "capsOption" : { + "output" : "=" + }, + "keyCode" : 81, + "option" : { + "output" : "=" + }, + "shift" : { + "output" : "=" + }, + "shiftCaps" : { + "output" : "=" + }, + "shiftCapsOption" : { + "output" : "=" + }, + "shiftOption" : { + "output" : "=" + }, + "unmodified" : { + "output" : "=" + } + }, + "82" : { + "caps" : { + "output" : "0" + }, + "capsOption" : { + "output" : "0" + }, + "keyCode" : 82, + "option" : { + "output" : "0" + }, + "shift" : { + "output" : "0" + }, + "shiftCaps" : { + "output" : "0" + }, + "shiftCapsOption" : { + "output" : "0" + }, + "shiftOption" : { + "output" : "0" + }, + "unmodified" : { + "output" : "0" + } + }, + "83" : { + "caps" : { + "output" : "1" + }, + "capsOption" : { + "output" : "1" + }, + "keyCode" : 83, + "option" : { + "output" : "1" + }, + "shift" : { + "output" : "1" + }, + "shiftCaps" : { + "output" : "1" + }, + "shiftCapsOption" : { + "output" : "1" + }, + "shiftOption" : { + "output" : "1" + }, + "unmodified" : { + "output" : "1" + } + }, + "84" : { + "caps" : { + "output" : "2" + }, + "capsOption" : { + "output" : "2" + }, + "keyCode" : 84, + "option" : { + "output" : "2" + }, + "shift" : { + "output" : "2" + }, + "shiftCaps" : { + "output" : "2" + }, + "shiftCapsOption" : { + "output" : "2" + }, + "shiftOption" : { + "output" : "2" + }, + "unmodified" : { + "output" : "2" + } + }, + "85" : { + "caps" : { + "output" : "3" + }, + "capsOption" : { + "output" : "3" + }, + "keyCode" : 85, + "option" : { + "output" : "3" + }, + "shift" : { + "output" : "3" + }, + "shiftCaps" : { + "output" : "3" + }, + "shiftCapsOption" : { + "output" : "3" + }, + "shiftOption" : { + "output" : "3" + }, + "unmodified" : { + "output" : "3" + } + }, + "86" : { + "caps" : { + "output" : "4" + }, + "capsOption" : { + "output" : "4" + }, + "keyCode" : 86, + "option" : { + "output" : "4" + }, + "shift" : { + "output" : "4" + }, + "shiftCaps" : { + "output" : "4" + }, + "shiftCapsOption" : { + "output" : "4" + }, + "shiftOption" : { + "output" : "4" + }, + "unmodified" : { + "output" : "4" + } + }, + "87" : { + "caps" : { + "output" : "5" + }, + "capsOption" : { + "output" : "5" + }, + "keyCode" : 87, + "option" : { + "output" : "5" + }, + "shift" : { + "output" : "5" + }, + "shiftCaps" : { + "output" : "5" + }, + "shiftCapsOption" : { + "output" : "5" + }, + "shiftOption" : { + "output" : "5" + }, + "unmodified" : { + "output" : "5" + } + }, + "88" : { + "caps" : { + "output" : "6" + }, + "capsOption" : { + "output" : "6" + }, + "keyCode" : 88, + "option" : { + "output" : "6" + }, + "shift" : { + "output" : "6" + }, + "shiftCaps" : { + "output" : "6" + }, + "shiftCapsOption" : { + "output" : "6" + }, + "shiftOption" : { + "output" : "6" + }, + "unmodified" : { + "output" : "6" + } + }, + "89" : { + "caps" : { + "output" : "7" + }, + "capsOption" : { + "output" : "7" + }, + "keyCode" : 89, + "option" : { + "output" : "7" + }, + "shift" : { + "output" : "7" + }, + "shiftCaps" : { + "output" : "7" + }, + "shiftCapsOption" : { + "output" : "7" + }, + "shiftOption" : { + "output" : "7" + }, + "unmodified" : { + "output" : "7" + } + }, + "9" : { + "caps" : { + "output" : "М" + }, + "capsOption" : { + "output" : "V" + }, + "keyCode" : 9, + "option" : { + "output" : "v" + }, + "shift" : { + "output" : "М" + }, + "shiftCaps" : { + "output" : "М" + }, + "shiftCapsOption" : { + "output" : "V" + }, + "shiftOption" : { + "output" : "V" + }, + "unmodified" : { + "output" : "м" + } + }, + "90" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 90, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "91" : { + "caps" : { + "output" : "8" + }, + "capsOption" : { + "output" : "8" + }, + "keyCode" : 91, + "option" : { + "output" : "8" + }, + "shift" : { + "output" : "8" + }, + "shiftCaps" : { + "output" : "8" + }, + "shiftCapsOption" : { + "output" : "8" + }, + "shiftOption" : { + "output" : "8" + }, + "unmodified" : { + "output" : "8" + } + }, + "92" : { + "caps" : { + "output" : "9" + }, + "capsOption" : { + "output" : "9" + }, + "keyCode" : 92, + "option" : { + "output" : "9" + }, + "shift" : { + "output" : "9" + }, + "shiftCaps" : { + "output" : "9" + }, + "shiftCapsOption" : { + "output" : "9" + }, + "shiftOption" : { + "output" : "9" + }, + "unmodified" : { + "output" : "9" + } + }, + "93" : { + "caps" : { + "output" : "Ё" + }, + "capsOption" : { + "output" : "§" + }, + "keyCode" : 93, + "option" : { + "output" : "§" + }, + "shift" : { + "output" : "Ё" + }, + "shiftCaps" : { + "output" : "Ё" + }, + "shiftCapsOption" : { + "output" : "±" + }, + "shiftOption" : { + "output" : "±" + }, + "unmodified" : { + "output" : "ё" + } + }, + "94" : { + "caps" : { + "output" : "Ë" + }, + "capsOption" : { + "output" : "*" + }, + "keyCode" : 94, + "option" : { + "output" : "[" + }, + "shift" : { + "output" : "Ë" + }, + "shiftCaps" : { + "output" : "Ë" + }, + "shiftCapsOption" : { + "output" : "]" + }, + "shiftOption" : { + "output" : "]" + }, + "unmodified" : { + "output" : "ё" + } + }, + "95" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 95, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "96" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 96, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "97" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 97, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "98" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 98, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "99" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 99, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + } + }, + "keyboardType" : 37, + "osBuild" : "24G830", + "osVersion" : "15.7.9", + "schemaVersion" : 1, + "sourceID" : "com.apple.keylayout.RussianWin" +} \ No newline at end of file diff --git a/Benchmarks/layout-tables/v1/com.apple.keylayout.RussianWin@91.json b/Benchmarks/layout-tables/v1/com.apple.keylayout.RussianWin@91.json new file mode 100644 index 0000000..056f37f --- /dev/null +++ b/Benchmarks/layout-tables/v1/com.apple.keylayout.RussianWin@91.json @@ -0,0 +1,3465 @@ +{ + "keyOutputs" : { + "0" : { + "caps" : { + "output" : "Ф" + }, + "capsOption" : { + "output" : "d" + }, + "keyCode" : 0, + "option" : { + "output" : "d" + }, + "shift" : { + "output" : "Ф" + }, + "shiftCaps" : { + "output" : "Ф" + }, + "shiftCapsOption" : { + "output" : "d" + }, + "shiftOption" : { + "output" : "d" + }, + "unmodified" : { + "output" : "ф" + } + }, + "1" : { + "caps" : { + "output" : "Ы" + }, + "capsOption" : { + "output" : "Ѕ" + }, + "keyCode" : 1, + "option" : { + "output" : "z" + }, + "shift" : { + "output" : "Ы" + }, + "shiftCaps" : { + "output" : "Ы" + }, + "shiftCapsOption" : { + "output" : "Z" + }, + "shiftOption" : { + "output" : "Z" + }, + "unmodified" : { + "output" : "ы" + } + }, + "10" : { + "caps" : { + "output" : "Ё" + }, + "capsOption" : { + "output" : "§" + }, + "keyCode" : 10, + "option" : { + "output" : "§" + }, + "shift" : { + "output" : "Ё" + }, + "shiftCaps" : { + "output" : "Ё" + }, + "shiftCapsOption" : { + "output" : "±" + }, + "shiftOption" : { + "output" : "±" + }, + "unmodified" : { + "output" : "ё" + } + }, + "100" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 100, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "101" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 101, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "102" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 102, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "103" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 103, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "104" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 104, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "105" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 105, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "106" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 106, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "107" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 107, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "108" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 108, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "109" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 109, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "11" : { + "caps" : { + "output" : "И" + }, + "capsOption" : { + "output" : "І" + }, + "keyCode" : 11, + "option" : { + "output" : "і" + }, + "shift" : { + "output" : "И" + }, + "shiftCaps" : { + "output" : "И" + }, + "shiftCapsOption" : { + "output" : "І" + }, + "shiftOption" : { + "output" : "І" + }, + "unmodified" : { + "output" : "и" + } + }, + "110" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 110, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "111" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 111, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "112" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 112, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "113" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 113, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "114" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 114, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "115" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 115, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "116" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 116, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "117" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 117, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "118" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 118, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "119" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 119, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "12" : { + "caps" : { + "output" : "Й" + }, + "capsOption" : { + "output" : "Ј" + }, + "keyCode" : 12, + "option" : { + "output" : "ј" + }, + "shift" : { + "output" : "Й" + }, + "shiftCaps" : { + "output" : "Й" + }, + "shiftCapsOption" : { + "output" : "Ј" + }, + "shiftOption" : { + "output" : "Ј" + }, + "unmodified" : { + "output" : "й" + } + }, + "120" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 120, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "121" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 121, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "122" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 122, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "123" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 123, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "124" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 124, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "125" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 125, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "126" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 126, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "127" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 127, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "13" : { + "caps" : { + "output" : "Ц" + }, + "capsOption" : { + "output" : "Џ" + }, + "keyCode" : 13, + "option" : { + "output" : "џ" + }, + "shift" : { + "output" : "Ц" + }, + "shiftCaps" : { + "output" : "Ц" + }, + "shiftCapsOption" : { + "output" : "Џ" + }, + "shiftOption" : { + "output" : "Џ" + }, + "unmodified" : { + "output" : "ц" + } + }, + "14" : { + "caps" : { + "output" : "У" + }, + "capsOption" : { + "output" : "Ў" + }, + "keyCode" : 14, + "option" : { + "output" : "ў" + }, + "shift" : { + "output" : "У" + }, + "shiftCaps" : { + "output" : "У" + }, + "shiftCapsOption" : { + "output" : "Ў" + }, + "shiftOption" : { + "output" : "Ў" + }, + "unmodified" : { + "output" : "у" + } + }, + "15" : { + "caps" : { + "output" : "К" + }, + "capsOption" : { + "output" : "Ќ" + }, + "keyCode" : 15, + "option" : { + "output" : "ќ" + }, + "shift" : { + "output" : "К" + }, + "shiftCaps" : { + "output" : "К" + }, + "shiftCapsOption" : { + "output" : "Ќ" + }, + "shiftOption" : { + "output" : "Ќ" + }, + "unmodified" : { + "output" : "к" + } + }, + "16" : { + "caps" : { + "output" : "Н" + }, + "capsOption" : { + "output" : "Њ" + }, + "keyCode" : 16, + "option" : { + "output" : "њ" + }, + "shift" : { + "output" : "Н" + }, + "shiftCaps" : { + "output" : "Н" + }, + "shiftCapsOption" : { + "output" : "Њ" + }, + "shiftOption" : { + "output" : "Њ" + }, + "unmodified" : { + "output" : "н" + } + }, + "17" : { + "caps" : { + "output" : "Е" + }, + "capsOption" : { + "output" : "†" + }, + "keyCode" : 17, + "option" : { + "output" : "†" + }, + "shift" : { + "output" : "Е" + }, + "shiftCaps" : { + "output" : "Е" + }, + "shiftCapsOption" : { + "output" : "†" + }, + "shiftOption" : { + "output" : "†" + }, + "unmodified" : { + "output" : "е" + } + }, + "18" : { + "caps" : { + "output" : "1" + }, + "capsOption" : { + "output" : "!" + }, + "keyCode" : 18, + "option" : { + "output" : "!" + }, + "shift" : { + "output" : "!" + }, + "shiftCaps" : { + "output" : "!" + }, + "shiftCapsOption" : { + "output" : "™" + }, + "shiftOption" : { + "output" : "™" + }, + "unmodified" : { + "output" : "1" + } + }, + "19" : { + "caps" : { + "output" : "2" + }, + "capsOption" : { + "output" : "@" + }, + "keyCode" : 19, + "option" : { + "output" : "@" + }, + "shift" : { + "output" : "\"" + }, + "shiftCaps" : { + "output" : "\"" + }, + "shiftCapsOption" : { + "output" : "§" + }, + "shiftOption" : { + "output" : "§" + }, + "unmodified" : { + "output" : "2" + } + }, + "2" : { + "caps" : { + "output" : "В" + }, + "capsOption" : { + "output" : "Ћ" + }, + "keyCode" : 2, + "option" : { + "output" : "ћ" + }, + "shift" : { + "output" : "В" + }, + "shiftCaps" : { + "output" : "В" + }, + "shiftCapsOption" : { + "output" : "Ћ" + }, + "shiftOption" : { + "output" : "Ћ" + }, + "unmodified" : { + "output" : "в" + } + }, + "20" : { + "caps" : { + "output" : "3" + }, + "capsOption" : { + "output" : "#" + }, + "keyCode" : 20, + "option" : { + "output" : "#" + }, + "shift" : { + "output" : "№" + }, + "shiftCaps" : { + "output" : "№" + }, + "shiftCapsOption" : { + "output" : "£" + }, + "shiftOption" : { + "output" : "£" + }, + "unmodified" : { + "output" : "3" + } + }, + "21" : { + "caps" : { + "output" : "4" + }, + "capsOption" : { + "output" : "$" + }, + "keyCode" : 21, + "option" : { + "output" : "$" + }, + "shift" : { + "output" : ";" + }, + "shiftCaps" : { + "output" : ";" + }, + "shiftCapsOption" : { + "output" : "€" + }, + "shiftOption" : { + "output" : "€" + }, + "unmodified" : { + "output" : "4" + } + }, + "22" : { + "caps" : { + "output" : "6" + }, + "capsOption" : { + "output" : "^" + }, + "keyCode" : 22, + "option" : { + "output" : "^" + }, + "shift" : { + "output" : ":" + }, + "shiftCaps" : { + "output" : ":" + }, + "shiftCapsOption" : { + "output" : "¬" + }, + "shiftOption" : { + "output" : "¬" + }, + "unmodified" : { + "output" : "6" + } + }, + "23" : { + "caps" : { + "output" : "5" + }, + "capsOption" : { + "output" : "©" + }, + "keyCode" : 23, + "option" : { + "output" : "©" + }, + "shift" : { + "output" : "%" + }, + "shiftCaps" : { + "output" : "%" + }, + "shiftCapsOption" : { + "output" : "®" + }, + "shiftOption" : { + "output" : "®" + }, + "unmodified" : { + "output" : "5" + } + }, + "24" : { + "caps" : { + "output" : "=" + }, + "capsOption" : { + "output" : "≠" + }, + "keyCode" : 24, + "option" : { + "output" : "≠" + }, + "shift" : { + "output" : "+" + }, + "shiftCaps" : { + "output" : "+" + }, + "shiftCapsOption" : { + "output" : "≈" + }, + "shiftOption" : { + "output" : "≈" + }, + "unmodified" : { + "output" : "=" + } + }, + "25" : { + "caps" : { + "output" : "9" + }, + "capsOption" : { + "output" : "(" + }, + "keyCode" : 25, + "option" : { + "output" : "(" + }, + "shift" : { + "output" : "(" + }, + "shiftCaps" : { + "output" : "(" + }, + "shiftCapsOption" : { + "output" : "{" + }, + "shiftOption" : { + "output" : "{" + }, + "unmodified" : { + "output" : "9" + } + }, + "26" : { + "caps" : { + "output" : "7" + }, + "capsOption" : { + "output" : "&" + }, + "keyCode" : 26, + "option" : { + "output" : "&" + }, + "shift" : { + "output" : "?" + }, + "shiftCaps" : { + "output" : "?" + }, + "shiftCapsOption" : { + "output" : "¶" + }, + "shiftOption" : { + "output" : "¶" + }, + "unmodified" : { + "output" : "7" + } + }, + "27" : { + "caps" : { + "output" : "-" + }, + "capsOption" : { + "output" : "–" + }, + "keyCode" : 27, + "option" : { + "output" : "–" + }, + "shift" : { + "output" : "_" + }, + "shiftCaps" : { + "output" : "_" + }, + "shiftCapsOption" : { + "output" : "—" + }, + "shiftOption" : { + "output" : "—" + }, + "unmodified" : { + "output" : "-" + } + }, + "28" : { + "caps" : { + "output" : "8" + }, + "capsOption" : { + "output" : "*" + }, + "keyCode" : 28, + "option" : { + "output" : "₽" + }, + "shift" : { + "output" : "*" + }, + "shiftCaps" : { + "output" : "*" + }, + "shiftCapsOption" : { + "output" : "°" + }, + "shiftOption" : { + "output" : "°" + }, + "unmodified" : { + "output" : "8" + } + }, + "29" : { + "caps" : { + "output" : "0" + }, + "capsOption" : { + "output" : ")" + }, + "keyCode" : 29, + "option" : { + "output" : ")" + }, + "shift" : { + "output" : ")" + }, + "shiftCaps" : { + "output" : ")" + }, + "shiftCapsOption" : { + "output" : "}" + }, + "shiftOption" : { + "output" : "}" + }, + "unmodified" : { + "output" : "0" + } + }, + "3" : { + "caps" : { + "output" : "А" + }, + "capsOption" : { + "output" : "±" + }, + "keyCode" : 3, + "option" : { + "output" : "÷" + }, + "shift" : { + "output" : "А" + }, + "shiftCaps" : { + "output" : "А" + }, + "shiftCapsOption" : { + "output" : "±" + }, + "shiftOption" : { + "output" : "±" + }, + "unmodified" : { + "output" : "а" + } + }, + "30" : { + "caps" : { + "output" : "Ъ" + }, + "capsOption" : { + "output" : "Ґ" + }, + "keyCode" : 30, + "option" : { + "output" : "«" + }, + "shift" : { + "output" : "Ъ" + }, + "shiftCaps" : { + "output" : "Ъ" + }, + "shiftCapsOption" : { + "output" : "»" + }, + "shiftOption" : { + "output" : "»" + }, + "unmodified" : { + "output" : "ъ" + } + }, + "31" : { + "caps" : { + "output" : "Щ" + }, + "capsOption" : { + "output" : "„" + }, + "keyCode" : 31, + "option" : { + "output" : "'" + }, + "shift" : { + "output" : "Щ" + }, + "shiftCaps" : { + "output" : "Щ" + }, + "shiftCapsOption" : { + "output" : "„" + }, + "shiftOption" : { + "output" : "„" + }, + "unmodified" : { + "output" : "щ" + } + }, + "32" : { + "caps" : { + "output" : "Г" + }, + "capsOption" : { + "output" : "Ѓ" + }, + "keyCode" : 32, + "option" : { + "output" : "ѓ" + }, + "shift" : { + "output" : "Г" + }, + "shiftCaps" : { + "output" : "Г" + }, + "shiftCapsOption" : { + "output" : "Ѓ" + }, + "shiftOption" : { + "output" : "Ѓ" + }, + "unmodified" : { + "output" : "г" + } + }, + "33" : { + "caps" : { + "output" : "Х" + }, + "capsOption" : { + "output" : "”" + }, + "keyCode" : 33, + "option" : { + "output" : "“" + }, + "shift" : { + "output" : "Х" + }, + "shiftCaps" : { + "output" : "Х" + }, + "shiftCapsOption" : { + "output" : "”" + }, + "shiftOption" : { + "output" : "”" + }, + "unmodified" : { + "output" : "х" + } + }, + "34" : { + "caps" : { + "output" : "Ш" + }, + "capsOption" : { + "output" : "Ѕ" + }, + "keyCode" : 34, + "option" : { + "output" : "ѕ" + }, + "shift" : { + "output" : "Ш" + }, + "shiftCaps" : { + "output" : "Ш" + }, + "shiftCapsOption" : { + "output" : "Ѕ" + }, + "shiftOption" : { + "output" : "Ѕ" + }, + "unmodified" : { + "output" : "ш" + } + }, + "35" : { + "caps" : { + "output" : "З" + }, + "capsOption" : { + "output" : "’" + }, + "keyCode" : 35, + "option" : { + "output" : "‘" + }, + "shift" : { + "output" : "З" + }, + "shiftCaps" : { + "output" : "З" + }, + "shiftCapsOption" : { + "output" : "’" + }, + "shiftOption" : { + "output" : "’" + }, + "unmodified" : { + "output" : "з" + } + }, + "36" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 36, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "37" : { + "caps" : { + "output" : "Д" + }, + "capsOption" : { + "output" : "L" + }, + "keyCode" : 37, + "option" : { + "output" : "l" + }, + "shift" : { + "output" : "Д" + }, + "shiftCaps" : { + "output" : "Д" + }, + "shiftCapsOption" : { + "output" : "L" + }, + "shiftOption" : { + "output" : "L" + }, + "unmodified" : { + "output" : "д" + } + }, + "38" : { + "caps" : { + "output" : "О" + }, + "capsOption" : { + "output" : "µ" + }, + "keyCode" : 38, + "option" : { + "output" : "∆" + }, + "shift" : { + "output" : "О" + }, + "shiftCaps" : { + "output" : "О" + }, + "shiftCapsOption" : { + "output" : "µ" + }, + "shiftOption" : { + "output" : "µ" + }, + "unmodified" : { + "output" : "о" + } + }, + "39" : { + "caps" : { + "output" : "Э" + }, + "capsOption" : { + "output" : "Є" + }, + "keyCode" : 39, + "option" : { + "output" : "є" + }, + "shift" : { + "output" : "Э" + }, + "shiftCaps" : { + "output" : "Э" + }, + "shiftCapsOption" : { + "output" : "Є" + }, + "shiftOption" : { + "output" : "Є" + }, + "unmodified" : { + "output" : "э" + } + }, + "4" : { + "caps" : { + "output" : "Р" + }, + "capsOption" : { + "output" : "∞" + }, + "keyCode" : 4, + "option" : { + "output" : "•" + }, + "shift" : { + "output" : "Р" + }, + "shiftCaps" : { + "output" : "Р" + }, + "shiftCapsOption" : { + "output" : "∞" + }, + "shiftOption" : { + "output" : "∞" + }, + "unmodified" : { + "output" : "р" + } + }, + "40" : { + "caps" : { + "output" : "Л" + }, + "capsOption" : { + "output" : "Љ" + }, + "keyCode" : 40, + "option" : { + "output" : "љ" + }, + "shift" : { + "output" : "Л" + }, + "shiftCaps" : { + "output" : "Л" + }, + "shiftCapsOption" : { + "output" : "Љ" + }, + "shiftOption" : { + "output" : "Љ" + }, + "unmodified" : { + "output" : "л" + } + }, + "41" : { + "caps" : { + "output" : "Ж" + }, + "capsOption" : { + "output" : "»" + }, + "keyCode" : 41, + "option" : { + "output" : "«" + }, + "shift" : { + "output" : "Ж" + }, + "shiftCaps" : { + "output" : "Ж" + }, + "shiftCapsOption" : { + "output" : "»" + }, + "shiftOption" : { + "output" : "»" + }, + "unmodified" : { + "output" : "ж" + } + }, + "42" : { + "caps" : { + "output" : "\\" + }, + "capsOption" : { + "output" : "|" + }, + "keyCode" : 42, + "option" : { + "output" : "\\" + }, + "shift" : { + "output" : "\/" + }, + "shiftCaps" : { + "output" : "\/" + }, + "shiftCapsOption" : { + "output" : "|" + }, + "shiftOption" : { + "output" : "|" + }, + "unmodified" : { + "output" : "\\" + } + }, + "43" : { + "caps" : { + "output" : "Б" + }, + "capsOption" : { + "output" : "<" + }, + "keyCode" : 43, + "option" : { + "output" : "≤" + }, + "shift" : { + "output" : "Б" + }, + "shiftCaps" : { + "output" : "Б" + }, + "shiftCapsOption" : { + "output" : "<" + }, + "shiftOption" : { + "output" : "<" + }, + "unmodified" : { + "output" : "б" + } + }, + "44" : { + "caps" : { + "output" : "." + }, + "capsOption" : { + "output" : "Ї" + }, + "keyCode" : 44, + "option" : { + "output" : "ї" + }, + "shift" : { + "output" : "," + }, + "shiftCaps" : { + "output" : "," + }, + "shiftCapsOption" : { + "output" : "Ї" + }, + "shiftOption" : { + "output" : "Ї" + }, + "unmodified" : { + "output" : "." + } + }, + "45" : { + "caps" : { + "output" : "Т" + }, + "capsOption" : { + "output" : "ƒ" + }, + "keyCode" : 45, + "option" : { + "output" : "ƒ" + }, + "shift" : { + "output" : "Т" + }, + "shiftCaps" : { + "output" : "Т" + }, + "shiftCapsOption" : { + "output" : "ƒ" + }, + "shiftOption" : { + "output" : "ƒ" + }, + "unmodified" : { + "output" : "т" + } + }, + "46" : { + "caps" : { + "output" : "Ь" + }, + "capsOption" : { + "output" : "M" + }, + "keyCode" : 46, + "option" : { + "output" : "m" + }, + "shift" : { + "output" : "Ь" + }, + "shiftCaps" : { + "output" : "Ь" + }, + "shiftCapsOption" : { + "output" : "M" + }, + "shiftOption" : { + "output" : "M" + }, + "unmodified" : { + "output" : "ь" + } + }, + "47" : { + "caps" : { + "output" : "Ю" + }, + "capsOption" : { + "output" : ">" + }, + "keyCode" : 47, + "option" : { + "output" : "≥" + }, + "shift" : { + "output" : "Ю" + }, + "shiftCaps" : { + "output" : "Ю" + }, + "shiftCapsOption" : { + "output" : ">" + }, + "shiftOption" : { + "output" : ">" + }, + "unmodified" : { + "output" : "ю" + } + }, + "48" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 48, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "49" : { + "caps" : { + "output" : " " + }, + "capsOption" : { + "output" : " " + }, + "keyCode" : 49, + "option" : { + "output" : " " + }, + "shift" : { + "output" : " " + }, + "shiftCaps" : { + "output" : " " + }, + "shiftCapsOption" : { + "output" : " " + }, + "shiftOption" : { + "output" : " " + }, + "unmodified" : { + "output" : " " + } + }, + "5" : { + "caps" : { + "output" : "П" + }, + "capsOption" : { + "output" : "√" + }, + "keyCode" : 5, + "option" : { + "output" : "…" + }, + "shift" : { + "output" : "П" + }, + "shiftCaps" : { + "output" : "П" + }, + "shiftCapsOption" : { + "output" : "√" + }, + "shiftOption" : { + "output" : "√" + }, + "unmodified" : { + "output" : "п" + } + }, + "50" : { + "caps" : { + "output" : "Ë" + }, + "capsOption" : { + "output" : "*" + }, + "keyCode" : 50, + "option" : { + "output" : "[" + }, + "shift" : { + "output" : "Ë" + }, + "shiftCaps" : { + "output" : "Ë" + }, + "shiftCapsOption" : { + "output" : "]" + }, + "shiftOption" : { + "output" : "]" + }, + "unmodified" : { + "output" : "ё" + } + }, + "51" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 51, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "52" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 52, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "53" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 53, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "54" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 54, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "55" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 55, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "56" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 56, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "57" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 57, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "58" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 58, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "59" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 59, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "6" : { + "caps" : { + "output" : "Я" + }, + "capsOption" : { + "output" : "Ђ" + }, + "keyCode" : 6, + "option" : { + "output" : "ђ" + }, + "shift" : { + "output" : "Я" + }, + "shiftCaps" : { + "output" : "Я" + }, + "shiftCapsOption" : { + "output" : "Ђ" + }, + "shiftOption" : { + "output" : "Ђ" + }, + "unmodified" : { + "output" : "я" + } + }, + "60" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 60, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "61" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 61, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "62" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 62, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "63" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 63, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "64" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 64, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "65" : { + "caps" : { + "output" : "," + }, + "capsOption" : { + "output" : "," + }, + "keyCode" : 65, + "option" : { + "output" : "," + }, + "shift" : { + "output" : "," + }, + "shiftCaps" : { + "output" : "," + }, + "shiftCapsOption" : { + "output" : "," + }, + "shiftOption" : { + "output" : "," + }, + "unmodified" : { + "output" : "," + } + }, + "66" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "output" : "*" + }, + "keyCode" : 66, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "*" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "output" : "*" + }, + "shiftOption" : { + "output" : "*" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "67" : { + "caps" : { + "output" : "*" + }, + "capsOption" : { + "output" : "*" + }, + "keyCode" : 67, + "option" : { + "output" : "*" + }, + "shift" : { + "output" : "*" + }, + "shiftCaps" : { + "output" : "*" + }, + "shiftCapsOption" : { + "output" : "*" + }, + "shiftOption" : { + "output" : "*" + }, + "unmodified" : { + "output" : "*" + } + }, + "68" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 68, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "69" : { + "caps" : { + "output" : "+" + }, + "capsOption" : { + "output" : "+" + }, + "keyCode" : 69, + "option" : { + "output" : "+" + }, + "shift" : { + "output" : "+" + }, + "shiftCaps" : { + "output" : "+" + }, + "shiftCapsOption" : { + "output" : "+" + }, + "shiftOption" : { + "output" : "+" + }, + "unmodified" : { + "output" : "+" + } + }, + "7" : { + "caps" : { + "output" : "Ч" + }, + "capsOption" : { + "output" : "X" + }, + "keyCode" : 7, + "option" : { + "output" : "x" + }, + "shift" : { + "output" : "Ч" + }, + "shiftCaps" : { + "output" : "Ч" + }, + "shiftCapsOption" : { + "output" : "X" + }, + "shiftOption" : { + "output" : "X" + }, + "unmodified" : { + "output" : "ч" + } + }, + "70" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "output" : "+" + }, + "keyCode" : 70, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "+" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "output" : "+" + }, + "shiftOption" : { + "output" : "+" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "71" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 71, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "72" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "output" : "=" + }, + "keyCode" : 72, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "=" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "output" : "=" + }, + "shiftOption" : { + "output" : "=" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "73" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 73, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "74" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 74, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "75" : { + "caps" : { + "output" : "\/" + }, + "capsOption" : { + "output" : "\/" + }, + "keyCode" : 75, + "option" : { + "output" : "\/" + }, + "shift" : { + "output" : "\/" + }, + "shiftCaps" : { + "output" : "\/" + }, + "shiftCapsOption" : { + "output" : "\/" + }, + "shiftOption" : { + "output" : "\/" + }, + "unmodified" : { + "output" : "\/" + } + }, + "76" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 76, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "77" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "output" : "\/" + }, + "keyCode" : 77, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "\/" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "output" : "\/" + }, + "shiftOption" : { + "output" : "\/" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "78" : { + "caps" : { + "output" : "-" + }, + "capsOption" : { + "output" : "-" + }, + "keyCode" : 78, + "option" : { + "output" : "-" + }, + "shift" : { + "output" : "-" + }, + "shiftCaps" : { + "output" : "-" + }, + "shiftCapsOption" : { + "output" : "-" + }, + "shiftOption" : { + "output" : "-" + }, + "unmodified" : { + "output" : "-" + } + }, + "79" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 79, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "8" : { + "caps" : { + "output" : "С" + }, + "capsOption" : { + "output" : "C" + }, + "keyCode" : 8, + "option" : { + "output" : "c" + }, + "shift" : { + "output" : "С" + }, + "shiftCaps" : { + "output" : "С" + }, + "shiftCapsOption" : { + "output" : "C" + }, + "shiftOption" : { + "output" : "C" + }, + "unmodified" : { + "output" : "с" + } + }, + "80" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 80, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "81" : { + "caps" : { + "output" : "=" + }, + "capsOption" : { + "output" : "=" + }, + "keyCode" : 81, + "option" : { + "output" : "=" + }, + "shift" : { + "output" : "=" + }, + "shiftCaps" : { + "output" : "=" + }, + "shiftCapsOption" : { + "output" : "=" + }, + "shiftOption" : { + "output" : "=" + }, + "unmodified" : { + "output" : "=" + } + }, + "82" : { + "caps" : { + "output" : "0" + }, + "capsOption" : { + "output" : "0" + }, + "keyCode" : 82, + "option" : { + "output" : "0" + }, + "shift" : { + "output" : "0" + }, + "shiftCaps" : { + "output" : "0" + }, + "shiftCapsOption" : { + "output" : "0" + }, + "shiftOption" : { + "output" : "0" + }, + "unmodified" : { + "output" : "0" + } + }, + "83" : { + "caps" : { + "output" : "1" + }, + "capsOption" : { + "output" : "1" + }, + "keyCode" : 83, + "option" : { + "output" : "1" + }, + "shift" : { + "output" : "1" + }, + "shiftCaps" : { + "output" : "1" + }, + "shiftCapsOption" : { + "output" : "1" + }, + "shiftOption" : { + "output" : "1" + }, + "unmodified" : { + "output" : "1" + } + }, + "84" : { + "caps" : { + "output" : "2" + }, + "capsOption" : { + "output" : "2" + }, + "keyCode" : 84, + "option" : { + "output" : "2" + }, + "shift" : { + "output" : "2" + }, + "shiftCaps" : { + "output" : "2" + }, + "shiftCapsOption" : { + "output" : "2" + }, + "shiftOption" : { + "output" : "2" + }, + "unmodified" : { + "output" : "2" + } + }, + "85" : { + "caps" : { + "output" : "3" + }, + "capsOption" : { + "output" : "3" + }, + "keyCode" : 85, + "option" : { + "output" : "3" + }, + "shift" : { + "output" : "3" + }, + "shiftCaps" : { + "output" : "3" + }, + "shiftCapsOption" : { + "output" : "3" + }, + "shiftOption" : { + "output" : "3" + }, + "unmodified" : { + "output" : "3" + } + }, + "86" : { + "caps" : { + "output" : "4" + }, + "capsOption" : { + "output" : "4" + }, + "keyCode" : 86, + "option" : { + "output" : "4" + }, + "shift" : { + "output" : "4" + }, + "shiftCaps" : { + "output" : "4" + }, + "shiftCapsOption" : { + "output" : "4" + }, + "shiftOption" : { + "output" : "4" + }, + "unmodified" : { + "output" : "4" + } + }, + "87" : { + "caps" : { + "output" : "5" + }, + "capsOption" : { + "output" : "5" + }, + "keyCode" : 87, + "option" : { + "output" : "5" + }, + "shift" : { + "output" : "5" + }, + "shiftCaps" : { + "output" : "5" + }, + "shiftCapsOption" : { + "output" : "5" + }, + "shiftOption" : { + "output" : "5" + }, + "unmodified" : { + "output" : "5" + } + }, + "88" : { + "caps" : { + "output" : "6" + }, + "capsOption" : { + "output" : "6" + }, + "keyCode" : 88, + "option" : { + "output" : "6" + }, + "shift" : { + "output" : "6" + }, + "shiftCaps" : { + "output" : "6" + }, + "shiftCapsOption" : { + "output" : "6" + }, + "shiftOption" : { + "output" : "6" + }, + "unmodified" : { + "output" : "6" + } + }, + "89" : { + "caps" : { + "output" : "7" + }, + "capsOption" : { + "output" : "7" + }, + "keyCode" : 89, + "option" : { + "output" : "7" + }, + "shift" : { + "output" : "7" + }, + "shiftCaps" : { + "output" : "7" + }, + "shiftCapsOption" : { + "output" : "7" + }, + "shiftOption" : { + "output" : "7" + }, + "unmodified" : { + "output" : "7" + } + }, + "9" : { + "caps" : { + "output" : "М" + }, + "capsOption" : { + "output" : "V" + }, + "keyCode" : 9, + "option" : { + "output" : "v" + }, + "shift" : { + "output" : "М" + }, + "shiftCaps" : { + "output" : "М" + }, + "shiftCapsOption" : { + "output" : "V" + }, + "shiftOption" : { + "output" : "V" + }, + "unmodified" : { + "output" : "м" + } + }, + "90" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 90, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "91" : { + "caps" : { + "output" : "8" + }, + "capsOption" : { + "output" : "8" + }, + "keyCode" : 91, + "option" : { + "output" : "8" + }, + "shift" : { + "output" : "8" + }, + "shiftCaps" : { + "output" : "8" + }, + "shiftCapsOption" : { + "output" : "8" + }, + "shiftOption" : { + "output" : "8" + }, + "unmodified" : { + "output" : "8" + } + }, + "92" : { + "caps" : { + "output" : "9" + }, + "capsOption" : { + "output" : "9" + }, + "keyCode" : 92, + "option" : { + "output" : "9" + }, + "shift" : { + "output" : "9" + }, + "shiftCaps" : { + "output" : "9" + }, + "shiftCapsOption" : { + "output" : "9" + }, + "shiftOption" : { + "output" : "9" + }, + "unmodified" : { + "output" : "9" + } + }, + "93" : { + "caps" : { + "output" : "Ё" + }, + "capsOption" : { + "output" : "§" + }, + "keyCode" : 93, + "option" : { + "output" : "§" + }, + "shift" : { + "output" : "Ё" + }, + "shiftCaps" : { + "output" : "Ё" + }, + "shiftCapsOption" : { + "output" : "±" + }, + "shiftOption" : { + "output" : "±" + }, + "unmodified" : { + "output" : "ё" + } + }, + "94" : { + "caps" : { + "output" : "Ë" + }, + "capsOption" : { + "output" : "*" + }, + "keyCode" : 94, + "option" : { + "output" : "[" + }, + "shift" : { + "output" : "Ë" + }, + "shiftCaps" : { + "output" : "Ë" + }, + "shiftCapsOption" : { + "output" : "]" + }, + "shiftOption" : { + "output" : "]" + }, + "unmodified" : { + "output" : "ё" + } + }, + "95" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 95, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "96" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 96, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "97" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 97, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "98" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 98, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "99" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 99, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + } + }, + "keyboardType" : 91, + "osBuild" : "24G830", + "osVersion" : "15.7.9", + "schemaVersion" : 1, + "sourceID" : "com.apple.keylayout.RussianWin" +} \ No newline at end of file diff --git a/Benchmarks/layout-tables/v1/com.apple.keylayout.US@37.json b/Benchmarks/layout-tables/v1/com.apple.keylayout.US@37.json new file mode 100644 index 0000000..25d39a8 --- /dev/null +++ b/Benchmarks/layout-tables/v1/com.apple.keylayout.US@37.json @@ -0,0 +1,3465 @@ +{ + "keyOutputs" : { + "0" : { + "caps" : { + "output" : "A" + }, + "capsOption" : { + "output" : "Å" + }, + "keyCode" : 0, + "option" : { + "output" : "å" + }, + "shift" : { + "output" : "A" + }, + "shiftCaps" : { + "output" : "A" + }, + "shiftCapsOption" : { + "output" : "Å" + }, + "shiftOption" : { + "output" : "Å" + }, + "unmodified" : { + "output" : "a" + } + }, + "1" : { + "caps" : { + "output" : "S" + }, + "capsOption" : { + "output" : "Í" + }, + "keyCode" : 1, + "option" : { + "output" : "ß" + }, + "shift" : { + "output" : "S" + }, + "shiftCaps" : { + "output" : "S" + }, + "shiftCapsOption" : { + "output" : "Í" + }, + "shiftOption" : { + "output" : "Í" + }, + "unmodified" : { + "output" : "s" + } + }, + "10" : { + "caps" : { + "output" : "§" + }, + "capsOption" : { + "output" : "§" + }, + "keyCode" : 10, + "option" : { + "output" : "§" + }, + "shift" : { + "output" : "±" + }, + "shiftCaps" : { + "output" : "±" + }, + "shiftCapsOption" : { + "output" : "±" + }, + "shiftOption" : { + "output" : "±" + }, + "unmodified" : { + "output" : "§" + } + }, + "100" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 100, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "101" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 101, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "102" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 102, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "103" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 103, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "104" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 104, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "105" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 105, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "106" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 106, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "107" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 107, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "108" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 108, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "109" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 109, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "11" : { + "caps" : { + "output" : "B" + }, + "capsOption" : { + "output" : "ı" + }, + "keyCode" : 11, + "option" : { + "output" : "∫" + }, + "shift" : { + "output" : "B" + }, + "shiftCaps" : { + "output" : "B" + }, + "shiftCapsOption" : { + "output" : "ı" + }, + "shiftOption" : { + "output" : "ı" + }, + "unmodified" : { + "output" : "b" + } + }, + "110" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 110, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "111" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 111, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "112" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 112, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "113" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 113, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "114" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 114, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "115" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 115, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "116" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 116, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "117" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 117, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "118" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 118, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "119" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 119, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "12" : { + "caps" : { + "output" : "Q" + }, + "capsOption" : { + "output" : "Œ" + }, + "keyCode" : 12, + "option" : { + "output" : "œ" + }, + "shift" : { + "output" : "Q" + }, + "shiftCaps" : { + "output" : "Q" + }, + "shiftCapsOption" : { + "output" : "Œ" + }, + "shiftOption" : { + "output" : "Œ" + }, + "unmodified" : { + "output" : "q" + } + }, + "120" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 120, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "121" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 121, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "122" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 122, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "123" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 123, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "124" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 124, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "125" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 125, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "126" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 126, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "127" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 127, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "13" : { + "caps" : { + "output" : "W" + }, + "capsOption" : { + "output" : "∑" + }, + "keyCode" : 13, + "option" : { + "output" : "∑" + }, + "shift" : { + "output" : "W" + }, + "shiftCaps" : { + "output" : "W" + }, + "shiftCapsOption" : { + "output" : "„" + }, + "shiftOption" : { + "output" : "„" + }, + "unmodified" : { + "output" : "w" + } + }, + "14" : { + "caps" : { + "output" : "E" + }, + "capsOption" : { + "output" : "´" + }, + "keyCode" : 14, + "option" : { + "rejection" : "deadKey" + }, + "shift" : { + "output" : "E" + }, + "shiftCaps" : { + "output" : "E" + }, + "shiftCapsOption" : { + "output" : "´" + }, + "shiftOption" : { + "output" : "´" + }, + "unmodified" : { + "output" : "e" + } + }, + "15" : { + "caps" : { + "output" : "R" + }, + "capsOption" : { + "output" : "®" + }, + "keyCode" : 15, + "option" : { + "output" : "®" + }, + "shift" : { + "output" : "R" + }, + "shiftCaps" : { + "output" : "R" + }, + "shiftCapsOption" : { + "output" : "‰" + }, + "shiftOption" : { + "output" : "‰" + }, + "unmodified" : { + "output" : "r" + } + }, + "16" : { + "caps" : { + "output" : "Y" + }, + "capsOption" : { + "output" : "Á" + }, + "keyCode" : 16, + "option" : { + "output" : "¥" + }, + "shift" : { + "output" : "Y" + }, + "shiftCaps" : { + "output" : "Y" + }, + "shiftCapsOption" : { + "output" : "Á" + }, + "shiftOption" : { + "output" : "Á" + }, + "unmodified" : { + "output" : "y" + } + }, + "17" : { + "caps" : { + "output" : "T" + }, + "capsOption" : { + "output" : "†" + }, + "keyCode" : 17, + "option" : { + "output" : "†" + }, + "shift" : { + "output" : "T" + }, + "shiftCaps" : { + "output" : "T" + }, + "shiftCapsOption" : { + "output" : "ˇ" + }, + "shiftOption" : { + "output" : "ˇ" + }, + "unmodified" : { + "output" : "t" + } + }, + "18" : { + "caps" : { + "output" : "1" + }, + "capsOption" : { + "output" : "¡" + }, + "keyCode" : 18, + "option" : { + "output" : "¡" + }, + "shift" : { + "output" : "!" + }, + "shiftCaps" : { + "output" : "!" + }, + "shiftCapsOption" : { + "output" : "⁄" + }, + "shiftOption" : { + "output" : "⁄" + }, + "unmodified" : { + "output" : "1" + } + }, + "19" : { + "caps" : { + "output" : "2" + }, + "capsOption" : { + "output" : "™" + }, + "keyCode" : 19, + "option" : { + "output" : "™" + }, + "shift" : { + "output" : "@" + }, + "shiftCaps" : { + "output" : "@" + }, + "shiftCapsOption" : { + "output" : "€" + }, + "shiftOption" : { + "output" : "€" + }, + "unmodified" : { + "output" : "2" + } + }, + "2" : { + "caps" : { + "output" : "D" + }, + "capsOption" : { + "output" : "Î" + }, + "keyCode" : 2, + "option" : { + "output" : "∂" + }, + "shift" : { + "output" : "D" + }, + "shiftCaps" : { + "output" : "D" + }, + "shiftCapsOption" : { + "output" : "Î" + }, + "shiftOption" : { + "output" : "Î" + }, + "unmodified" : { + "output" : "d" + } + }, + "20" : { + "caps" : { + "output" : "3" + }, + "capsOption" : { + "output" : "£" + }, + "keyCode" : 20, + "option" : { + "output" : "£" + }, + "shift" : { + "output" : "#" + }, + "shiftCaps" : { + "output" : "#" + }, + "shiftCapsOption" : { + "output" : "‹" + }, + "shiftOption" : { + "output" : "‹" + }, + "unmodified" : { + "output" : "3" + } + }, + "21" : { + "caps" : { + "output" : "4" + }, + "capsOption" : { + "output" : "¢" + }, + "keyCode" : 21, + "option" : { + "output" : "¢" + }, + "shift" : { + "output" : "$" + }, + "shiftCaps" : { + "output" : "$" + }, + "shiftCapsOption" : { + "output" : "›" + }, + "shiftOption" : { + "output" : "›" + }, + "unmodified" : { + "output" : "4" + } + }, + "22" : { + "caps" : { + "output" : "6" + }, + "capsOption" : { + "output" : "§" + }, + "keyCode" : 22, + "option" : { + "output" : "§" + }, + "shift" : { + "output" : "^" + }, + "shiftCaps" : { + "output" : "^" + }, + "shiftCapsOption" : { + "output" : "fl" + }, + "shiftOption" : { + "output" : "fl" + }, + "unmodified" : { + "output" : "6" + } + }, + "23" : { + "caps" : { + "output" : "5" + }, + "capsOption" : { + "output" : "∞" + }, + "keyCode" : 23, + "option" : { + "output" : "∞" + }, + "shift" : { + "output" : "%" + }, + "shiftCaps" : { + "output" : "%" + }, + "shiftCapsOption" : { + "output" : "fi" + }, + "shiftOption" : { + "output" : "fi" + }, + "unmodified" : { + "output" : "5" + } + }, + "24" : { + "caps" : { + "output" : "=" + }, + "capsOption" : { + "output" : "≠" + }, + "keyCode" : 24, + "option" : { + "output" : "≠" + }, + "shift" : { + "output" : "+" + }, + "shiftCaps" : { + "output" : "+" + }, + "shiftCapsOption" : { + "output" : "±" + }, + "shiftOption" : { + "output" : "±" + }, + "unmodified" : { + "output" : "=" + } + }, + "25" : { + "caps" : { + "output" : "9" + }, + "capsOption" : { + "output" : "ª" + }, + "keyCode" : 25, + "option" : { + "output" : "ª" + }, + "shift" : { + "output" : "(" + }, + "shiftCaps" : { + "output" : "(" + }, + "shiftCapsOption" : { + "output" : "·" + }, + "shiftOption" : { + "output" : "·" + }, + "unmodified" : { + "output" : "9" + } + }, + "26" : { + "caps" : { + "output" : "7" + }, + "capsOption" : { + "output" : "¶" + }, + "keyCode" : 26, + "option" : { + "output" : "¶" + }, + "shift" : { + "output" : "&" + }, + "shiftCaps" : { + "output" : "&" + }, + "shiftCapsOption" : { + "output" : "‡" + }, + "shiftOption" : { + "output" : "‡" + }, + "unmodified" : { + "output" : "7" + } + }, + "27" : { + "caps" : { + "output" : "-" + }, + "capsOption" : { + "output" : "–" + }, + "keyCode" : 27, + "option" : { + "output" : "–" + }, + "shift" : { + "output" : "_" + }, + "shiftCaps" : { + "output" : "_" + }, + "shiftCapsOption" : { + "output" : "—" + }, + "shiftOption" : { + "output" : "—" + }, + "unmodified" : { + "output" : "-" + } + }, + "28" : { + "caps" : { + "output" : "8" + }, + "capsOption" : { + "output" : "•" + }, + "keyCode" : 28, + "option" : { + "output" : "•" + }, + "shift" : { + "output" : "*" + }, + "shiftCaps" : { + "output" : "*" + }, + "shiftCapsOption" : { + "output" : "°" + }, + "shiftOption" : { + "output" : "°" + }, + "unmodified" : { + "output" : "8" + } + }, + "29" : { + "caps" : { + "output" : "0" + }, + "capsOption" : { + "output" : "º" + }, + "keyCode" : 29, + "option" : { + "output" : "º" + }, + "shift" : { + "output" : ")" + }, + "shiftCaps" : { + "output" : ")" + }, + "shiftCapsOption" : { + "output" : "‚" + }, + "shiftOption" : { + "output" : "‚" + }, + "unmodified" : { + "output" : "0" + } + }, + "3" : { + "caps" : { + "output" : "F" + }, + "capsOption" : { + "output" : "Ï" + }, + "keyCode" : 3, + "option" : { + "output" : "ƒ" + }, + "shift" : { + "output" : "F" + }, + "shiftCaps" : { + "output" : "F" + }, + "shiftCapsOption" : { + "output" : "Ï" + }, + "shiftOption" : { + "output" : "Ï" + }, + "unmodified" : { + "output" : "f" + } + }, + "30" : { + "caps" : { + "output" : "]" + }, + "capsOption" : { + "output" : "‘" + }, + "keyCode" : 30, + "option" : { + "output" : "‘" + }, + "shift" : { + "output" : "}" + }, + "shiftCaps" : { + "output" : "}" + }, + "shiftCapsOption" : { + "output" : "’" + }, + "shiftOption" : { + "output" : "’" + }, + "unmodified" : { + "output" : "]" + } + }, + "31" : { + "caps" : { + "output" : "O" + }, + "capsOption" : { + "output" : "Ø" + }, + "keyCode" : 31, + "option" : { + "output" : "ø" + }, + "shift" : { + "output" : "O" + }, + "shiftCaps" : { + "output" : "O" + }, + "shiftCapsOption" : { + "output" : "Ø" + }, + "shiftOption" : { + "output" : "Ø" + }, + "unmodified" : { + "output" : "o" + } + }, + "32" : { + "caps" : { + "output" : "U" + }, + "capsOption" : { + "output" : "¨" + }, + "keyCode" : 32, + "option" : { + "rejection" : "deadKey" + }, + "shift" : { + "output" : "U" + }, + "shiftCaps" : { + "output" : "U" + }, + "shiftCapsOption" : { + "output" : "¨" + }, + "shiftOption" : { + "output" : "¨" + }, + "unmodified" : { + "output" : "u" + } + }, + "33" : { + "caps" : { + "output" : "[" + }, + "capsOption" : { + "output" : "“" + }, + "keyCode" : 33, + "option" : { + "output" : "“" + }, + "shift" : { + "output" : "{" + }, + "shiftCaps" : { + "output" : "{" + }, + "shiftCapsOption" : { + "output" : "”" + }, + "shiftOption" : { + "output" : "”" + }, + "unmodified" : { + "output" : "[" + } + }, + "34" : { + "caps" : { + "output" : "I" + }, + "capsOption" : { + "output" : "ˆ" + }, + "keyCode" : 34, + "option" : { + "rejection" : "deadKey" + }, + "shift" : { + "output" : "I" + }, + "shiftCaps" : { + "output" : "I" + }, + "shiftCapsOption" : { + "output" : "ˆ" + }, + "shiftOption" : { + "output" : "ˆ" + }, + "unmodified" : { + "output" : "i" + } + }, + "35" : { + "caps" : { + "output" : "P" + }, + "capsOption" : { + "output" : "∏" + }, + "keyCode" : 35, + "option" : { + "output" : "π" + }, + "shift" : { + "output" : "P" + }, + "shiftCaps" : { + "output" : "P" + }, + "shiftCapsOption" : { + "output" : "∏" + }, + "shiftOption" : { + "output" : "∏" + }, + "unmodified" : { + "output" : "p" + } + }, + "36" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 36, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "37" : { + "caps" : { + "output" : "L" + }, + "capsOption" : { + "output" : "Ò" + }, + "keyCode" : 37, + "option" : { + "output" : "¬" + }, + "shift" : { + "output" : "L" + }, + "shiftCaps" : { + "output" : "L" + }, + "shiftCapsOption" : { + "output" : "Ò" + }, + "shiftOption" : { + "output" : "Ò" + }, + "unmodified" : { + "output" : "l" + } + }, + "38" : { + "caps" : { + "output" : "J" + }, + "capsOption" : { + "output" : "Ô" + }, + "keyCode" : 38, + "option" : { + "output" : "∆" + }, + "shift" : { + "output" : "J" + }, + "shiftCaps" : { + "output" : "J" + }, + "shiftCapsOption" : { + "output" : "Ô" + }, + "shiftOption" : { + "output" : "Ô" + }, + "unmodified" : { + "output" : "j" + } + }, + "39" : { + "caps" : { + "output" : "'" + }, + "capsOption" : { + "output" : "Æ" + }, + "keyCode" : 39, + "option" : { + "output" : "æ" + }, + "shift" : { + "output" : "\"" + }, + "shiftCaps" : { + "output" : "\"" + }, + "shiftCapsOption" : { + "output" : "Æ" + }, + "shiftOption" : { + "output" : "Æ" + }, + "unmodified" : { + "output" : "'" + } + }, + "4" : { + "caps" : { + "output" : "H" + }, + "capsOption" : { + "output" : "Ó" + }, + "keyCode" : 4, + "option" : { + "output" : "˙" + }, + "shift" : { + "output" : "H" + }, + "shiftCaps" : { + "output" : "H" + }, + "shiftCapsOption" : { + "output" : "Ó" + }, + "shiftOption" : { + "output" : "Ó" + }, + "unmodified" : { + "output" : "h" + } + }, + "40" : { + "caps" : { + "output" : "K" + }, + "capsOption" : { + "output" : "˚" + }, + "keyCode" : 40, + "option" : { + "output" : "˚" + }, + "shift" : { + "output" : "K" + }, + "shiftCaps" : { + "output" : "K" + }, + "shiftCapsOption" : { + "rejection" : "privateUseOrSurrogate" + }, + "shiftOption" : { + "rejection" : "privateUseOrSurrogate" + }, + "unmodified" : { + "output" : "k" + } + }, + "41" : { + "caps" : { + "output" : ";" + }, + "capsOption" : { + "output" : "…" + }, + "keyCode" : 41, + "option" : { + "output" : "…" + }, + "shift" : { + "output" : ":" + }, + "shiftCaps" : { + "output" : ":" + }, + "shiftCapsOption" : { + "output" : "Ú" + }, + "shiftOption" : { + "output" : "Ú" + }, + "unmodified" : { + "output" : ";" + } + }, + "42" : { + "caps" : { + "output" : "\\" + }, + "capsOption" : { + "output" : "«" + }, + "keyCode" : 42, + "option" : { + "output" : "«" + }, + "shift" : { + "output" : "|" + }, + "shiftCaps" : { + "output" : "|" + }, + "shiftCapsOption" : { + "output" : "»" + }, + "shiftOption" : { + "output" : "»" + }, + "unmodified" : { + "output" : "\\" + } + }, + "43" : { + "caps" : { + "output" : "," + }, + "capsOption" : { + "output" : "≤" + }, + "keyCode" : 43, + "option" : { + "output" : "≤" + }, + "shift" : { + "output" : "<" + }, + "shiftCaps" : { + "output" : "<" + }, + "shiftCapsOption" : { + "output" : "¯" + }, + "shiftOption" : { + "output" : "¯" + }, + "unmodified" : { + "output" : "," + } + }, + "44" : { + "caps" : { + "output" : "\/" + }, + "capsOption" : { + "output" : "÷" + }, + "keyCode" : 44, + "option" : { + "output" : "÷" + }, + "shift" : { + "output" : "?" + }, + "shiftCaps" : { + "output" : "?" + }, + "shiftCapsOption" : { + "output" : "¿" + }, + "shiftOption" : { + "output" : "¿" + }, + "unmodified" : { + "output" : "\/" + } + }, + "45" : { + "caps" : { + "output" : "N" + }, + "capsOption" : { + "output" : "˜" + }, + "keyCode" : 45, + "option" : { + "rejection" : "deadKey" + }, + "shift" : { + "output" : "N" + }, + "shiftCaps" : { + "output" : "N" + }, + "shiftCapsOption" : { + "output" : "˜" + }, + "shiftOption" : { + "output" : "˜" + }, + "unmodified" : { + "output" : "n" + } + }, + "46" : { + "caps" : { + "output" : "M" + }, + "capsOption" : { + "output" : "Â" + }, + "keyCode" : 46, + "option" : { + "output" : "µ" + }, + "shift" : { + "output" : "M" + }, + "shiftCaps" : { + "output" : "M" + }, + "shiftCapsOption" : { + "output" : "Â" + }, + "shiftOption" : { + "output" : "Â" + }, + "unmodified" : { + "output" : "m" + } + }, + "47" : { + "caps" : { + "output" : "." + }, + "capsOption" : { + "output" : "≥" + }, + "keyCode" : 47, + "option" : { + "output" : "≥" + }, + "shift" : { + "output" : ">" + }, + "shiftCaps" : { + "output" : ">" + }, + "shiftCapsOption" : { + "output" : "˘" + }, + "shiftOption" : { + "output" : "˘" + }, + "unmodified" : { + "output" : "." + } + }, + "48" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 48, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "49" : { + "caps" : { + "output" : " " + }, + "capsOption" : { + "output" : " " + }, + "keyCode" : 49, + "option" : { + "output" : " " + }, + "shift" : { + "output" : " " + }, + "shiftCaps" : { + "output" : " " + }, + "shiftCapsOption" : { + "output" : " " + }, + "shiftOption" : { + "output" : " " + }, + "unmodified" : { + "output" : " " + } + }, + "5" : { + "caps" : { + "output" : "G" + }, + "capsOption" : { + "output" : "©" + }, + "keyCode" : 5, + "option" : { + "output" : "©" + }, + "shift" : { + "output" : "G" + }, + "shiftCaps" : { + "output" : "G" + }, + "shiftCapsOption" : { + "output" : "˝" + }, + "shiftOption" : { + "output" : "˝" + }, + "unmodified" : { + "output" : "g" + } + }, + "50" : { + "caps" : { + "output" : "`" + }, + "capsOption" : { + "output" : "`" + }, + "keyCode" : 50, + "option" : { + "rejection" : "deadKey" + }, + "shift" : { + "output" : "~" + }, + "shiftCaps" : { + "output" : "~" + }, + "shiftCapsOption" : { + "output" : "`" + }, + "shiftOption" : { + "output" : "`" + }, + "unmodified" : { + "output" : "`" + } + }, + "51" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 51, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "52" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 52, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "53" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 53, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "54" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 54, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "55" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 55, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "56" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 56, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "57" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 57, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "58" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 58, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "59" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 59, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "6" : { + "caps" : { + "output" : "Z" + }, + "capsOption" : { + "output" : "Ω" + }, + "keyCode" : 6, + "option" : { + "output" : "Ω" + }, + "shift" : { + "output" : "Z" + }, + "shiftCaps" : { + "output" : "Z" + }, + "shiftCapsOption" : { + "output" : "¸" + }, + "shiftOption" : { + "output" : "¸" + }, + "unmodified" : { + "output" : "z" + } + }, + "60" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 60, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "61" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 61, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "62" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 62, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "63" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 63, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "64" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 64, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "65" : { + "caps" : { + "output" : "." + }, + "capsOption" : { + "output" : "." + }, + "keyCode" : 65, + "option" : { + "output" : "." + }, + "shift" : { + "output" : "." + }, + "shiftCaps" : { + "output" : "." + }, + "shiftCapsOption" : { + "output" : "." + }, + "shiftOption" : { + "output" : "." + }, + "unmodified" : { + "output" : "." + } + }, + "66" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 66, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "*" + }, + "shiftCaps" : { + "output" : "*" + }, + "shiftCapsOption" : { + "output" : "*" + }, + "shiftOption" : { + "output" : "*" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "67" : { + "caps" : { + "output" : "*" + }, + "capsOption" : { + "output" : "*" + }, + "keyCode" : 67, + "option" : { + "output" : "*" + }, + "shift" : { + "output" : "*" + }, + "shiftCaps" : { + "output" : "*" + }, + "shiftCapsOption" : { + "output" : "*" + }, + "shiftOption" : { + "output" : "*" + }, + "unmodified" : { + "output" : "*" + } + }, + "68" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 68, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "69" : { + "caps" : { + "output" : "+" + }, + "capsOption" : { + "output" : "+" + }, + "keyCode" : 69, + "option" : { + "output" : "+" + }, + "shift" : { + "output" : "+" + }, + "shiftCaps" : { + "output" : "+" + }, + "shiftCapsOption" : { + "output" : "+" + }, + "shiftOption" : { + "output" : "+" + }, + "unmodified" : { + "output" : "+" + } + }, + "7" : { + "caps" : { + "output" : "X" + }, + "capsOption" : { + "output" : "≈" + }, + "keyCode" : 7, + "option" : { + "output" : "≈" + }, + "shift" : { + "output" : "X" + }, + "shiftCaps" : { + "output" : "X" + }, + "shiftCapsOption" : { + "output" : "˛" + }, + "shiftOption" : { + "output" : "˛" + }, + "unmodified" : { + "output" : "x" + } + }, + "70" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 70, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "+" + }, + "shiftCaps" : { + "output" : "+" + }, + "shiftCapsOption" : { + "output" : "+" + }, + "shiftOption" : { + "output" : "+" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "71" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 71, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "72" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 72, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "=" + }, + "shiftCaps" : { + "output" : "=" + }, + "shiftCapsOption" : { + "output" : "=" + }, + "shiftOption" : { + "output" : "=" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "73" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 73, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "74" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 74, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "75" : { + "caps" : { + "output" : "\/" + }, + "capsOption" : { + "output" : "\/" + }, + "keyCode" : 75, + "option" : { + "output" : "\/" + }, + "shift" : { + "output" : "\/" + }, + "shiftCaps" : { + "output" : "\/" + }, + "shiftCapsOption" : { + "output" : "\/" + }, + "shiftOption" : { + "output" : "\/" + }, + "unmodified" : { + "output" : "\/" + } + }, + "76" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 76, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "77" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 77, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "\/" + }, + "shiftCaps" : { + "output" : "\/" + }, + "shiftCapsOption" : { + "output" : "\/" + }, + "shiftOption" : { + "output" : "\/" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "78" : { + "caps" : { + "output" : "-" + }, + "capsOption" : { + "output" : "-" + }, + "keyCode" : 78, + "option" : { + "output" : "-" + }, + "shift" : { + "output" : "-" + }, + "shiftCaps" : { + "output" : "-" + }, + "shiftCapsOption" : { + "output" : "-" + }, + "shiftOption" : { + "output" : "-" + }, + "unmodified" : { + "output" : "-" + } + }, + "79" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 79, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "8" : { + "caps" : { + "output" : "C" + }, + "capsOption" : { + "output" : "Ç" + }, + "keyCode" : 8, + "option" : { + "output" : "ç" + }, + "shift" : { + "output" : "C" + }, + "shiftCaps" : { + "output" : "C" + }, + "shiftCapsOption" : { + "output" : "Ç" + }, + "shiftOption" : { + "output" : "Ç" + }, + "unmodified" : { + "output" : "c" + } + }, + "80" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 80, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "81" : { + "caps" : { + "output" : "=" + }, + "capsOption" : { + "output" : "=" + }, + "keyCode" : 81, + "option" : { + "output" : "=" + }, + "shift" : { + "output" : "=" + }, + "shiftCaps" : { + "output" : "=" + }, + "shiftCapsOption" : { + "output" : "=" + }, + "shiftOption" : { + "output" : "=" + }, + "unmodified" : { + "output" : "=" + } + }, + "82" : { + "caps" : { + "output" : "0" + }, + "capsOption" : { + "output" : "0" + }, + "keyCode" : 82, + "option" : { + "output" : "0" + }, + "shift" : { + "output" : "0" + }, + "shiftCaps" : { + "output" : "0" + }, + "shiftCapsOption" : { + "output" : "0" + }, + "shiftOption" : { + "output" : "0" + }, + "unmodified" : { + "output" : "0" + } + }, + "83" : { + "caps" : { + "output" : "1" + }, + "capsOption" : { + "output" : "1" + }, + "keyCode" : 83, + "option" : { + "output" : "1" + }, + "shift" : { + "output" : "1" + }, + "shiftCaps" : { + "output" : "1" + }, + "shiftCapsOption" : { + "output" : "1" + }, + "shiftOption" : { + "output" : "1" + }, + "unmodified" : { + "output" : "1" + } + }, + "84" : { + "caps" : { + "output" : "2" + }, + "capsOption" : { + "output" : "2" + }, + "keyCode" : 84, + "option" : { + "output" : "2" + }, + "shift" : { + "output" : "2" + }, + "shiftCaps" : { + "output" : "2" + }, + "shiftCapsOption" : { + "output" : "2" + }, + "shiftOption" : { + "output" : "2" + }, + "unmodified" : { + "output" : "2" + } + }, + "85" : { + "caps" : { + "output" : "3" + }, + "capsOption" : { + "output" : "3" + }, + "keyCode" : 85, + "option" : { + "output" : "3" + }, + "shift" : { + "output" : "3" + }, + "shiftCaps" : { + "output" : "3" + }, + "shiftCapsOption" : { + "output" : "3" + }, + "shiftOption" : { + "output" : "3" + }, + "unmodified" : { + "output" : "3" + } + }, + "86" : { + "caps" : { + "output" : "4" + }, + "capsOption" : { + "output" : "4" + }, + "keyCode" : 86, + "option" : { + "output" : "4" + }, + "shift" : { + "output" : "4" + }, + "shiftCaps" : { + "output" : "4" + }, + "shiftCapsOption" : { + "output" : "4" + }, + "shiftOption" : { + "output" : "4" + }, + "unmodified" : { + "output" : "4" + } + }, + "87" : { + "caps" : { + "output" : "5" + }, + "capsOption" : { + "output" : "5" + }, + "keyCode" : 87, + "option" : { + "output" : "5" + }, + "shift" : { + "output" : "5" + }, + "shiftCaps" : { + "output" : "5" + }, + "shiftCapsOption" : { + "output" : "5" + }, + "shiftOption" : { + "output" : "5" + }, + "unmodified" : { + "output" : "5" + } + }, + "88" : { + "caps" : { + "output" : "6" + }, + "capsOption" : { + "output" : "6" + }, + "keyCode" : 88, + "option" : { + "output" : "6" + }, + "shift" : { + "output" : "6" + }, + "shiftCaps" : { + "output" : "6" + }, + "shiftCapsOption" : { + "output" : "6" + }, + "shiftOption" : { + "output" : "6" + }, + "unmodified" : { + "output" : "6" + } + }, + "89" : { + "caps" : { + "output" : "7" + }, + "capsOption" : { + "output" : "7" + }, + "keyCode" : 89, + "option" : { + "output" : "7" + }, + "shift" : { + "output" : "7" + }, + "shiftCaps" : { + "output" : "7" + }, + "shiftCapsOption" : { + "output" : "7" + }, + "shiftOption" : { + "output" : "7" + }, + "unmodified" : { + "output" : "7" + } + }, + "9" : { + "caps" : { + "output" : "V" + }, + "capsOption" : { + "output" : "√" + }, + "keyCode" : 9, + "option" : { + "output" : "√" + }, + "shift" : { + "output" : "V" + }, + "shiftCaps" : { + "output" : "V" + }, + "shiftCapsOption" : { + "output" : "◊" + }, + "shiftOption" : { + "output" : "◊" + }, + "unmodified" : { + "output" : "v" + } + }, + "90" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 90, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "91" : { + "caps" : { + "output" : "8" + }, + "capsOption" : { + "output" : "8" + }, + "keyCode" : 91, + "option" : { + "output" : "8" + }, + "shift" : { + "output" : "8" + }, + "shiftCaps" : { + "output" : "8" + }, + "shiftCapsOption" : { + "output" : "8" + }, + "shiftOption" : { + "output" : "8" + }, + "unmodified" : { + "output" : "8" + } + }, + "92" : { + "caps" : { + "output" : "9" + }, + "capsOption" : { + "output" : "9" + }, + "keyCode" : 92, + "option" : { + "output" : "9" + }, + "shift" : { + "output" : "9" + }, + "shiftCaps" : { + "output" : "9" + }, + "shiftCapsOption" : { + "output" : "9" + }, + "shiftOption" : { + "output" : "9" + }, + "unmodified" : { + "output" : "9" + } + }, + "93" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 93, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "94" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 94, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "95" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 95, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "96" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 96, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "97" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 97, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "98" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 98, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "99" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 99, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + } + }, + "keyboardType" : 37, + "osBuild" : "24G830", + "osVersion" : "15.7.9", + "schemaVersion" : 1, + "sourceID" : "com.apple.keylayout.US" +} \ No newline at end of file diff --git a/Benchmarks/layout-tables/v1/com.apple.keylayout.US@91.json b/Benchmarks/layout-tables/v1/com.apple.keylayout.US@91.json new file mode 100644 index 0000000..0c15602 --- /dev/null +++ b/Benchmarks/layout-tables/v1/com.apple.keylayout.US@91.json @@ -0,0 +1,3465 @@ +{ + "keyOutputs" : { + "0" : { + "caps" : { + "output" : "A" + }, + "capsOption" : { + "output" : "Å" + }, + "keyCode" : 0, + "option" : { + "output" : "å" + }, + "shift" : { + "output" : "A" + }, + "shiftCaps" : { + "output" : "A" + }, + "shiftCapsOption" : { + "output" : "Å" + }, + "shiftOption" : { + "output" : "Å" + }, + "unmodified" : { + "output" : "a" + } + }, + "1" : { + "caps" : { + "output" : "S" + }, + "capsOption" : { + "output" : "Í" + }, + "keyCode" : 1, + "option" : { + "output" : "ß" + }, + "shift" : { + "output" : "S" + }, + "shiftCaps" : { + "output" : "S" + }, + "shiftCapsOption" : { + "output" : "Í" + }, + "shiftOption" : { + "output" : "Í" + }, + "unmodified" : { + "output" : "s" + } + }, + "10" : { + "caps" : { + "output" : "§" + }, + "capsOption" : { + "output" : "§" + }, + "keyCode" : 10, + "option" : { + "output" : "§" + }, + "shift" : { + "output" : "±" + }, + "shiftCaps" : { + "output" : "±" + }, + "shiftCapsOption" : { + "output" : "±" + }, + "shiftOption" : { + "output" : "±" + }, + "unmodified" : { + "output" : "§" + } + }, + "100" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 100, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "101" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 101, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "102" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 102, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "103" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 103, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "104" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 104, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "105" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 105, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "106" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 106, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "107" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 107, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "108" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 108, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "109" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 109, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "11" : { + "caps" : { + "output" : "B" + }, + "capsOption" : { + "output" : "ı" + }, + "keyCode" : 11, + "option" : { + "output" : "∫" + }, + "shift" : { + "output" : "B" + }, + "shiftCaps" : { + "output" : "B" + }, + "shiftCapsOption" : { + "output" : "ı" + }, + "shiftOption" : { + "output" : "ı" + }, + "unmodified" : { + "output" : "b" + } + }, + "110" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 110, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "111" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 111, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "112" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 112, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "113" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 113, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "114" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 114, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "115" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 115, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "116" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 116, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "117" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 117, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "118" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 118, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "119" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 119, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "12" : { + "caps" : { + "output" : "Q" + }, + "capsOption" : { + "output" : "Œ" + }, + "keyCode" : 12, + "option" : { + "output" : "œ" + }, + "shift" : { + "output" : "Q" + }, + "shiftCaps" : { + "output" : "Q" + }, + "shiftCapsOption" : { + "output" : "Œ" + }, + "shiftOption" : { + "output" : "Œ" + }, + "unmodified" : { + "output" : "q" + } + }, + "120" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 120, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "121" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 121, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "122" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 122, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "123" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 123, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "124" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 124, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "125" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 125, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "126" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 126, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "127" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 127, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "13" : { + "caps" : { + "output" : "W" + }, + "capsOption" : { + "output" : "∑" + }, + "keyCode" : 13, + "option" : { + "output" : "∑" + }, + "shift" : { + "output" : "W" + }, + "shiftCaps" : { + "output" : "W" + }, + "shiftCapsOption" : { + "output" : "„" + }, + "shiftOption" : { + "output" : "„" + }, + "unmodified" : { + "output" : "w" + } + }, + "14" : { + "caps" : { + "output" : "E" + }, + "capsOption" : { + "output" : "´" + }, + "keyCode" : 14, + "option" : { + "rejection" : "deadKey" + }, + "shift" : { + "output" : "E" + }, + "shiftCaps" : { + "output" : "E" + }, + "shiftCapsOption" : { + "output" : "´" + }, + "shiftOption" : { + "output" : "´" + }, + "unmodified" : { + "output" : "e" + } + }, + "15" : { + "caps" : { + "output" : "R" + }, + "capsOption" : { + "output" : "®" + }, + "keyCode" : 15, + "option" : { + "output" : "®" + }, + "shift" : { + "output" : "R" + }, + "shiftCaps" : { + "output" : "R" + }, + "shiftCapsOption" : { + "output" : "‰" + }, + "shiftOption" : { + "output" : "‰" + }, + "unmodified" : { + "output" : "r" + } + }, + "16" : { + "caps" : { + "output" : "Y" + }, + "capsOption" : { + "output" : "Á" + }, + "keyCode" : 16, + "option" : { + "output" : "¥" + }, + "shift" : { + "output" : "Y" + }, + "shiftCaps" : { + "output" : "Y" + }, + "shiftCapsOption" : { + "output" : "Á" + }, + "shiftOption" : { + "output" : "Á" + }, + "unmodified" : { + "output" : "y" + } + }, + "17" : { + "caps" : { + "output" : "T" + }, + "capsOption" : { + "output" : "†" + }, + "keyCode" : 17, + "option" : { + "output" : "†" + }, + "shift" : { + "output" : "T" + }, + "shiftCaps" : { + "output" : "T" + }, + "shiftCapsOption" : { + "output" : "ˇ" + }, + "shiftOption" : { + "output" : "ˇ" + }, + "unmodified" : { + "output" : "t" + } + }, + "18" : { + "caps" : { + "output" : "1" + }, + "capsOption" : { + "output" : "¡" + }, + "keyCode" : 18, + "option" : { + "output" : "¡" + }, + "shift" : { + "output" : "!" + }, + "shiftCaps" : { + "output" : "!" + }, + "shiftCapsOption" : { + "output" : "⁄" + }, + "shiftOption" : { + "output" : "⁄" + }, + "unmodified" : { + "output" : "1" + } + }, + "19" : { + "caps" : { + "output" : "2" + }, + "capsOption" : { + "output" : "™" + }, + "keyCode" : 19, + "option" : { + "output" : "™" + }, + "shift" : { + "output" : "@" + }, + "shiftCaps" : { + "output" : "@" + }, + "shiftCapsOption" : { + "output" : "€" + }, + "shiftOption" : { + "output" : "€" + }, + "unmodified" : { + "output" : "2" + } + }, + "2" : { + "caps" : { + "output" : "D" + }, + "capsOption" : { + "output" : "Î" + }, + "keyCode" : 2, + "option" : { + "output" : "∂" + }, + "shift" : { + "output" : "D" + }, + "shiftCaps" : { + "output" : "D" + }, + "shiftCapsOption" : { + "output" : "Î" + }, + "shiftOption" : { + "output" : "Î" + }, + "unmodified" : { + "output" : "d" + } + }, + "20" : { + "caps" : { + "output" : "3" + }, + "capsOption" : { + "output" : "£" + }, + "keyCode" : 20, + "option" : { + "output" : "£" + }, + "shift" : { + "output" : "#" + }, + "shiftCaps" : { + "output" : "#" + }, + "shiftCapsOption" : { + "output" : "‹" + }, + "shiftOption" : { + "output" : "‹" + }, + "unmodified" : { + "output" : "3" + } + }, + "21" : { + "caps" : { + "output" : "4" + }, + "capsOption" : { + "output" : "¢" + }, + "keyCode" : 21, + "option" : { + "output" : "¢" + }, + "shift" : { + "output" : "$" + }, + "shiftCaps" : { + "output" : "$" + }, + "shiftCapsOption" : { + "output" : "›" + }, + "shiftOption" : { + "output" : "›" + }, + "unmodified" : { + "output" : "4" + } + }, + "22" : { + "caps" : { + "output" : "6" + }, + "capsOption" : { + "output" : "§" + }, + "keyCode" : 22, + "option" : { + "output" : "§" + }, + "shift" : { + "output" : "^" + }, + "shiftCaps" : { + "output" : "^" + }, + "shiftCapsOption" : { + "output" : "fl" + }, + "shiftOption" : { + "output" : "fl" + }, + "unmodified" : { + "output" : "6" + } + }, + "23" : { + "caps" : { + "output" : "5" + }, + "capsOption" : { + "output" : "∞" + }, + "keyCode" : 23, + "option" : { + "output" : "∞" + }, + "shift" : { + "output" : "%" + }, + "shiftCaps" : { + "output" : "%" + }, + "shiftCapsOption" : { + "output" : "fi" + }, + "shiftOption" : { + "output" : "fi" + }, + "unmodified" : { + "output" : "5" + } + }, + "24" : { + "caps" : { + "output" : "=" + }, + "capsOption" : { + "output" : "≠" + }, + "keyCode" : 24, + "option" : { + "output" : "≠" + }, + "shift" : { + "output" : "+" + }, + "shiftCaps" : { + "output" : "+" + }, + "shiftCapsOption" : { + "output" : "±" + }, + "shiftOption" : { + "output" : "±" + }, + "unmodified" : { + "output" : "=" + } + }, + "25" : { + "caps" : { + "output" : "9" + }, + "capsOption" : { + "output" : "ª" + }, + "keyCode" : 25, + "option" : { + "output" : "ª" + }, + "shift" : { + "output" : "(" + }, + "shiftCaps" : { + "output" : "(" + }, + "shiftCapsOption" : { + "output" : "·" + }, + "shiftOption" : { + "output" : "·" + }, + "unmodified" : { + "output" : "9" + } + }, + "26" : { + "caps" : { + "output" : "7" + }, + "capsOption" : { + "output" : "¶" + }, + "keyCode" : 26, + "option" : { + "output" : "¶" + }, + "shift" : { + "output" : "&" + }, + "shiftCaps" : { + "output" : "&" + }, + "shiftCapsOption" : { + "output" : "‡" + }, + "shiftOption" : { + "output" : "‡" + }, + "unmodified" : { + "output" : "7" + } + }, + "27" : { + "caps" : { + "output" : "-" + }, + "capsOption" : { + "output" : "–" + }, + "keyCode" : 27, + "option" : { + "output" : "–" + }, + "shift" : { + "output" : "_" + }, + "shiftCaps" : { + "output" : "_" + }, + "shiftCapsOption" : { + "output" : "—" + }, + "shiftOption" : { + "output" : "—" + }, + "unmodified" : { + "output" : "-" + } + }, + "28" : { + "caps" : { + "output" : "8" + }, + "capsOption" : { + "output" : "•" + }, + "keyCode" : 28, + "option" : { + "output" : "•" + }, + "shift" : { + "output" : "*" + }, + "shiftCaps" : { + "output" : "*" + }, + "shiftCapsOption" : { + "output" : "°" + }, + "shiftOption" : { + "output" : "°" + }, + "unmodified" : { + "output" : "8" + } + }, + "29" : { + "caps" : { + "output" : "0" + }, + "capsOption" : { + "output" : "º" + }, + "keyCode" : 29, + "option" : { + "output" : "º" + }, + "shift" : { + "output" : ")" + }, + "shiftCaps" : { + "output" : ")" + }, + "shiftCapsOption" : { + "output" : "‚" + }, + "shiftOption" : { + "output" : "‚" + }, + "unmodified" : { + "output" : "0" + } + }, + "3" : { + "caps" : { + "output" : "F" + }, + "capsOption" : { + "output" : "Ï" + }, + "keyCode" : 3, + "option" : { + "output" : "ƒ" + }, + "shift" : { + "output" : "F" + }, + "shiftCaps" : { + "output" : "F" + }, + "shiftCapsOption" : { + "output" : "Ï" + }, + "shiftOption" : { + "output" : "Ï" + }, + "unmodified" : { + "output" : "f" + } + }, + "30" : { + "caps" : { + "output" : "]" + }, + "capsOption" : { + "output" : "‘" + }, + "keyCode" : 30, + "option" : { + "output" : "‘" + }, + "shift" : { + "output" : "}" + }, + "shiftCaps" : { + "output" : "}" + }, + "shiftCapsOption" : { + "output" : "’" + }, + "shiftOption" : { + "output" : "’" + }, + "unmodified" : { + "output" : "]" + } + }, + "31" : { + "caps" : { + "output" : "O" + }, + "capsOption" : { + "output" : "Ø" + }, + "keyCode" : 31, + "option" : { + "output" : "ø" + }, + "shift" : { + "output" : "O" + }, + "shiftCaps" : { + "output" : "O" + }, + "shiftCapsOption" : { + "output" : "Ø" + }, + "shiftOption" : { + "output" : "Ø" + }, + "unmodified" : { + "output" : "o" + } + }, + "32" : { + "caps" : { + "output" : "U" + }, + "capsOption" : { + "output" : "¨" + }, + "keyCode" : 32, + "option" : { + "rejection" : "deadKey" + }, + "shift" : { + "output" : "U" + }, + "shiftCaps" : { + "output" : "U" + }, + "shiftCapsOption" : { + "output" : "¨" + }, + "shiftOption" : { + "output" : "¨" + }, + "unmodified" : { + "output" : "u" + } + }, + "33" : { + "caps" : { + "output" : "[" + }, + "capsOption" : { + "output" : "“" + }, + "keyCode" : 33, + "option" : { + "output" : "“" + }, + "shift" : { + "output" : "{" + }, + "shiftCaps" : { + "output" : "{" + }, + "shiftCapsOption" : { + "output" : "”" + }, + "shiftOption" : { + "output" : "”" + }, + "unmodified" : { + "output" : "[" + } + }, + "34" : { + "caps" : { + "output" : "I" + }, + "capsOption" : { + "output" : "ˆ" + }, + "keyCode" : 34, + "option" : { + "rejection" : "deadKey" + }, + "shift" : { + "output" : "I" + }, + "shiftCaps" : { + "output" : "I" + }, + "shiftCapsOption" : { + "output" : "ˆ" + }, + "shiftOption" : { + "output" : "ˆ" + }, + "unmodified" : { + "output" : "i" + } + }, + "35" : { + "caps" : { + "output" : "P" + }, + "capsOption" : { + "output" : "∏" + }, + "keyCode" : 35, + "option" : { + "output" : "π" + }, + "shift" : { + "output" : "P" + }, + "shiftCaps" : { + "output" : "P" + }, + "shiftCapsOption" : { + "output" : "∏" + }, + "shiftOption" : { + "output" : "∏" + }, + "unmodified" : { + "output" : "p" + } + }, + "36" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 36, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "37" : { + "caps" : { + "output" : "L" + }, + "capsOption" : { + "output" : "Ò" + }, + "keyCode" : 37, + "option" : { + "output" : "¬" + }, + "shift" : { + "output" : "L" + }, + "shiftCaps" : { + "output" : "L" + }, + "shiftCapsOption" : { + "output" : "Ò" + }, + "shiftOption" : { + "output" : "Ò" + }, + "unmodified" : { + "output" : "l" + } + }, + "38" : { + "caps" : { + "output" : "J" + }, + "capsOption" : { + "output" : "Ô" + }, + "keyCode" : 38, + "option" : { + "output" : "∆" + }, + "shift" : { + "output" : "J" + }, + "shiftCaps" : { + "output" : "J" + }, + "shiftCapsOption" : { + "output" : "Ô" + }, + "shiftOption" : { + "output" : "Ô" + }, + "unmodified" : { + "output" : "j" + } + }, + "39" : { + "caps" : { + "output" : "'" + }, + "capsOption" : { + "output" : "Æ" + }, + "keyCode" : 39, + "option" : { + "output" : "æ" + }, + "shift" : { + "output" : "\"" + }, + "shiftCaps" : { + "output" : "\"" + }, + "shiftCapsOption" : { + "output" : "Æ" + }, + "shiftOption" : { + "output" : "Æ" + }, + "unmodified" : { + "output" : "'" + } + }, + "4" : { + "caps" : { + "output" : "H" + }, + "capsOption" : { + "output" : "Ó" + }, + "keyCode" : 4, + "option" : { + "output" : "˙" + }, + "shift" : { + "output" : "H" + }, + "shiftCaps" : { + "output" : "H" + }, + "shiftCapsOption" : { + "output" : "Ó" + }, + "shiftOption" : { + "output" : "Ó" + }, + "unmodified" : { + "output" : "h" + } + }, + "40" : { + "caps" : { + "output" : "K" + }, + "capsOption" : { + "output" : "˚" + }, + "keyCode" : 40, + "option" : { + "output" : "˚" + }, + "shift" : { + "output" : "K" + }, + "shiftCaps" : { + "output" : "K" + }, + "shiftCapsOption" : { + "rejection" : "privateUseOrSurrogate" + }, + "shiftOption" : { + "rejection" : "privateUseOrSurrogate" + }, + "unmodified" : { + "output" : "k" + } + }, + "41" : { + "caps" : { + "output" : ";" + }, + "capsOption" : { + "output" : "…" + }, + "keyCode" : 41, + "option" : { + "output" : "…" + }, + "shift" : { + "output" : ":" + }, + "shiftCaps" : { + "output" : ":" + }, + "shiftCapsOption" : { + "output" : "Ú" + }, + "shiftOption" : { + "output" : "Ú" + }, + "unmodified" : { + "output" : ";" + } + }, + "42" : { + "caps" : { + "output" : "\\" + }, + "capsOption" : { + "output" : "«" + }, + "keyCode" : 42, + "option" : { + "output" : "«" + }, + "shift" : { + "output" : "|" + }, + "shiftCaps" : { + "output" : "|" + }, + "shiftCapsOption" : { + "output" : "»" + }, + "shiftOption" : { + "output" : "»" + }, + "unmodified" : { + "output" : "\\" + } + }, + "43" : { + "caps" : { + "output" : "," + }, + "capsOption" : { + "output" : "≤" + }, + "keyCode" : 43, + "option" : { + "output" : "≤" + }, + "shift" : { + "output" : "<" + }, + "shiftCaps" : { + "output" : "<" + }, + "shiftCapsOption" : { + "output" : "¯" + }, + "shiftOption" : { + "output" : "¯" + }, + "unmodified" : { + "output" : "," + } + }, + "44" : { + "caps" : { + "output" : "\/" + }, + "capsOption" : { + "output" : "÷" + }, + "keyCode" : 44, + "option" : { + "output" : "÷" + }, + "shift" : { + "output" : "?" + }, + "shiftCaps" : { + "output" : "?" + }, + "shiftCapsOption" : { + "output" : "¿" + }, + "shiftOption" : { + "output" : "¿" + }, + "unmodified" : { + "output" : "\/" + } + }, + "45" : { + "caps" : { + "output" : "N" + }, + "capsOption" : { + "output" : "˜" + }, + "keyCode" : 45, + "option" : { + "rejection" : "deadKey" + }, + "shift" : { + "output" : "N" + }, + "shiftCaps" : { + "output" : "N" + }, + "shiftCapsOption" : { + "output" : "˜" + }, + "shiftOption" : { + "output" : "˜" + }, + "unmodified" : { + "output" : "n" + } + }, + "46" : { + "caps" : { + "output" : "M" + }, + "capsOption" : { + "output" : "Â" + }, + "keyCode" : 46, + "option" : { + "output" : "µ" + }, + "shift" : { + "output" : "M" + }, + "shiftCaps" : { + "output" : "M" + }, + "shiftCapsOption" : { + "output" : "Â" + }, + "shiftOption" : { + "output" : "Â" + }, + "unmodified" : { + "output" : "m" + } + }, + "47" : { + "caps" : { + "output" : "." + }, + "capsOption" : { + "output" : "≥" + }, + "keyCode" : 47, + "option" : { + "output" : "≥" + }, + "shift" : { + "output" : ">" + }, + "shiftCaps" : { + "output" : ">" + }, + "shiftCapsOption" : { + "output" : "˘" + }, + "shiftOption" : { + "output" : "˘" + }, + "unmodified" : { + "output" : "." + } + }, + "48" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 48, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "49" : { + "caps" : { + "output" : " " + }, + "capsOption" : { + "output" : " " + }, + "keyCode" : 49, + "option" : { + "output" : " " + }, + "shift" : { + "output" : " " + }, + "shiftCaps" : { + "output" : " " + }, + "shiftCapsOption" : { + "output" : " " + }, + "shiftOption" : { + "output" : " " + }, + "unmodified" : { + "output" : " " + } + }, + "5" : { + "caps" : { + "output" : "G" + }, + "capsOption" : { + "output" : "©" + }, + "keyCode" : 5, + "option" : { + "output" : "©" + }, + "shift" : { + "output" : "G" + }, + "shiftCaps" : { + "output" : "G" + }, + "shiftCapsOption" : { + "output" : "˝" + }, + "shiftOption" : { + "output" : "˝" + }, + "unmodified" : { + "output" : "g" + } + }, + "50" : { + "caps" : { + "output" : "`" + }, + "capsOption" : { + "output" : "`" + }, + "keyCode" : 50, + "option" : { + "rejection" : "deadKey" + }, + "shift" : { + "output" : "~" + }, + "shiftCaps" : { + "output" : "~" + }, + "shiftCapsOption" : { + "output" : "`" + }, + "shiftOption" : { + "output" : "`" + }, + "unmodified" : { + "output" : "`" + } + }, + "51" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 51, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "52" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 52, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "53" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 53, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "54" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 54, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "55" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 55, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "56" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 56, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "57" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 57, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "58" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 58, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "59" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 59, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "6" : { + "caps" : { + "output" : "Z" + }, + "capsOption" : { + "output" : "Ω" + }, + "keyCode" : 6, + "option" : { + "output" : "Ω" + }, + "shift" : { + "output" : "Z" + }, + "shiftCaps" : { + "output" : "Z" + }, + "shiftCapsOption" : { + "output" : "¸" + }, + "shiftOption" : { + "output" : "¸" + }, + "unmodified" : { + "output" : "z" + } + }, + "60" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 60, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "61" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 61, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "62" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 62, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "63" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 63, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "64" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 64, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "65" : { + "caps" : { + "output" : "." + }, + "capsOption" : { + "output" : "." + }, + "keyCode" : 65, + "option" : { + "output" : "." + }, + "shift" : { + "output" : "." + }, + "shiftCaps" : { + "output" : "." + }, + "shiftCapsOption" : { + "output" : "." + }, + "shiftOption" : { + "output" : "." + }, + "unmodified" : { + "output" : "." + } + }, + "66" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 66, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "*" + }, + "shiftCaps" : { + "output" : "*" + }, + "shiftCapsOption" : { + "output" : "*" + }, + "shiftOption" : { + "output" : "*" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "67" : { + "caps" : { + "output" : "*" + }, + "capsOption" : { + "output" : "*" + }, + "keyCode" : 67, + "option" : { + "output" : "*" + }, + "shift" : { + "output" : "*" + }, + "shiftCaps" : { + "output" : "*" + }, + "shiftCapsOption" : { + "output" : "*" + }, + "shiftOption" : { + "output" : "*" + }, + "unmodified" : { + "output" : "*" + } + }, + "68" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 68, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "69" : { + "caps" : { + "output" : "+" + }, + "capsOption" : { + "output" : "+" + }, + "keyCode" : 69, + "option" : { + "output" : "+" + }, + "shift" : { + "output" : "+" + }, + "shiftCaps" : { + "output" : "+" + }, + "shiftCapsOption" : { + "output" : "+" + }, + "shiftOption" : { + "output" : "+" + }, + "unmodified" : { + "output" : "+" + } + }, + "7" : { + "caps" : { + "output" : "X" + }, + "capsOption" : { + "output" : "≈" + }, + "keyCode" : 7, + "option" : { + "output" : "≈" + }, + "shift" : { + "output" : "X" + }, + "shiftCaps" : { + "output" : "X" + }, + "shiftCapsOption" : { + "output" : "˛" + }, + "shiftOption" : { + "output" : "˛" + }, + "unmodified" : { + "output" : "x" + } + }, + "70" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 70, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "+" + }, + "shiftCaps" : { + "output" : "+" + }, + "shiftCapsOption" : { + "output" : "+" + }, + "shiftOption" : { + "output" : "+" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "71" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 71, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "72" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 72, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "=" + }, + "shiftCaps" : { + "output" : "=" + }, + "shiftCapsOption" : { + "output" : "=" + }, + "shiftOption" : { + "output" : "=" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "73" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 73, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "74" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 74, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "75" : { + "caps" : { + "output" : "\/" + }, + "capsOption" : { + "output" : "\/" + }, + "keyCode" : 75, + "option" : { + "output" : "\/" + }, + "shift" : { + "output" : "\/" + }, + "shiftCaps" : { + "output" : "\/" + }, + "shiftCapsOption" : { + "output" : "\/" + }, + "shiftOption" : { + "output" : "\/" + }, + "unmodified" : { + "output" : "\/" + } + }, + "76" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 76, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "77" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 77, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "\/" + }, + "shiftCaps" : { + "output" : "\/" + }, + "shiftCapsOption" : { + "output" : "\/" + }, + "shiftOption" : { + "output" : "\/" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "78" : { + "caps" : { + "output" : "-" + }, + "capsOption" : { + "output" : "-" + }, + "keyCode" : 78, + "option" : { + "output" : "-" + }, + "shift" : { + "output" : "-" + }, + "shiftCaps" : { + "output" : "-" + }, + "shiftCapsOption" : { + "output" : "-" + }, + "shiftOption" : { + "output" : "-" + }, + "unmodified" : { + "output" : "-" + } + }, + "79" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 79, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "8" : { + "caps" : { + "output" : "C" + }, + "capsOption" : { + "output" : "Ç" + }, + "keyCode" : 8, + "option" : { + "output" : "ç" + }, + "shift" : { + "output" : "C" + }, + "shiftCaps" : { + "output" : "C" + }, + "shiftCapsOption" : { + "output" : "Ç" + }, + "shiftOption" : { + "output" : "Ç" + }, + "unmodified" : { + "output" : "c" + } + }, + "80" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 80, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "81" : { + "caps" : { + "output" : "=" + }, + "capsOption" : { + "output" : "=" + }, + "keyCode" : 81, + "option" : { + "output" : "=" + }, + "shift" : { + "output" : "=" + }, + "shiftCaps" : { + "output" : "=" + }, + "shiftCapsOption" : { + "output" : "=" + }, + "shiftOption" : { + "output" : "=" + }, + "unmodified" : { + "output" : "=" + } + }, + "82" : { + "caps" : { + "output" : "0" + }, + "capsOption" : { + "output" : "0" + }, + "keyCode" : 82, + "option" : { + "output" : "0" + }, + "shift" : { + "output" : "0" + }, + "shiftCaps" : { + "output" : "0" + }, + "shiftCapsOption" : { + "output" : "0" + }, + "shiftOption" : { + "output" : "0" + }, + "unmodified" : { + "output" : "0" + } + }, + "83" : { + "caps" : { + "output" : "1" + }, + "capsOption" : { + "output" : "1" + }, + "keyCode" : 83, + "option" : { + "output" : "1" + }, + "shift" : { + "output" : "1" + }, + "shiftCaps" : { + "output" : "1" + }, + "shiftCapsOption" : { + "output" : "1" + }, + "shiftOption" : { + "output" : "1" + }, + "unmodified" : { + "output" : "1" + } + }, + "84" : { + "caps" : { + "output" : "2" + }, + "capsOption" : { + "output" : "2" + }, + "keyCode" : 84, + "option" : { + "output" : "2" + }, + "shift" : { + "output" : "2" + }, + "shiftCaps" : { + "output" : "2" + }, + "shiftCapsOption" : { + "output" : "2" + }, + "shiftOption" : { + "output" : "2" + }, + "unmodified" : { + "output" : "2" + } + }, + "85" : { + "caps" : { + "output" : "3" + }, + "capsOption" : { + "output" : "3" + }, + "keyCode" : 85, + "option" : { + "output" : "3" + }, + "shift" : { + "output" : "3" + }, + "shiftCaps" : { + "output" : "3" + }, + "shiftCapsOption" : { + "output" : "3" + }, + "shiftOption" : { + "output" : "3" + }, + "unmodified" : { + "output" : "3" + } + }, + "86" : { + "caps" : { + "output" : "4" + }, + "capsOption" : { + "output" : "4" + }, + "keyCode" : 86, + "option" : { + "output" : "4" + }, + "shift" : { + "output" : "4" + }, + "shiftCaps" : { + "output" : "4" + }, + "shiftCapsOption" : { + "output" : "4" + }, + "shiftOption" : { + "output" : "4" + }, + "unmodified" : { + "output" : "4" + } + }, + "87" : { + "caps" : { + "output" : "5" + }, + "capsOption" : { + "output" : "5" + }, + "keyCode" : 87, + "option" : { + "output" : "5" + }, + "shift" : { + "output" : "5" + }, + "shiftCaps" : { + "output" : "5" + }, + "shiftCapsOption" : { + "output" : "5" + }, + "shiftOption" : { + "output" : "5" + }, + "unmodified" : { + "output" : "5" + } + }, + "88" : { + "caps" : { + "output" : "6" + }, + "capsOption" : { + "output" : "6" + }, + "keyCode" : 88, + "option" : { + "output" : "6" + }, + "shift" : { + "output" : "6" + }, + "shiftCaps" : { + "output" : "6" + }, + "shiftCapsOption" : { + "output" : "6" + }, + "shiftOption" : { + "output" : "6" + }, + "unmodified" : { + "output" : "6" + } + }, + "89" : { + "caps" : { + "output" : "7" + }, + "capsOption" : { + "output" : "7" + }, + "keyCode" : 89, + "option" : { + "output" : "7" + }, + "shift" : { + "output" : "7" + }, + "shiftCaps" : { + "output" : "7" + }, + "shiftCapsOption" : { + "output" : "7" + }, + "shiftOption" : { + "output" : "7" + }, + "unmodified" : { + "output" : "7" + } + }, + "9" : { + "caps" : { + "output" : "V" + }, + "capsOption" : { + "output" : "√" + }, + "keyCode" : 9, + "option" : { + "output" : "√" + }, + "shift" : { + "output" : "V" + }, + "shiftCaps" : { + "output" : "V" + }, + "shiftCapsOption" : { + "output" : "◊" + }, + "shiftOption" : { + "output" : "◊" + }, + "unmodified" : { + "output" : "v" + } + }, + "90" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 90, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "91" : { + "caps" : { + "output" : "8" + }, + "capsOption" : { + "output" : "8" + }, + "keyCode" : 91, + "option" : { + "output" : "8" + }, + "shift" : { + "output" : "8" + }, + "shiftCaps" : { + "output" : "8" + }, + "shiftCapsOption" : { + "output" : "8" + }, + "shiftOption" : { + "output" : "8" + }, + "unmodified" : { + "output" : "8" + } + }, + "92" : { + "caps" : { + "output" : "9" + }, + "capsOption" : { + "output" : "9" + }, + "keyCode" : 92, + "option" : { + "output" : "9" + }, + "shift" : { + "output" : "9" + }, + "shiftCaps" : { + "output" : "9" + }, + "shiftCapsOption" : { + "output" : "9" + }, + "shiftOption" : { + "output" : "9" + }, + "unmodified" : { + "output" : "9" + } + }, + "93" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 93, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "94" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 94, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "95" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 95, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "96" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 96, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "97" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 97, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "98" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 98, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "99" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 99, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + } + }, + "keyboardType" : 91, + "osBuild" : "24G830", + "osVersion" : "15.7.9", + "schemaVersion" : 1, + "sourceID" : "com.apple.keylayout.US" +} \ No newline at end of file diff --git a/Benchmarks/layout-tables/v1/com.apple.keylayout.Ukrainian-PC@37.json b/Benchmarks/layout-tables/v1/com.apple.keylayout.Ukrainian-PC@37.json new file mode 100644 index 0000000..f9b049f --- /dev/null +++ b/Benchmarks/layout-tables/v1/com.apple.keylayout.Ukrainian-PC@37.json @@ -0,0 +1,3465 @@ +{ + "keyOutputs" : { + "0" : { + "caps" : { + "output" : "Ф" + }, + "capsOption" : { + "output" : "ƒ" + }, + "keyCode" : 0, + "option" : { + "output" : "ƒ" + }, + "shift" : { + "output" : "Ф" + }, + "shiftCaps" : { + "output" : "Ф" + }, + "shiftCapsOption" : { + "output" : "₴" + }, + "shiftOption" : { + "output" : "₴" + }, + "unmodified" : { + "output" : "ф" + } + }, + "1" : { + "caps" : { + "output" : "І" + }, + "capsOption" : { + "output" : "Ы" + }, + "keyCode" : 1, + "option" : { + "output" : "ы" + }, + "shift" : { + "output" : "І" + }, + "shiftCaps" : { + "output" : "І" + }, + "shiftCapsOption" : { + "output" : "Ы" + }, + "shiftOption" : { + "output" : "Ы" + }, + "unmodified" : { + "output" : "і" + } + }, + "10" : { + "caps" : { + "output" : "'" + }, + "capsOption" : { + "output" : "]" + }, + "keyCode" : 10, + "option" : { + "output" : "§" + }, + "shift" : { + "output" : "\/" + }, + "shiftCaps" : { + "output" : "\/" + }, + "shiftCapsOption" : { + "output" : "±" + }, + "shiftOption" : { + "output" : "±" + }, + "unmodified" : { + "output" : "\\" + } + }, + "100" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 100, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "101" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 101, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "102" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 102, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "103" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 103, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "104" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 104, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "105" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 105, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "106" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 106, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "107" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 107, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "108" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 108, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "109" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 109, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "11" : { + "caps" : { + "output" : "И" + }, + "capsOption" : { + "output" : "И" + }, + "keyCode" : 11, + "option" : { + "output" : "и" + }, + "shift" : { + "output" : "И" + }, + "shiftCaps" : { + "output" : "И" + }, + "shiftCapsOption" : { + "output" : "И" + }, + "shiftOption" : { + "output" : "И" + }, + "unmodified" : { + "output" : "и" + } + }, + "110" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 110, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "111" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 111, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "112" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 112, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "113" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 113, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "114" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 114, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "115" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 115, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "116" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 116, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "117" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 117, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "118" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 118, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "119" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 119, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "12" : { + "caps" : { + "output" : "Й" + }, + "capsOption" : { + "output" : "Ј" + }, + "keyCode" : 12, + "option" : { + "output" : "ј" + }, + "shift" : { + "output" : "Й" + }, + "shiftCaps" : { + "output" : "Й" + }, + "shiftCapsOption" : { + "output" : "Ј" + }, + "shiftOption" : { + "output" : "Ј" + }, + "unmodified" : { + "output" : "й" + } + }, + "120" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 120, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "121" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 121, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "122" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 122, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "123" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 123, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "124" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 124, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "125" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 125, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "126" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 126, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "127" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 127, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "13" : { + "caps" : { + "output" : "Ц" + }, + "capsOption" : { + "output" : "Џ" + }, + "keyCode" : 13, + "option" : { + "output" : "џ" + }, + "shift" : { + "output" : "Ц" + }, + "shiftCaps" : { + "output" : "Ц" + }, + "shiftCapsOption" : { + "output" : "Џ" + }, + "shiftOption" : { + "output" : "Џ" + }, + "unmodified" : { + "output" : "ц" + } + }, + "14" : { + "caps" : { + "output" : "У" + }, + "capsOption" : { + "output" : "Ќ" + }, + "keyCode" : 14, + "option" : { + "output" : "ќ" + }, + "shift" : { + "output" : "У" + }, + "shiftCaps" : { + "output" : "У" + }, + "shiftCapsOption" : { + "output" : "Ќ" + }, + "shiftOption" : { + "output" : "Ќ" + }, + "unmodified" : { + "output" : "у" + } + }, + "15" : { + "caps" : { + "output" : "К" + }, + "capsOption" : { + "output" : "®" + }, + "keyCode" : 15, + "option" : { + "output" : "®" + }, + "shift" : { + "output" : "К" + }, + "shiftCaps" : { + "output" : "К" + }, + "shiftCapsOption" : { + "output" : "®" + }, + "shiftOption" : { + "output" : "®" + }, + "unmodified" : { + "output" : "к" + } + }, + "16" : { + "caps" : { + "output" : "Н" + }, + "capsOption" : { + "output" : "Њ" + }, + "keyCode" : 16, + "option" : { + "output" : "њ" + }, + "shift" : { + "output" : "Н" + }, + "shiftCaps" : { + "output" : "Н" + }, + "shiftCapsOption" : { + "output" : "Њ" + }, + "shiftOption" : { + "output" : "Њ" + }, + "unmodified" : { + "output" : "н" + } + }, + "17" : { + "caps" : { + "output" : "Е" + }, + "capsOption" : { + "output" : "†" + }, + "keyCode" : 17, + "option" : { + "output" : "ё" + }, + "shift" : { + "output" : "Е" + }, + "shiftCaps" : { + "output" : "Е" + }, + "shiftCapsOption" : { + "output" : "Ё" + }, + "shiftOption" : { + "output" : "Ё" + }, + "unmodified" : { + "output" : "е" + } + }, + "18" : { + "caps" : { + "output" : "1" + }, + "capsOption" : { + "output" : "!" + }, + "keyCode" : 18, + "option" : { + "output" : "!" + }, + "shift" : { + "output" : "!" + }, + "shiftCaps" : { + "output" : "!" + }, + "shiftCapsOption" : { + "output" : "|" + }, + "shiftOption" : { + "output" : "|" + }, + "unmodified" : { + "output" : "1" + } + }, + "19" : { + "caps" : { + "output" : "2" + }, + "capsOption" : { + "output" : "@" + }, + "keyCode" : 19, + "option" : { + "output" : "@" + }, + "shift" : { + "output" : "\"" + }, + "shiftCaps" : { + "output" : "\"" + }, + "shiftCapsOption" : { + "output" : "\"" + }, + "shiftOption" : { + "output" : "\"" + }, + "unmodified" : { + "output" : "2" + } + }, + "2" : { + "caps" : { + "output" : "В" + }, + "capsOption" : { + "output" : "Ћ" + }, + "keyCode" : 2, + "option" : { + "output" : "ћ" + }, + "shift" : { + "output" : "В" + }, + "shiftCaps" : { + "output" : "В" + }, + "shiftCapsOption" : { + "output" : "Ћ" + }, + "shiftOption" : { + "output" : "Ћ" + }, + "unmodified" : { + "output" : "в" + } + }, + "20" : { + "caps" : { + "output" : "3" + }, + "capsOption" : { + "output" : "#" + }, + "keyCode" : 20, + "option" : { + "output" : "#" + }, + "shift" : { + "output" : "№" + }, + "shiftCaps" : { + "output" : "№" + }, + "shiftCapsOption" : { + "output" : "£" + }, + "shiftOption" : { + "output" : "£" + }, + "unmodified" : { + "output" : "3" + } + }, + "21" : { + "caps" : { + "output" : "4" + }, + "capsOption" : { + "output" : "$" + }, + "keyCode" : 21, + "option" : { + "output" : "$" + }, + "shift" : { + "output" : ";" + }, + "shiftCaps" : { + "output" : ";" + }, + "shiftCapsOption" : { + "output" : "€" + }, + "shiftOption" : { + "output" : "€" + }, + "unmodified" : { + "output" : "4" + } + }, + "22" : { + "caps" : { + "output" : "6" + }, + "capsOption" : { + "output" : "^" + }, + "keyCode" : 22, + "option" : { + "output" : "^" + }, + "shift" : { + "output" : ":" + }, + "shiftCaps" : { + "output" : ":" + }, + "shiftCapsOption" : { + "output" : "¬" + }, + "shiftOption" : { + "output" : "¬" + }, + "unmodified" : { + "output" : "6" + } + }, + "23" : { + "caps" : { + "output" : "5" + }, + "capsOption" : { + "output" : "%" + }, + "keyCode" : 23, + "option" : { + "output" : "%" + }, + "shift" : { + "output" : "%" + }, + "shiftCaps" : { + "output" : "%" + }, + "shiftCapsOption" : { + "output" : "∞" + }, + "shiftOption" : { + "output" : "∞" + }, + "unmodified" : { + "output" : "5" + } + }, + "24" : { + "caps" : { + "output" : "=" + }, + "capsOption" : { + "output" : "»" + }, + "keyCode" : 24, + "option" : { + "output" : "»" + }, + "shift" : { + "output" : "+" + }, + "shiftCaps" : { + "output" : "+" + }, + "shiftCapsOption" : { + "output" : "«" + }, + "shiftOption" : { + "output" : "«" + }, + "unmodified" : { + "output" : "=" + } + }, + "25" : { + "caps" : { + "output" : "9" + }, + "capsOption" : { + "output" : "(" + }, + "keyCode" : 25, + "option" : { + "output" : "(" + }, + "shift" : { + "output" : "(" + }, + "shiftCaps" : { + "output" : "(" + }, + "shiftCapsOption" : { + "output" : "'" + }, + "shiftOption" : { + "output" : "'" + }, + "unmodified" : { + "output" : "9" + } + }, + "26" : { + "caps" : { + "output" : "7" + }, + "capsOption" : { + "output" : "&" + }, + "keyCode" : 26, + "option" : { + "output" : "&" + }, + "shift" : { + "output" : "?" + }, + "shiftCaps" : { + "output" : "?" + }, + "shiftCapsOption" : { + "output" : "¶" + }, + "shiftOption" : { + "output" : "¶" + }, + "unmodified" : { + "output" : "7" + } + }, + "27" : { + "caps" : { + "output" : "-" + }, + "capsOption" : { + "output" : "–" + }, + "keyCode" : 27, + "option" : { + "output" : "–" + }, + "shift" : { + "output" : "_" + }, + "shiftCaps" : { + "output" : "_" + }, + "shiftCapsOption" : { + "output" : "—" + }, + "shiftOption" : { + "output" : "—" + }, + "unmodified" : { + "output" : "-" + } + }, + "28" : { + "caps" : { + "output" : "8" + }, + "capsOption" : { + "output" : "*" + }, + "keyCode" : 28, + "option" : { + "output" : "*" + }, + "shift" : { + "output" : "*" + }, + "shiftCaps" : { + "output" : "*" + }, + "shiftCapsOption" : { + "output" : "√" + }, + "shiftOption" : { + "output" : "√" + }, + "unmodified" : { + "output" : "8" + } + }, + "29" : { + "caps" : { + "output" : "0" + }, + "capsOption" : { + "output" : ")" + }, + "keyCode" : 29, + "option" : { + "output" : ")" + }, + "shift" : { + "output" : ")" + }, + "shiftCaps" : { + "output" : ")" + }, + "shiftCapsOption" : { + "output" : "`" + }, + "shiftOption" : { + "output" : "`" + }, + "unmodified" : { + "output" : "0" + } + }, + "3" : { + "caps" : { + "output" : "А" + }, + "capsOption" : { + "output" : "÷" + }, + "keyCode" : 3, + "option" : { + "output" : "÷" + }, + "shift" : { + "output" : "А" + }, + "shiftCaps" : { + "output" : "А" + }, + "shiftCapsOption" : { + "output" : "÷" + }, + "shiftOption" : { + "output" : "÷" + }, + "unmodified" : { + "output" : "а" + } + }, + "30" : { + "caps" : { + "output" : "Ї" + }, + "capsOption" : { + "output" : "Ъ" + }, + "keyCode" : 30, + "option" : { + "output" : "ъ" + }, + "shift" : { + "output" : "Ї" + }, + "shiftCaps" : { + "output" : "Ї" + }, + "shiftCapsOption" : { + "output" : "Ъ" + }, + "shiftOption" : { + "output" : "Ъ" + }, + "unmodified" : { + "output" : "ї" + } + }, + "31" : { + "caps" : { + "output" : "Щ" + }, + "capsOption" : { + "output" : "Ў" + }, + "keyCode" : 31, + "option" : { + "output" : "ў" + }, + "shift" : { + "output" : "Щ" + }, + "shiftCaps" : { + "output" : "Щ" + }, + "shiftCapsOption" : { + "output" : "Ў" + }, + "shiftOption" : { + "output" : "Ў" + }, + "unmodified" : { + "output" : "щ" + } + }, + "32" : { + "caps" : { + "output" : "Г" + }, + "capsOption" : { + "output" : "Ѓ" + }, + "keyCode" : 32, + "option" : { + "output" : "ґ" + }, + "shift" : { + "output" : "Г" + }, + "shiftCaps" : { + "output" : "Г" + }, + "shiftCapsOption" : { + "output" : "Ґ" + }, + "shiftOption" : { + "output" : "Ґ" + }, + "unmodified" : { + "output" : "г" + } + }, + "33" : { + "caps" : { + "output" : "Х" + }, + "capsOption" : { + "output" : "“" + }, + "keyCode" : 33, + "option" : { + "output" : "“" + }, + "shift" : { + "output" : "Х" + }, + "shiftCaps" : { + "output" : "Х" + }, + "shiftCapsOption" : { + "output" : "”" + }, + "shiftOption" : { + "output" : "”" + }, + "unmodified" : { + "output" : "х" + } + }, + "34" : { + "caps" : { + "output" : "Ш" + }, + "capsOption" : { + "output" : "Ѕ" + }, + "keyCode" : 34, + "option" : { + "output" : "ѕ" + }, + "shift" : { + "output" : "Ш" + }, + "shiftCaps" : { + "output" : "Ш" + }, + "shiftCapsOption" : { + "output" : "Ѕ" + }, + "shiftOption" : { + "output" : "Ѕ" + }, + "unmodified" : { + "output" : "ш" + } + }, + "35" : { + "caps" : { + "output" : "З" + }, + "capsOption" : { + "output" : "‘" + }, + "keyCode" : 35, + "option" : { + "output" : "‘" + }, + "shift" : { + "output" : "З" + }, + "shiftCaps" : { + "output" : "З" + }, + "shiftCapsOption" : { + "output" : "’" + }, + "shiftOption" : { + "output" : "’" + }, + "unmodified" : { + "output" : "з" + } + }, + "36" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 36, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "37" : { + "caps" : { + "output" : "Д" + }, + "capsOption" : { + "output" : "∆" + }, + "keyCode" : 37, + "option" : { + "output" : "∆" + }, + "shift" : { + "output" : "Д" + }, + "shiftCaps" : { + "output" : "Д" + }, + "shiftCapsOption" : { + "output" : "∆" + }, + "shiftOption" : { + "output" : "∆" + }, + "unmodified" : { + "output" : "д" + } + }, + "38" : { + "caps" : { + "output" : "О" + }, + "capsOption" : { + "output" : "°" + }, + "keyCode" : 38, + "option" : { + "output" : "°" + }, + "shift" : { + "output" : "О" + }, + "shiftCaps" : { + "output" : "О" + }, + "shiftCapsOption" : { + "output" : "•" + }, + "shiftOption" : { + "output" : "•" + }, + "unmodified" : { + "output" : "о" + } + }, + "39" : { + "caps" : { + "output" : "Є" + }, + "capsOption" : { + "output" : "Э" + }, + "keyCode" : 39, + "option" : { + "output" : "э" + }, + "shift" : { + "output" : "Є" + }, + "shiftCaps" : { + "output" : "Є" + }, + "shiftCapsOption" : { + "output" : "Э" + }, + "shiftOption" : { + "output" : "Э" + }, + "unmodified" : { + "output" : "є" + } + }, + "4" : { + "caps" : { + "output" : "Р" + }, + "capsOption" : { + "output" : "}" + }, + "keyCode" : 4, + "option" : { + "output" : "}" + }, + "shift" : { + "output" : "Р" + }, + "shiftCaps" : { + "output" : "Р" + }, + "shiftCapsOption" : { + "output" : "{" + }, + "shiftOption" : { + "output" : "{" + }, + "unmodified" : { + "output" : "р" + } + }, + "40" : { + "caps" : { + "output" : "Л" + }, + "capsOption" : { + "output" : "Љ" + }, + "keyCode" : 40, + "option" : { + "output" : "љ" + }, + "shift" : { + "output" : "Л" + }, + "shiftCaps" : { + "output" : "Л" + }, + "shiftCapsOption" : { + "output" : "Љ" + }, + "shiftOption" : { + "output" : "Љ" + }, + "unmodified" : { + "output" : "л" + } + }, + "41" : { + "caps" : { + "output" : "Ж" + }, + "capsOption" : { + "output" : "…" + }, + "keyCode" : 41, + "option" : { + "output" : "…" + }, + "shift" : { + "output" : "Ж" + }, + "shiftCaps" : { + "output" : "Ж" + }, + "shiftCapsOption" : { + "output" : "…" + }, + "shiftOption" : { + "output" : "…" + }, + "unmodified" : { + "output" : "ж" + } + }, + "42" : { + "caps" : { + "output" : "\\" + }, + "capsOption" : { + "output" : "Ё" + }, + "keyCode" : 42, + "option" : { + "output" : "ё" + }, + "shift" : { + "output" : "₴" + }, + "shiftCaps" : { + "output" : "₴" + }, + "shiftCapsOption" : { + "output" : "Ё" + }, + "shiftOption" : { + "output" : "Ё" + }, + "unmodified" : { + "output" : "ʼ" + } + }, + "43" : { + "caps" : { + "output" : "Б" + }, + "capsOption" : { + "output" : "≤" + }, + "keyCode" : 43, + "option" : { + "output" : "≤" + }, + "shift" : { + "output" : "Б" + }, + "shiftCaps" : { + "output" : "Б" + }, + "shiftCapsOption" : { + "output" : "<" + }, + "shiftOption" : { + "output" : "<" + }, + "unmodified" : { + "output" : "б" + } + }, + "44" : { + "caps" : { + "output" : "." + }, + "capsOption" : { + "output" : "“" + }, + "keyCode" : 44, + "option" : { + "output" : "“" + }, + "shift" : { + "output" : "," + }, + "shiftCaps" : { + "output" : "," + }, + "shiftCapsOption" : { + "output" : "„" + }, + "shiftOption" : { + "output" : "„" + }, + "unmodified" : { + "output" : "." + } + }, + "45" : { + "caps" : { + "output" : "Т" + }, + "capsOption" : { + "output" : "™" + }, + "keyCode" : 45, + "option" : { + "output" : "™" + }, + "shift" : { + "output" : "Т" + }, + "shiftCaps" : { + "output" : "Т" + }, + "shiftCapsOption" : { + "output" : "™" + }, + "shiftOption" : { + "output" : "™" + }, + "unmodified" : { + "output" : "т" + } + }, + "46" : { + "caps" : { + "output" : "Ь" + }, + "capsOption" : { + "output" : "~" + }, + "keyCode" : 46, + "option" : { + "output" : "~" + }, + "shift" : { + "output" : "Ь" + }, + "shiftCaps" : { + "output" : "Ь" + }, + "shiftCapsOption" : { + "output" : "~" + }, + "shiftOption" : { + "output" : "~" + }, + "unmodified" : { + "output" : "ь" + } + }, + "47" : { + "caps" : { + "output" : "Ю" + }, + "capsOption" : { + "output" : "≥" + }, + "keyCode" : 47, + "option" : { + "output" : "≥" + }, + "shift" : { + "output" : "Ю" + }, + "shiftCaps" : { + "output" : "Ю" + }, + "shiftCapsOption" : { + "output" : ">" + }, + "shiftOption" : { + "output" : ">" + }, + "unmodified" : { + "output" : "ю" + } + }, + "48" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 48, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "49" : { + "caps" : { + "output" : " " + }, + "capsOption" : { + "output" : " " + }, + "keyCode" : 49, + "option" : { + "output" : " " + }, + "shift" : { + "output" : " " + }, + "shiftCaps" : { + "output" : " " + }, + "shiftCapsOption" : { + "output" : " " + }, + "shiftOption" : { + "output" : " " + }, + "unmodified" : { + "output" : " " + } + }, + "5" : { + "caps" : { + "output" : "П" + }, + "capsOption" : { + "output" : "©" + }, + "keyCode" : 5, + "option" : { + "output" : "©" + }, + "shift" : { + "output" : "П" + }, + "shiftCaps" : { + "output" : "П" + }, + "shiftCapsOption" : { + "output" : "©" + }, + "shiftOption" : { + "output" : "©" + }, + "unmodified" : { + "output" : "п" + } + }, + "50" : { + "caps" : { + "output" : "Ґ" + }, + "capsOption" : { + "output" : "§" + }, + "keyCode" : 50, + "option" : { + "output" : "]" + }, + "shift" : { + "output" : "Ґ" + }, + "shiftCaps" : { + "output" : "Ґ" + }, + "shiftCapsOption" : { + "output" : "[" + }, + "shiftOption" : { + "output" : "[" + }, + "unmodified" : { + "output" : "ґ" + } + }, + "51" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 51, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "52" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 52, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "53" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 53, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "54" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 54, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "55" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 55, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "56" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 56, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "57" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 57, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "58" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 58, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "59" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 59, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "6" : { + "caps" : { + "output" : "Я" + }, + "capsOption" : { + "output" : "Ђ" + }, + "keyCode" : 6, + "option" : { + "output" : "ђ" + }, + "shift" : { + "output" : "Я" + }, + "shiftCaps" : { + "output" : "Я" + }, + "shiftCapsOption" : { + "output" : "Ђ" + }, + "shiftOption" : { + "output" : "Ђ" + }, + "unmodified" : { + "output" : "я" + } + }, + "60" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 60, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "61" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 61, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "62" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 62, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "63" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 63, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "64" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 64, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "65" : { + "caps" : { + "output" : "," + }, + "capsOption" : { + "output" : "," + }, + "keyCode" : 65, + "option" : { + "output" : "," + }, + "shift" : { + "output" : "." + }, + "shiftCaps" : { + "output" : "." + }, + "shiftCapsOption" : { + "output" : "." + }, + "shiftOption" : { + "output" : "." + }, + "unmodified" : { + "output" : "," + } + }, + "66" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 66, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "*" + }, + "shiftCaps" : { + "output" : "*" + }, + "shiftCapsOption" : { + "output" : "*" + }, + "shiftOption" : { + "output" : "*" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "67" : { + "caps" : { + "output" : "*" + }, + "capsOption" : { + "output" : "*" + }, + "keyCode" : 67, + "option" : { + "output" : "*" + }, + "shift" : { + "output" : "*" + }, + "shiftCaps" : { + "output" : "*" + }, + "shiftCapsOption" : { + "output" : "*" + }, + "shiftOption" : { + "output" : "*" + }, + "unmodified" : { + "output" : "*" + } + }, + "68" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 68, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "69" : { + "caps" : { + "output" : "+" + }, + "capsOption" : { + "output" : "+" + }, + "keyCode" : 69, + "option" : { + "output" : "+" + }, + "shift" : { + "output" : "+" + }, + "shiftCaps" : { + "output" : "+" + }, + "shiftCapsOption" : { + "output" : "+" + }, + "shiftOption" : { + "output" : "+" + }, + "unmodified" : { + "output" : "+" + } + }, + "7" : { + "caps" : { + "output" : "Ч" + }, + "capsOption" : { + "output" : "≈" + }, + "keyCode" : 7, + "option" : { + "output" : "≈" + }, + "shift" : { + "output" : "Ч" + }, + "shiftCaps" : { + "output" : "Ч" + }, + "shiftCapsOption" : { + "output" : "≈" + }, + "shiftOption" : { + "output" : "≈" + }, + "unmodified" : { + "output" : "ч" + } + }, + "70" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 70, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "+" + }, + "shiftCaps" : { + "output" : "+" + }, + "shiftCapsOption" : { + "output" : "+" + }, + "shiftOption" : { + "output" : "+" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "71" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 71, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "72" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 72, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "=" + }, + "shiftCaps" : { + "output" : "=" + }, + "shiftCapsOption" : { + "output" : "=" + }, + "shiftOption" : { + "output" : "=" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "73" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 73, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "74" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 74, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "75" : { + "caps" : { + "output" : "\/" + }, + "capsOption" : { + "output" : "\/" + }, + "keyCode" : 75, + "option" : { + "output" : "\/" + }, + "shift" : { + "output" : "\/" + }, + "shiftCaps" : { + "output" : "\/" + }, + "shiftCapsOption" : { + "output" : "\/" + }, + "shiftOption" : { + "output" : "\/" + }, + "unmodified" : { + "output" : "\/" + } + }, + "76" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 76, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "77" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 77, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "\/" + }, + "shiftCaps" : { + "output" : "\/" + }, + "shiftCapsOption" : { + "output" : "\/" + }, + "shiftOption" : { + "output" : "\/" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "78" : { + "caps" : { + "output" : "-" + }, + "capsOption" : { + "output" : "-" + }, + "keyCode" : 78, + "option" : { + "output" : "-" + }, + "shift" : { + "output" : "-" + }, + "shiftCaps" : { + "output" : "-" + }, + "shiftCapsOption" : { + "output" : "-" + }, + "shiftOption" : { + "output" : "-" + }, + "unmodified" : { + "output" : "-" + } + }, + "79" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 79, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "8" : { + "caps" : { + "output" : "С" + }, + "capsOption" : { + "output" : "≠" + }, + "keyCode" : 8, + "option" : { + "output" : "≠" + }, + "shift" : { + "output" : "С" + }, + "shiftCaps" : { + "output" : "С" + }, + "shiftCapsOption" : { + "output" : "≠" + }, + "shiftOption" : { + "output" : "≠" + }, + "unmodified" : { + "output" : "с" + } + }, + "80" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 80, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "81" : { + "caps" : { + "output" : "=" + }, + "capsOption" : { + "output" : "=" + }, + "keyCode" : 81, + "option" : { + "output" : "=" + }, + "shift" : { + "output" : "=" + }, + "shiftCaps" : { + "output" : "=" + }, + "shiftCapsOption" : { + "output" : "=" + }, + "shiftOption" : { + "output" : "=" + }, + "unmodified" : { + "output" : "=" + } + }, + "82" : { + "caps" : { + "output" : "0" + }, + "capsOption" : { + "output" : "0" + }, + "keyCode" : 82, + "option" : { + "output" : "0" + }, + "shift" : { + "output" : "0" + }, + "shiftCaps" : { + "output" : "0" + }, + "shiftCapsOption" : { + "output" : "0" + }, + "shiftOption" : { + "output" : "0" + }, + "unmodified" : { + "output" : "0" + } + }, + "83" : { + "caps" : { + "output" : "1" + }, + "capsOption" : { + "output" : "1" + }, + "keyCode" : 83, + "option" : { + "output" : "1" + }, + "shift" : { + "output" : "1" + }, + "shiftCaps" : { + "output" : "1" + }, + "shiftCapsOption" : { + "output" : "1" + }, + "shiftOption" : { + "output" : "1" + }, + "unmodified" : { + "output" : "1" + } + }, + "84" : { + "caps" : { + "output" : "2" + }, + "capsOption" : { + "output" : "2" + }, + "keyCode" : 84, + "option" : { + "output" : "2" + }, + "shift" : { + "output" : "2" + }, + "shiftCaps" : { + "output" : "2" + }, + "shiftCapsOption" : { + "output" : "2" + }, + "shiftOption" : { + "output" : "2" + }, + "unmodified" : { + "output" : "2" + } + }, + "85" : { + "caps" : { + "output" : "3" + }, + "capsOption" : { + "output" : "3" + }, + "keyCode" : 85, + "option" : { + "output" : "3" + }, + "shift" : { + "output" : "3" + }, + "shiftCaps" : { + "output" : "3" + }, + "shiftCapsOption" : { + "output" : "3" + }, + "shiftOption" : { + "output" : "3" + }, + "unmodified" : { + "output" : "3" + } + }, + "86" : { + "caps" : { + "output" : "4" + }, + "capsOption" : { + "output" : "4" + }, + "keyCode" : 86, + "option" : { + "output" : "4" + }, + "shift" : { + "output" : "4" + }, + "shiftCaps" : { + "output" : "4" + }, + "shiftCapsOption" : { + "output" : "4" + }, + "shiftOption" : { + "output" : "4" + }, + "unmodified" : { + "output" : "4" + } + }, + "87" : { + "caps" : { + "output" : "5" + }, + "capsOption" : { + "output" : "5" + }, + "keyCode" : 87, + "option" : { + "output" : "5" + }, + "shift" : { + "output" : "5" + }, + "shiftCaps" : { + "output" : "5" + }, + "shiftCapsOption" : { + "output" : "5" + }, + "shiftOption" : { + "output" : "5" + }, + "unmodified" : { + "output" : "5" + } + }, + "88" : { + "caps" : { + "output" : "6" + }, + "capsOption" : { + "output" : "6" + }, + "keyCode" : 88, + "option" : { + "output" : "6" + }, + "shift" : { + "output" : "6" + }, + "shiftCaps" : { + "output" : "6" + }, + "shiftCapsOption" : { + "output" : "6" + }, + "shiftOption" : { + "output" : "6" + }, + "unmodified" : { + "output" : "6" + } + }, + "89" : { + "caps" : { + "output" : "7" + }, + "capsOption" : { + "output" : "7" + }, + "keyCode" : 89, + "option" : { + "output" : "7" + }, + "shift" : { + "output" : "7" + }, + "shiftCaps" : { + "output" : "7" + }, + "shiftCapsOption" : { + "output" : "7" + }, + "shiftOption" : { + "output" : "7" + }, + "unmodified" : { + "output" : "7" + } + }, + "9" : { + "caps" : { + "output" : "М" + }, + "capsOption" : { + "output" : "µ" + }, + "keyCode" : 9, + "option" : { + "output" : "µ" + }, + "shift" : { + "output" : "М" + }, + "shiftCaps" : { + "output" : "М" + }, + "shiftCapsOption" : { + "output" : "µ" + }, + "shiftOption" : { + "output" : "µ" + }, + "unmodified" : { + "output" : "м" + } + }, + "90" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 90, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "91" : { + "caps" : { + "output" : "8" + }, + "capsOption" : { + "output" : "8" + }, + "keyCode" : 91, + "option" : { + "output" : "8" + }, + "shift" : { + "output" : "8" + }, + "shiftCaps" : { + "output" : "8" + }, + "shiftCapsOption" : { + "output" : "8" + }, + "shiftOption" : { + "output" : "8" + }, + "unmodified" : { + "output" : "8" + } + }, + "92" : { + "caps" : { + "output" : "9" + }, + "capsOption" : { + "output" : "9" + }, + "keyCode" : 92, + "option" : { + "output" : "9" + }, + "shift" : { + "output" : "9" + }, + "shiftCaps" : { + "output" : "9" + }, + "shiftCapsOption" : { + "output" : "9" + }, + "shiftOption" : { + "output" : "9" + }, + "unmodified" : { + "output" : "9" + } + }, + "93" : { + "caps" : { + "output" : "'" + }, + "capsOption" : { + "output" : "]" + }, + "keyCode" : 93, + "option" : { + "output" : "§" + }, + "shift" : { + "output" : "₴" + }, + "shiftCaps" : { + "output" : "₴" + }, + "shiftCapsOption" : { + "output" : "±" + }, + "shiftOption" : { + "output" : "±" + }, + "unmodified" : { + "output" : "'" + } + }, + "94" : { + "caps" : { + "output" : "Ґ" + }, + "capsOption" : { + "output" : "§" + }, + "keyCode" : 94, + "option" : { + "output" : "]" + }, + "shift" : { + "output" : "Ґ" + }, + "shiftCaps" : { + "output" : "Ґ" + }, + "shiftCapsOption" : { + "output" : "[" + }, + "shiftOption" : { + "output" : "[" + }, + "unmodified" : { + "output" : "ґ" + } + }, + "95" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 95, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "96" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 96, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "97" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 97, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "98" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 98, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "99" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 99, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + } + }, + "keyboardType" : 37, + "osBuild" : "24G830", + "osVersion" : "15.7.9", + "schemaVersion" : 1, + "sourceID" : "com.apple.keylayout.Ukrainian-PC" +} \ No newline at end of file diff --git a/Benchmarks/layout-tables/v1/com.apple.keylayout.Ukrainian-PC@91.json b/Benchmarks/layout-tables/v1/com.apple.keylayout.Ukrainian-PC@91.json new file mode 100644 index 0000000..405e84f --- /dev/null +++ b/Benchmarks/layout-tables/v1/com.apple.keylayout.Ukrainian-PC@91.json @@ -0,0 +1,3465 @@ +{ + "keyOutputs" : { + "0" : { + "caps" : { + "output" : "Ф" + }, + "capsOption" : { + "output" : "ƒ" + }, + "keyCode" : 0, + "option" : { + "output" : "ƒ" + }, + "shift" : { + "output" : "Ф" + }, + "shiftCaps" : { + "output" : "Ф" + }, + "shiftCapsOption" : { + "output" : "₴" + }, + "shiftOption" : { + "output" : "₴" + }, + "unmodified" : { + "output" : "ф" + } + }, + "1" : { + "caps" : { + "output" : "І" + }, + "capsOption" : { + "output" : "Ы" + }, + "keyCode" : 1, + "option" : { + "output" : "ы" + }, + "shift" : { + "output" : "І" + }, + "shiftCaps" : { + "output" : "І" + }, + "shiftCapsOption" : { + "output" : "Ы" + }, + "shiftOption" : { + "output" : "Ы" + }, + "unmodified" : { + "output" : "і" + } + }, + "10" : { + "caps" : { + "output" : "'" + }, + "capsOption" : { + "output" : "]" + }, + "keyCode" : 10, + "option" : { + "output" : "§" + }, + "shift" : { + "output" : "\/" + }, + "shiftCaps" : { + "output" : "\/" + }, + "shiftCapsOption" : { + "output" : "±" + }, + "shiftOption" : { + "output" : "±" + }, + "unmodified" : { + "output" : "\\" + } + }, + "100" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 100, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "101" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 101, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "102" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 102, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "103" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 103, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "104" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 104, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "105" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 105, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "106" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 106, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "107" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 107, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "108" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 108, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "109" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 109, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "11" : { + "caps" : { + "output" : "И" + }, + "capsOption" : { + "output" : "И" + }, + "keyCode" : 11, + "option" : { + "output" : "и" + }, + "shift" : { + "output" : "И" + }, + "shiftCaps" : { + "output" : "И" + }, + "shiftCapsOption" : { + "output" : "И" + }, + "shiftOption" : { + "output" : "И" + }, + "unmodified" : { + "output" : "и" + } + }, + "110" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 110, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "111" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 111, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "112" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 112, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "113" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 113, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "114" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 114, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "115" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 115, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "116" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 116, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "117" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 117, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "118" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 118, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "119" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 119, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "12" : { + "caps" : { + "output" : "Й" + }, + "capsOption" : { + "output" : "Ј" + }, + "keyCode" : 12, + "option" : { + "output" : "ј" + }, + "shift" : { + "output" : "Й" + }, + "shiftCaps" : { + "output" : "Й" + }, + "shiftCapsOption" : { + "output" : "Ј" + }, + "shiftOption" : { + "output" : "Ј" + }, + "unmodified" : { + "output" : "й" + } + }, + "120" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 120, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "121" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 121, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "122" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 122, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "123" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 123, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "124" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 124, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "125" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 125, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "126" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 126, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "127" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 127, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "13" : { + "caps" : { + "output" : "Ц" + }, + "capsOption" : { + "output" : "Џ" + }, + "keyCode" : 13, + "option" : { + "output" : "џ" + }, + "shift" : { + "output" : "Ц" + }, + "shiftCaps" : { + "output" : "Ц" + }, + "shiftCapsOption" : { + "output" : "Џ" + }, + "shiftOption" : { + "output" : "Џ" + }, + "unmodified" : { + "output" : "ц" + } + }, + "14" : { + "caps" : { + "output" : "У" + }, + "capsOption" : { + "output" : "Ќ" + }, + "keyCode" : 14, + "option" : { + "output" : "ќ" + }, + "shift" : { + "output" : "У" + }, + "shiftCaps" : { + "output" : "У" + }, + "shiftCapsOption" : { + "output" : "Ќ" + }, + "shiftOption" : { + "output" : "Ќ" + }, + "unmodified" : { + "output" : "у" + } + }, + "15" : { + "caps" : { + "output" : "К" + }, + "capsOption" : { + "output" : "®" + }, + "keyCode" : 15, + "option" : { + "output" : "®" + }, + "shift" : { + "output" : "К" + }, + "shiftCaps" : { + "output" : "К" + }, + "shiftCapsOption" : { + "output" : "®" + }, + "shiftOption" : { + "output" : "®" + }, + "unmodified" : { + "output" : "к" + } + }, + "16" : { + "caps" : { + "output" : "Н" + }, + "capsOption" : { + "output" : "Њ" + }, + "keyCode" : 16, + "option" : { + "output" : "њ" + }, + "shift" : { + "output" : "Н" + }, + "shiftCaps" : { + "output" : "Н" + }, + "shiftCapsOption" : { + "output" : "Њ" + }, + "shiftOption" : { + "output" : "Њ" + }, + "unmodified" : { + "output" : "н" + } + }, + "17" : { + "caps" : { + "output" : "Е" + }, + "capsOption" : { + "output" : "†" + }, + "keyCode" : 17, + "option" : { + "output" : "ё" + }, + "shift" : { + "output" : "Е" + }, + "shiftCaps" : { + "output" : "Е" + }, + "shiftCapsOption" : { + "output" : "Ё" + }, + "shiftOption" : { + "output" : "Ё" + }, + "unmodified" : { + "output" : "е" + } + }, + "18" : { + "caps" : { + "output" : "1" + }, + "capsOption" : { + "output" : "!" + }, + "keyCode" : 18, + "option" : { + "output" : "!" + }, + "shift" : { + "output" : "!" + }, + "shiftCaps" : { + "output" : "!" + }, + "shiftCapsOption" : { + "output" : "|" + }, + "shiftOption" : { + "output" : "|" + }, + "unmodified" : { + "output" : "1" + } + }, + "19" : { + "caps" : { + "output" : "2" + }, + "capsOption" : { + "output" : "@" + }, + "keyCode" : 19, + "option" : { + "output" : "@" + }, + "shift" : { + "output" : "\"" + }, + "shiftCaps" : { + "output" : "\"" + }, + "shiftCapsOption" : { + "output" : "\"" + }, + "shiftOption" : { + "output" : "\"" + }, + "unmodified" : { + "output" : "2" + } + }, + "2" : { + "caps" : { + "output" : "В" + }, + "capsOption" : { + "output" : "Ћ" + }, + "keyCode" : 2, + "option" : { + "output" : "ћ" + }, + "shift" : { + "output" : "В" + }, + "shiftCaps" : { + "output" : "В" + }, + "shiftCapsOption" : { + "output" : "Ћ" + }, + "shiftOption" : { + "output" : "Ћ" + }, + "unmodified" : { + "output" : "в" + } + }, + "20" : { + "caps" : { + "output" : "3" + }, + "capsOption" : { + "output" : "#" + }, + "keyCode" : 20, + "option" : { + "output" : "#" + }, + "shift" : { + "output" : "№" + }, + "shiftCaps" : { + "output" : "№" + }, + "shiftCapsOption" : { + "output" : "£" + }, + "shiftOption" : { + "output" : "£" + }, + "unmodified" : { + "output" : "3" + } + }, + "21" : { + "caps" : { + "output" : "4" + }, + "capsOption" : { + "output" : "$" + }, + "keyCode" : 21, + "option" : { + "output" : "$" + }, + "shift" : { + "output" : ";" + }, + "shiftCaps" : { + "output" : ";" + }, + "shiftCapsOption" : { + "output" : "€" + }, + "shiftOption" : { + "output" : "€" + }, + "unmodified" : { + "output" : "4" + } + }, + "22" : { + "caps" : { + "output" : "6" + }, + "capsOption" : { + "output" : "^" + }, + "keyCode" : 22, + "option" : { + "output" : "^" + }, + "shift" : { + "output" : ":" + }, + "shiftCaps" : { + "output" : ":" + }, + "shiftCapsOption" : { + "output" : "¬" + }, + "shiftOption" : { + "output" : "¬" + }, + "unmodified" : { + "output" : "6" + } + }, + "23" : { + "caps" : { + "output" : "5" + }, + "capsOption" : { + "output" : "%" + }, + "keyCode" : 23, + "option" : { + "output" : "%" + }, + "shift" : { + "output" : "%" + }, + "shiftCaps" : { + "output" : "%" + }, + "shiftCapsOption" : { + "output" : "∞" + }, + "shiftOption" : { + "output" : "∞" + }, + "unmodified" : { + "output" : "5" + } + }, + "24" : { + "caps" : { + "output" : "=" + }, + "capsOption" : { + "output" : "»" + }, + "keyCode" : 24, + "option" : { + "output" : "»" + }, + "shift" : { + "output" : "+" + }, + "shiftCaps" : { + "output" : "+" + }, + "shiftCapsOption" : { + "output" : "«" + }, + "shiftOption" : { + "output" : "«" + }, + "unmodified" : { + "output" : "=" + } + }, + "25" : { + "caps" : { + "output" : "9" + }, + "capsOption" : { + "output" : "(" + }, + "keyCode" : 25, + "option" : { + "output" : "(" + }, + "shift" : { + "output" : "(" + }, + "shiftCaps" : { + "output" : "(" + }, + "shiftCapsOption" : { + "output" : "'" + }, + "shiftOption" : { + "output" : "'" + }, + "unmodified" : { + "output" : "9" + } + }, + "26" : { + "caps" : { + "output" : "7" + }, + "capsOption" : { + "output" : "&" + }, + "keyCode" : 26, + "option" : { + "output" : "&" + }, + "shift" : { + "output" : "?" + }, + "shiftCaps" : { + "output" : "?" + }, + "shiftCapsOption" : { + "output" : "¶" + }, + "shiftOption" : { + "output" : "¶" + }, + "unmodified" : { + "output" : "7" + } + }, + "27" : { + "caps" : { + "output" : "-" + }, + "capsOption" : { + "output" : "–" + }, + "keyCode" : 27, + "option" : { + "output" : "–" + }, + "shift" : { + "output" : "_" + }, + "shiftCaps" : { + "output" : "_" + }, + "shiftCapsOption" : { + "output" : "—" + }, + "shiftOption" : { + "output" : "—" + }, + "unmodified" : { + "output" : "-" + } + }, + "28" : { + "caps" : { + "output" : "8" + }, + "capsOption" : { + "output" : "*" + }, + "keyCode" : 28, + "option" : { + "output" : "*" + }, + "shift" : { + "output" : "*" + }, + "shiftCaps" : { + "output" : "*" + }, + "shiftCapsOption" : { + "output" : "√" + }, + "shiftOption" : { + "output" : "√" + }, + "unmodified" : { + "output" : "8" + } + }, + "29" : { + "caps" : { + "output" : "0" + }, + "capsOption" : { + "output" : ")" + }, + "keyCode" : 29, + "option" : { + "output" : ")" + }, + "shift" : { + "output" : ")" + }, + "shiftCaps" : { + "output" : ")" + }, + "shiftCapsOption" : { + "output" : "`" + }, + "shiftOption" : { + "output" : "`" + }, + "unmodified" : { + "output" : "0" + } + }, + "3" : { + "caps" : { + "output" : "А" + }, + "capsOption" : { + "output" : "÷" + }, + "keyCode" : 3, + "option" : { + "output" : "÷" + }, + "shift" : { + "output" : "А" + }, + "shiftCaps" : { + "output" : "А" + }, + "shiftCapsOption" : { + "output" : "÷" + }, + "shiftOption" : { + "output" : "÷" + }, + "unmodified" : { + "output" : "а" + } + }, + "30" : { + "caps" : { + "output" : "Ї" + }, + "capsOption" : { + "output" : "Ъ" + }, + "keyCode" : 30, + "option" : { + "output" : "ъ" + }, + "shift" : { + "output" : "Ї" + }, + "shiftCaps" : { + "output" : "Ї" + }, + "shiftCapsOption" : { + "output" : "Ъ" + }, + "shiftOption" : { + "output" : "Ъ" + }, + "unmodified" : { + "output" : "ї" + } + }, + "31" : { + "caps" : { + "output" : "Щ" + }, + "capsOption" : { + "output" : "Ў" + }, + "keyCode" : 31, + "option" : { + "output" : "ў" + }, + "shift" : { + "output" : "Щ" + }, + "shiftCaps" : { + "output" : "Щ" + }, + "shiftCapsOption" : { + "output" : "Ў" + }, + "shiftOption" : { + "output" : "Ў" + }, + "unmodified" : { + "output" : "щ" + } + }, + "32" : { + "caps" : { + "output" : "Г" + }, + "capsOption" : { + "output" : "Ѓ" + }, + "keyCode" : 32, + "option" : { + "output" : "ґ" + }, + "shift" : { + "output" : "Г" + }, + "shiftCaps" : { + "output" : "Г" + }, + "shiftCapsOption" : { + "output" : "Ґ" + }, + "shiftOption" : { + "output" : "Ґ" + }, + "unmodified" : { + "output" : "г" + } + }, + "33" : { + "caps" : { + "output" : "Х" + }, + "capsOption" : { + "output" : "“" + }, + "keyCode" : 33, + "option" : { + "output" : "“" + }, + "shift" : { + "output" : "Х" + }, + "shiftCaps" : { + "output" : "Х" + }, + "shiftCapsOption" : { + "output" : "”" + }, + "shiftOption" : { + "output" : "”" + }, + "unmodified" : { + "output" : "х" + } + }, + "34" : { + "caps" : { + "output" : "Ш" + }, + "capsOption" : { + "output" : "Ѕ" + }, + "keyCode" : 34, + "option" : { + "output" : "ѕ" + }, + "shift" : { + "output" : "Ш" + }, + "shiftCaps" : { + "output" : "Ш" + }, + "shiftCapsOption" : { + "output" : "Ѕ" + }, + "shiftOption" : { + "output" : "Ѕ" + }, + "unmodified" : { + "output" : "ш" + } + }, + "35" : { + "caps" : { + "output" : "З" + }, + "capsOption" : { + "output" : "‘" + }, + "keyCode" : 35, + "option" : { + "output" : "‘" + }, + "shift" : { + "output" : "З" + }, + "shiftCaps" : { + "output" : "З" + }, + "shiftCapsOption" : { + "output" : "’" + }, + "shiftOption" : { + "output" : "’" + }, + "unmodified" : { + "output" : "з" + } + }, + "36" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 36, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "37" : { + "caps" : { + "output" : "Д" + }, + "capsOption" : { + "output" : "∆" + }, + "keyCode" : 37, + "option" : { + "output" : "∆" + }, + "shift" : { + "output" : "Д" + }, + "shiftCaps" : { + "output" : "Д" + }, + "shiftCapsOption" : { + "output" : "∆" + }, + "shiftOption" : { + "output" : "∆" + }, + "unmodified" : { + "output" : "д" + } + }, + "38" : { + "caps" : { + "output" : "О" + }, + "capsOption" : { + "output" : "°" + }, + "keyCode" : 38, + "option" : { + "output" : "°" + }, + "shift" : { + "output" : "О" + }, + "shiftCaps" : { + "output" : "О" + }, + "shiftCapsOption" : { + "output" : "•" + }, + "shiftOption" : { + "output" : "•" + }, + "unmodified" : { + "output" : "о" + } + }, + "39" : { + "caps" : { + "output" : "Є" + }, + "capsOption" : { + "output" : "Э" + }, + "keyCode" : 39, + "option" : { + "output" : "э" + }, + "shift" : { + "output" : "Є" + }, + "shiftCaps" : { + "output" : "Є" + }, + "shiftCapsOption" : { + "output" : "Э" + }, + "shiftOption" : { + "output" : "Э" + }, + "unmodified" : { + "output" : "є" + } + }, + "4" : { + "caps" : { + "output" : "Р" + }, + "capsOption" : { + "output" : "}" + }, + "keyCode" : 4, + "option" : { + "output" : "}" + }, + "shift" : { + "output" : "Р" + }, + "shiftCaps" : { + "output" : "Р" + }, + "shiftCapsOption" : { + "output" : "{" + }, + "shiftOption" : { + "output" : "{" + }, + "unmodified" : { + "output" : "р" + } + }, + "40" : { + "caps" : { + "output" : "Л" + }, + "capsOption" : { + "output" : "Љ" + }, + "keyCode" : 40, + "option" : { + "output" : "љ" + }, + "shift" : { + "output" : "Л" + }, + "shiftCaps" : { + "output" : "Л" + }, + "shiftCapsOption" : { + "output" : "Љ" + }, + "shiftOption" : { + "output" : "Љ" + }, + "unmodified" : { + "output" : "л" + } + }, + "41" : { + "caps" : { + "output" : "Ж" + }, + "capsOption" : { + "output" : "…" + }, + "keyCode" : 41, + "option" : { + "output" : "…" + }, + "shift" : { + "output" : "Ж" + }, + "shiftCaps" : { + "output" : "Ж" + }, + "shiftCapsOption" : { + "output" : "…" + }, + "shiftOption" : { + "output" : "…" + }, + "unmodified" : { + "output" : "ж" + } + }, + "42" : { + "caps" : { + "output" : "\\" + }, + "capsOption" : { + "output" : "Ё" + }, + "keyCode" : 42, + "option" : { + "output" : "ё" + }, + "shift" : { + "output" : "₴" + }, + "shiftCaps" : { + "output" : "₴" + }, + "shiftCapsOption" : { + "output" : "Ё" + }, + "shiftOption" : { + "output" : "Ё" + }, + "unmodified" : { + "output" : "ʼ" + } + }, + "43" : { + "caps" : { + "output" : "Б" + }, + "capsOption" : { + "output" : "≤" + }, + "keyCode" : 43, + "option" : { + "output" : "≤" + }, + "shift" : { + "output" : "Б" + }, + "shiftCaps" : { + "output" : "Б" + }, + "shiftCapsOption" : { + "output" : "<" + }, + "shiftOption" : { + "output" : "<" + }, + "unmodified" : { + "output" : "б" + } + }, + "44" : { + "caps" : { + "output" : "." + }, + "capsOption" : { + "output" : "“" + }, + "keyCode" : 44, + "option" : { + "output" : "“" + }, + "shift" : { + "output" : "," + }, + "shiftCaps" : { + "output" : "," + }, + "shiftCapsOption" : { + "output" : "„" + }, + "shiftOption" : { + "output" : "„" + }, + "unmodified" : { + "output" : "." + } + }, + "45" : { + "caps" : { + "output" : "Т" + }, + "capsOption" : { + "output" : "™" + }, + "keyCode" : 45, + "option" : { + "output" : "™" + }, + "shift" : { + "output" : "Т" + }, + "shiftCaps" : { + "output" : "Т" + }, + "shiftCapsOption" : { + "output" : "™" + }, + "shiftOption" : { + "output" : "™" + }, + "unmodified" : { + "output" : "т" + } + }, + "46" : { + "caps" : { + "output" : "Ь" + }, + "capsOption" : { + "output" : "~" + }, + "keyCode" : 46, + "option" : { + "output" : "~" + }, + "shift" : { + "output" : "Ь" + }, + "shiftCaps" : { + "output" : "Ь" + }, + "shiftCapsOption" : { + "output" : "~" + }, + "shiftOption" : { + "output" : "~" + }, + "unmodified" : { + "output" : "ь" + } + }, + "47" : { + "caps" : { + "output" : "Ю" + }, + "capsOption" : { + "output" : "≥" + }, + "keyCode" : 47, + "option" : { + "output" : "≥" + }, + "shift" : { + "output" : "Ю" + }, + "shiftCaps" : { + "output" : "Ю" + }, + "shiftCapsOption" : { + "output" : ">" + }, + "shiftOption" : { + "output" : ">" + }, + "unmodified" : { + "output" : "ю" + } + }, + "48" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 48, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "49" : { + "caps" : { + "output" : " " + }, + "capsOption" : { + "output" : " " + }, + "keyCode" : 49, + "option" : { + "output" : " " + }, + "shift" : { + "output" : " " + }, + "shiftCaps" : { + "output" : " " + }, + "shiftCapsOption" : { + "output" : " " + }, + "shiftOption" : { + "output" : " " + }, + "unmodified" : { + "output" : " " + } + }, + "5" : { + "caps" : { + "output" : "П" + }, + "capsOption" : { + "output" : "©" + }, + "keyCode" : 5, + "option" : { + "output" : "©" + }, + "shift" : { + "output" : "П" + }, + "shiftCaps" : { + "output" : "П" + }, + "shiftCapsOption" : { + "output" : "©" + }, + "shiftOption" : { + "output" : "©" + }, + "unmodified" : { + "output" : "п" + } + }, + "50" : { + "caps" : { + "output" : "Ґ" + }, + "capsOption" : { + "output" : "§" + }, + "keyCode" : 50, + "option" : { + "output" : "]" + }, + "shift" : { + "output" : "Ґ" + }, + "shiftCaps" : { + "output" : "Ґ" + }, + "shiftCapsOption" : { + "output" : "[" + }, + "shiftOption" : { + "output" : "[" + }, + "unmodified" : { + "output" : "ґ" + } + }, + "51" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 51, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "52" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 52, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "53" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 53, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "54" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 54, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "55" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 55, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "56" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 56, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "57" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 57, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "58" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 58, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "59" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 59, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "6" : { + "caps" : { + "output" : "Я" + }, + "capsOption" : { + "output" : "Ђ" + }, + "keyCode" : 6, + "option" : { + "output" : "ђ" + }, + "shift" : { + "output" : "Я" + }, + "shiftCaps" : { + "output" : "Я" + }, + "shiftCapsOption" : { + "output" : "Ђ" + }, + "shiftOption" : { + "output" : "Ђ" + }, + "unmodified" : { + "output" : "я" + } + }, + "60" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 60, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "61" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 61, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "62" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 62, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "63" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 63, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "64" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 64, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "65" : { + "caps" : { + "output" : "," + }, + "capsOption" : { + "output" : "," + }, + "keyCode" : 65, + "option" : { + "output" : "," + }, + "shift" : { + "output" : "." + }, + "shiftCaps" : { + "output" : "." + }, + "shiftCapsOption" : { + "output" : "." + }, + "shiftOption" : { + "output" : "." + }, + "unmodified" : { + "output" : "," + } + }, + "66" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 66, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "*" + }, + "shiftCaps" : { + "output" : "*" + }, + "shiftCapsOption" : { + "output" : "*" + }, + "shiftOption" : { + "output" : "*" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "67" : { + "caps" : { + "output" : "*" + }, + "capsOption" : { + "output" : "*" + }, + "keyCode" : 67, + "option" : { + "output" : "*" + }, + "shift" : { + "output" : "*" + }, + "shiftCaps" : { + "output" : "*" + }, + "shiftCapsOption" : { + "output" : "*" + }, + "shiftOption" : { + "output" : "*" + }, + "unmodified" : { + "output" : "*" + } + }, + "68" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 68, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "69" : { + "caps" : { + "output" : "+" + }, + "capsOption" : { + "output" : "+" + }, + "keyCode" : 69, + "option" : { + "output" : "+" + }, + "shift" : { + "output" : "+" + }, + "shiftCaps" : { + "output" : "+" + }, + "shiftCapsOption" : { + "output" : "+" + }, + "shiftOption" : { + "output" : "+" + }, + "unmodified" : { + "output" : "+" + } + }, + "7" : { + "caps" : { + "output" : "Ч" + }, + "capsOption" : { + "output" : "≈" + }, + "keyCode" : 7, + "option" : { + "output" : "≈" + }, + "shift" : { + "output" : "Ч" + }, + "shiftCaps" : { + "output" : "Ч" + }, + "shiftCapsOption" : { + "output" : "≈" + }, + "shiftOption" : { + "output" : "≈" + }, + "unmodified" : { + "output" : "ч" + } + }, + "70" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 70, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "+" + }, + "shiftCaps" : { + "output" : "+" + }, + "shiftCapsOption" : { + "output" : "+" + }, + "shiftOption" : { + "output" : "+" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "71" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 71, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "72" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 72, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "=" + }, + "shiftCaps" : { + "output" : "=" + }, + "shiftCapsOption" : { + "output" : "=" + }, + "shiftOption" : { + "output" : "=" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "73" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 73, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "74" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 74, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "75" : { + "caps" : { + "output" : "\/" + }, + "capsOption" : { + "output" : "\/" + }, + "keyCode" : 75, + "option" : { + "output" : "\/" + }, + "shift" : { + "output" : "\/" + }, + "shiftCaps" : { + "output" : "\/" + }, + "shiftCapsOption" : { + "output" : "\/" + }, + "shiftOption" : { + "output" : "\/" + }, + "unmodified" : { + "output" : "\/" + } + }, + "76" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 76, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "77" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 77, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "\/" + }, + "shiftCaps" : { + "output" : "\/" + }, + "shiftCapsOption" : { + "output" : "\/" + }, + "shiftOption" : { + "output" : "\/" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "78" : { + "caps" : { + "output" : "-" + }, + "capsOption" : { + "output" : "-" + }, + "keyCode" : 78, + "option" : { + "output" : "-" + }, + "shift" : { + "output" : "-" + }, + "shiftCaps" : { + "output" : "-" + }, + "shiftCapsOption" : { + "output" : "-" + }, + "shiftOption" : { + "output" : "-" + }, + "unmodified" : { + "output" : "-" + } + }, + "79" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 79, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "8" : { + "caps" : { + "output" : "С" + }, + "capsOption" : { + "output" : "≠" + }, + "keyCode" : 8, + "option" : { + "output" : "≠" + }, + "shift" : { + "output" : "С" + }, + "shiftCaps" : { + "output" : "С" + }, + "shiftCapsOption" : { + "output" : "≠" + }, + "shiftOption" : { + "output" : "≠" + }, + "unmodified" : { + "output" : "с" + } + }, + "80" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 80, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "81" : { + "caps" : { + "output" : "=" + }, + "capsOption" : { + "output" : "=" + }, + "keyCode" : 81, + "option" : { + "output" : "=" + }, + "shift" : { + "output" : "=" + }, + "shiftCaps" : { + "output" : "=" + }, + "shiftCapsOption" : { + "output" : "=" + }, + "shiftOption" : { + "output" : "=" + }, + "unmodified" : { + "output" : "=" + } + }, + "82" : { + "caps" : { + "output" : "0" + }, + "capsOption" : { + "output" : "0" + }, + "keyCode" : 82, + "option" : { + "output" : "0" + }, + "shift" : { + "output" : "0" + }, + "shiftCaps" : { + "output" : "0" + }, + "shiftCapsOption" : { + "output" : "0" + }, + "shiftOption" : { + "output" : "0" + }, + "unmodified" : { + "output" : "0" + } + }, + "83" : { + "caps" : { + "output" : "1" + }, + "capsOption" : { + "output" : "1" + }, + "keyCode" : 83, + "option" : { + "output" : "1" + }, + "shift" : { + "output" : "1" + }, + "shiftCaps" : { + "output" : "1" + }, + "shiftCapsOption" : { + "output" : "1" + }, + "shiftOption" : { + "output" : "1" + }, + "unmodified" : { + "output" : "1" + } + }, + "84" : { + "caps" : { + "output" : "2" + }, + "capsOption" : { + "output" : "2" + }, + "keyCode" : 84, + "option" : { + "output" : "2" + }, + "shift" : { + "output" : "2" + }, + "shiftCaps" : { + "output" : "2" + }, + "shiftCapsOption" : { + "output" : "2" + }, + "shiftOption" : { + "output" : "2" + }, + "unmodified" : { + "output" : "2" + } + }, + "85" : { + "caps" : { + "output" : "3" + }, + "capsOption" : { + "output" : "3" + }, + "keyCode" : 85, + "option" : { + "output" : "3" + }, + "shift" : { + "output" : "3" + }, + "shiftCaps" : { + "output" : "3" + }, + "shiftCapsOption" : { + "output" : "3" + }, + "shiftOption" : { + "output" : "3" + }, + "unmodified" : { + "output" : "3" + } + }, + "86" : { + "caps" : { + "output" : "4" + }, + "capsOption" : { + "output" : "4" + }, + "keyCode" : 86, + "option" : { + "output" : "4" + }, + "shift" : { + "output" : "4" + }, + "shiftCaps" : { + "output" : "4" + }, + "shiftCapsOption" : { + "output" : "4" + }, + "shiftOption" : { + "output" : "4" + }, + "unmodified" : { + "output" : "4" + } + }, + "87" : { + "caps" : { + "output" : "5" + }, + "capsOption" : { + "output" : "5" + }, + "keyCode" : 87, + "option" : { + "output" : "5" + }, + "shift" : { + "output" : "5" + }, + "shiftCaps" : { + "output" : "5" + }, + "shiftCapsOption" : { + "output" : "5" + }, + "shiftOption" : { + "output" : "5" + }, + "unmodified" : { + "output" : "5" + } + }, + "88" : { + "caps" : { + "output" : "6" + }, + "capsOption" : { + "output" : "6" + }, + "keyCode" : 88, + "option" : { + "output" : "6" + }, + "shift" : { + "output" : "6" + }, + "shiftCaps" : { + "output" : "6" + }, + "shiftCapsOption" : { + "output" : "6" + }, + "shiftOption" : { + "output" : "6" + }, + "unmodified" : { + "output" : "6" + } + }, + "89" : { + "caps" : { + "output" : "7" + }, + "capsOption" : { + "output" : "7" + }, + "keyCode" : 89, + "option" : { + "output" : "7" + }, + "shift" : { + "output" : "7" + }, + "shiftCaps" : { + "output" : "7" + }, + "shiftCapsOption" : { + "output" : "7" + }, + "shiftOption" : { + "output" : "7" + }, + "unmodified" : { + "output" : "7" + } + }, + "9" : { + "caps" : { + "output" : "М" + }, + "capsOption" : { + "output" : "µ" + }, + "keyCode" : 9, + "option" : { + "output" : "µ" + }, + "shift" : { + "output" : "М" + }, + "shiftCaps" : { + "output" : "М" + }, + "shiftCapsOption" : { + "output" : "µ" + }, + "shiftOption" : { + "output" : "µ" + }, + "unmodified" : { + "output" : "м" + } + }, + "90" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 90, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "91" : { + "caps" : { + "output" : "8" + }, + "capsOption" : { + "output" : "8" + }, + "keyCode" : 91, + "option" : { + "output" : "8" + }, + "shift" : { + "output" : "8" + }, + "shiftCaps" : { + "output" : "8" + }, + "shiftCapsOption" : { + "output" : "8" + }, + "shiftOption" : { + "output" : "8" + }, + "unmodified" : { + "output" : "8" + } + }, + "92" : { + "caps" : { + "output" : "9" + }, + "capsOption" : { + "output" : "9" + }, + "keyCode" : 92, + "option" : { + "output" : "9" + }, + "shift" : { + "output" : "9" + }, + "shiftCaps" : { + "output" : "9" + }, + "shiftCapsOption" : { + "output" : "9" + }, + "shiftOption" : { + "output" : "9" + }, + "unmodified" : { + "output" : "9" + } + }, + "93" : { + "caps" : { + "output" : "'" + }, + "capsOption" : { + "output" : "]" + }, + "keyCode" : 93, + "option" : { + "output" : "§" + }, + "shift" : { + "output" : "₴" + }, + "shiftCaps" : { + "output" : "₴" + }, + "shiftCapsOption" : { + "output" : "±" + }, + "shiftOption" : { + "output" : "±" + }, + "unmodified" : { + "output" : "'" + } + }, + "94" : { + "caps" : { + "output" : "Ґ" + }, + "capsOption" : { + "output" : "§" + }, + "keyCode" : 94, + "option" : { + "output" : "]" + }, + "shift" : { + "output" : "Ґ" + }, + "shiftCaps" : { + "output" : "Ґ" + }, + "shiftCapsOption" : { + "output" : "[" + }, + "shiftOption" : { + "output" : "[" + }, + "unmodified" : { + "output" : "ґ" + } + }, + "95" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 95, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "96" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 96, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "97" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 97, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "98" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 98, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "99" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 99, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + } + }, + "keyboardType" : 91, + "osBuild" : "24G830", + "osVersion" : "15.7.9", + "schemaVersion" : 1, + "sourceID" : "com.apple.keylayout.Ukrainian-PC" +} \ No newline at end of file diff --git a/Benchmarks/layout-tables/v1/com.apple.keylayout.Ukrainian-QWERTY@37.json b/Benchmarks/layout-tables/v1/com.apple.keylayout.Ukrainian-QWERTY@37.json new file mode 100644 index 0000000..e5a67c6 --- /dev/null +++ b/Benchmarks/layout-tables/v1/com.apple.keylayout.Ukrainian-QWERTY@37.json @@ -0,0 +1,3465 @@ +{ + "keyOutputs" : { + "0" : { + "caps" : { + "output" : "А" + }, + "capsOption" : { + "output" : "а" + }, + "keyCode" : 0, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "output" : "А" + }, + "shiftCaps" : { + "output" : "А" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "output" : "а" + } + }, + "1" : { + "caps" : { + "output" : "С" + }, + "capsOption" : { + "output" : "с" + }, + "keyCode" : 1, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "output" : "С" + }, + "shiftCaps" : { + "output" : "С" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "output" : "с" + } + }, + "10" : { + "caps" : { + "output" : "§" + }, + "capsOption" : { + "output" : "§" + }, + "keyCode" : 10, + "option" : { + "output" : "`" + }, + "shift" : { + "output" : "±" + }, + "shiftCaps" : { + "output" : "±" + }, + "shiftCapsOption" : { + "output" : "~" + }, + "shiftOption" : { + "output" : "~" + }, + "unmodified" : { + "output" : "§" + } + }, + "100" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 100, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "101" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 101, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "102" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 102, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "103" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 103, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "104" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 104, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "105" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 105, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "106" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 106, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "107" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 107, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "108" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 108, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "109" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 109, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "11" : { + "caps" : { + "output" : "Б" + }, + "capsOption" : { + "output" : "б" + }, + "keyCode" : 11, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "output" : "Б" + }, + "shiftCaps" : { + "output" : "Б" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "output" : "б" + } + }, + "110" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 110, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "111" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 111, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "112" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 112, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "113" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 113, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "114" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 114, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "115" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 115, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "116" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 116, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "117" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 117, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "118" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 118, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "119" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 119, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "12" : { + "caps" : { + "output" : "Я" + }, + "capsOption" : { + "output" : "я" + }, + "keyCode" : 12, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "output" : "Я" + }, + "shiftCaps" : { + "output" : "Я" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "output" : "я" + } + }, + "120" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 120, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "121" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 121, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "122" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 122, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "123" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 123, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "124" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 124, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "125" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 125, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "126" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 126, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "127" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 127, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "13" : { + "caps" : { + "output" : "Ш" + }, + "capsOption" : { + "output" : "ш" + }, + "keyCode" : 13, + "option" : { + "output" : "щ" + }, + "shift" : { + "output" : "Ш" + }, + "shiftCaps" : { + "output" : "Ш" + }, + "shiftCapsOption" : { + "output" : "Щ" + }, + "shiftOption" : { + "output" : "Щ" + }, + "unmodified" : { + "output" : "ш" + } + }, + "14" : { + "caps" : { + "output" : "Е" + }, + "capsOption" : { + "output" : "е" + }, + "keyCode" : 14, + "option" : { + "output" : "ё" + }, + "shift" : { + "output" : "Е" + }, + "shiftCaps" : { + "output" : "Е" + }, + "shiftCapsOption" : { + "output" : "Ё" + }, + "shiftOption" : { + "output" : "Ё" + }, + "unmodified" : { + "output" : "е" + } + }, + "15" : { + "caps" : { + "output" : "Р" + }, + "capsOption" : { + "output" : "р" + }, + "keyCode" : 15, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "output" : "Р" + }, + "shiftCaps" : { + "output" : "Р" + }, + "shiftCapsOption" : { + "output" : "®" + }, + "shiftOption" : { + "output" : "®" + }, + "unmodified" : { + "output" : "р" + } + }, + "16" : { + "caps" : { + "output" : "И" + }, + "capsOption" : { + "output" : "и" + }, + "keyCode" : 16, + "option" : { + "output" : "ы" + }, + "shift" : { + "output" : "И" + }, + "shiftCaps" : { + "output" : "И" + }, + "shiftCapsOption" : { + "output" : "Ы" + }, + "shiftOption" : { + "output" : "Ы" + }, + "unmodified" : { + "output" : "и" + } + }, + "17" : { + "caps" : { + "output" : "Т" + }, + "capsOption" : { + "output" : "т" + }, + "keyCode" : 17, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "output" : "Т" + }, + "shiftCaps" : { + "output" : "Т" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "output" : "т" + } + }, + "18" : { + "caps" : { + "output" : "1" + }, + "capsOption" : { + "output" : "1" + }, + "keyCode" : 18, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "output" : "!" + }, + "shiftCaps" : { + "output" : "!" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "output" : "1" + } + }, + "19" : { + "caps" : { + "output" : "2" + }, + "capsOption" : { + "output" : "2" + }, + "keyCode" : 19, + "option" : { + "output" : "™" + }, + "shift" : { + "output" : "@" + }, + "shiftCaps" : { + "output" : "@" + }, + "shiftCapsOption" : { + "output" : "£" + }, + "shiftOption" : { + "output" : "£" + }, + "unmodified" : { + "output" : "2" + } + }, + "2" : { + "caps" : { + "output" : "Д" + }, + "capsOption" : { + "output" : "д" + }, + "keyCode" : 2, + "option" : { + "output" : "ð" + }, + "shift" : { + "output" : "Д" + }, + "shiftCaps" : { + "output" : "Д" + }, + "shiftCapsOption" : { + "output" : "∆" + }, + "shiftOption" : { + "output" : "∆" + }, + "unmodified" : { + "output" : "д" + } + }, + "20" : { + "caps" : { + "output" : "3" + }, + "capsOption" : { + "output" : "3" + }, + "keyCode" : 20, + "option" : { + "output" : "№" + }, + "shift" : { + "output" : "#" + }, + "shiftCaps" : { + "output" : "#" + }, + "shiftCapsOption" : { + "output" : "€" + }, + "shiftOption" : { + "output" : "€" + }, + "unmodified" : { + "output" : "3" + } + }, + "21" : { + "caps" : { + "output" : "4" + }, + "capsOption" : { + "output" : "4" + }, + "keyCode" : 21, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "output" : "$" + }, + "shiftCaps" : { + "output" : "$" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "output" : "4" + } + }, + "22" : { + "caps" : { + "output" : "6" + }, + "capsOption" : { + "output" : "6" + }, + "keyCode" : 22, + "option" : { + "output" : "§" + }, + "shift" : { + "output" : "^" + }, + "shiftCaps" : { + "output" : "^" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "output" : "6" + } + }, + "23" : { + "caps" : { + "output" : "5" + }, + "capsOption" : { + "output" : "5" + }, + "keyCode" : 23, + "option" : { + "output" : "∞" + }, + "shift" : { + "output" : "%" + }, + "shiftCaps" : { + "output" : "%" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "output" : "5" + } + }, + "24" : { + "caps" : { + "output" : "=" + }, + "capsOption" : { + "output" : "=" + }, + "keyCode" : 24, + "option" : { + "output" : "≠" + }, + "shift" : { + "output" : "+" + }, + "shiftCaps" : { + "output" : "+" + }, + "shiftCapsOption" : { + "output" : "±" + }, + "shiftOption" : { + "output" : "±" + }, + "unmodified" : { + "output" : "=" + } + }, + "25" : { + "caps" : { + "output" : "9" + }, + "capsOption" : { + "output" : "9" + }, + "keyCode" : 25, + "option" : { + "output" : "[" + }, + "shift" : { + "output" : "(" + }, + "shiftCaps" : { + "output" : "(" + }, + "shiftCapsOption" : { + "output" : "{" + }, + "shiftOption" : { + "output" : "{" + }, + "unmodified" : { + "output" : "9" + } + }, + "26" : { + "caps" : { + "output" : "7" + }, + "capsOption" : { + "output" : "7" + }, + "keyCode" : 26, + "option" : { + "output" : "¶" + }, + "shift" : { + "output" : "&" + }, + "shiftCaps" : { + "output" : "&" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "output" : "7" + } + }, + "27" : { + "caps" : { + "output" : "-" + }, + "capsOption" : { + "output" : "-" + }, + "keyCode" : 27, + "option" : { + "output" : "–" + }, + "shift" : { + "output" : "_" + }, + "shiftCaps" : { + "output" : "_" + }, + "shiftCapsOption" : { + "output" : "—" + }, + "shiftOption" : { + "output" : "—" + }, + "unmodified" : { + "output" : "-" + } + }, + "28" : { + "caps" : { + "output" : "8" + }, + "capsOption" : { + "output" : "8" + }, + "keyCode" : 28, + "option" : { + "output" : "•" + }, + "shift" : { + "output" : "*" + }, + "shiftCaps" : { + "output" : "*" + }, + "shiftCapsOption" : { + "output" : "°" + }, + "shiftOption" : { + "output" : "°" + }, + "unmodified" : { + "output" : "8" + } + }, + "29" : { + "caps" : { + "output" : "0" + }, + "capsOption" : { + "output" : "0" + }, + "keyCode" : 29, + "option" : { + "output" : "]" + }, + "shift" : { + "output" : ")" + }, + "shiftCaps" : { + "output" : ")" + }, + "shiftCapsOption" : { + "output" : "}" + }, + "shiftOption" : { + "output" : "}" + }, + "unmodified" : { + "output" : "0" + } + }, + "3" : { + "caps" : { + "output" : "Ф" + }, + "capsOption" : { + "output" : "ф" + }, + "keyCode" : 3, + "option" : { + "output" : "ƒ" + }, + "shift" : { + "output" : "Ф" + }, + "shiftCaps" : { + "output" : "Ф" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "output" : "ф" + } + }, + "30" : { + "caps" : { + "output" : "Ж" + }, + "capsOption" : { + "output" : "ж" + }, + "keyCode" : 30, + "option" : { + "output" : "]" + }, + "shift" : { + "output" : "Ж" + }, + "shiftCaps" : { + "output" : "Ж" + }, + "shiftCapsOption" : { + "output" : "}" + }, + "shiftOption" : { + "output" : "}" + }, + "unmodified" : { + "output" : "ж" + } + }, + "31" : { + "caps" : { + "output" : "О" + }, + "capsOption" : { + "output" : "о" + }, + "keyCode" : 31, + "option" : { + "output" : "«" + }, + "shift" : { + "output" : "О" + }, + "shiftCaps" : { + "output" : "О" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "output" : "о" + } + }, + "32" : { + "caps" : { + "output" : "У" + }, + "capsOption" : { + "output" : "у" + }, + "keyCode" : 32, + "option" : { + "output" : "ў" + }, + "shift" : { + "output" : "У" + }, + "shiftCaps" : { + "output" : "У" + }, + "shiftCapsOption" : { + "output" : "Ў" + }, + "shiftOption" : { + "output" : "Ў" + }, + "unmodified" : { + "output" : "у" + } + }, + "33" : { + "caps" : { + "output" : "Ю" + }, + "capsOption" : { + "output" : "ю" + }, + "keyCode" : 33, + "option" : { + "output" : "[" + }, + "shift" : { + "output" : "Ю" + }, + "shiftCaps" : { + "output" : "Ю" + }, + "shiftCapsOption" : { + "output" : "{" + }, + "shiftOption" : { + "output" : "{" + }, + "unmodified" : { + "output" : "ю" + } + }, + "34" : { + "caps" : { + "output" : "І" + }, + "capsOption" : { + "output" : "і" + }, + "keyCode" : 34, + "option" : { + "output" : "ї" + }, + "shift" : { + "output" : "І" + }, + "shiftCaps" : { + "output" : "І" + }, + "shiftCapsOption" : { + "output" : "Ї" + }, + "shiftOption" : { + "output" : "Ї" + }, + "unmodified" : { + "output" : "і" + } + }, + "35" : { + "caps" : { + "output" : "П" + }, + "capsOption" : { + "output" : "п" + }, + "keyCode" : 35, + "option" : { + "output" : "»" + }, + "shift" : { + "output" : "П" + }, + "shiftCaps" : { + "output" : "П" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "output" : "п" + } + }, + "36" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 36, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "37" : { + "caps" : { + "output" : "Л" + }, + "capsOption" : { + "output" : "л" + }, + "keyCode" : 37, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "output" : "Л" + }, + "shiftCaps" : { + "output" : "Л" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "output" : "л" + } + }, + "38" : { + "caps" : { + "output" : "Й" + }, + "capsOption" : { + "output" : "й" + }, + "keyCode" : 38, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "output" : "Й" + }, + "shiftCaps" : { + "output" : "Й" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "output" : "й" + } + }, + "39" : { + "caps" : { + "output" : "ʼ" + }, + "capsOption" : { + "output" : "ʼ" + }, + "keyCode" : 39, + "option" : { + "output" : "'" + }, + "shift" : { + "output" : "₴" + }, + "shiftCaps" : { + "output" : "₴" + }, + "shiftCapsOption" : { + "output" : "\"" + }, + "shiftOption" : { + "output" : "\"" + }, + "unmodified" : { + "output" : "ʼ" + } + }, + "4" : { + "caps" : { + "output" : "Ч" + }, + "capsOption" : { + "output" : "ч" + }, + "keyCode" : 4, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "output" : "Ч" + }, + "shiftCaps" : { + "output" : "Ч" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "output" : "ч" + } + }, + "40" : { + "caps" : { + "output" : "К" + }, + "capsOption" : { + "output" : "к" + }, + "keyCode" : 40, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "output" : "К" + }, + "shiftCaps" : { + "output" : "К" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "output" : "к" + } + }, + "41" : { + "caps" : { + "output" : ";" + }, + "capsOption" : { + "output" : ";" + }, + "keyCode" : 41, + "option" : { + "output" : "…" + }, + "shift" : { + "output" : ":" + }, + "shiftCaps" : { + "output" : ":" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "output" : ";" + } + }, + "42" : { + "caps" : { + "output" : "Є" + }, + "capsOption" : { + "output" : "є" + }, + "keyCode" : 42, + "option" : { + "output" : "\\" + }, + "shift" : { + "output" : "Є" + }, + "shiftCaps" : { + "output" : "Є" + }, + "shiftCapsOption" : { + "output" : "|" + }, + "shiftOption" : { + "output" : "|" + }, + "unmodified" : { + "output" : "є" + } + }, + "43" : { + "caps" : { + "output" : "," + }, + "capsOption" : { + "output" : "," + }, + "keyCode" : 43, + "option" : { + "output" : "≤" + }, + "shift" : { + "output" : "<" + }, + "shiftCaps" : { + "output" : "<" + }, + "shiftCapsOption" : { + "output" : "„" + }, + "shiftOption" : { + "output" : "„" + }, + "unmodified" : { + "output" : "," + } + }, + "44" : { + "caps" : { + "output" : "\/" + }, + "capsOption" : { + "output" : "\/" + }, + "keyCode" : 44, + "option" : { + "output" : "÷" + }, + "shift" : { + "output" : "?" + }, + "shiftCaps" : { + "output" : "?" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "output" : "\/" + } + }, + "45" : { + "caps" : { + "output" : "Н" + }, + "capsOption" : { + "output" : "н" + }, + "keyCode" : 45, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "output" : "Н" + }, + "shiftCaps" : { + "output" : "Н" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "output" : "н" + } + }, + "46" : { + "caps" : { + "output" : "М" + }, + "capsOption" : { + "output" : "м" + }, + "keyCode" : 46, + "option" : { + "output" : "µ" + }, + "shift" : { + "output" : "М" + }, + "shiftCaps" : { + "output" : "М" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "output" : "м" + } + }, + "47" : { + "caps" : { + "output" : "." + }, + "capsOption" : { + "output" : "." + }, + "keyCode" : 47, + "option" : { + "output" : "≥" + }, + "shift" : { + "output" : ">" + }, + "shiftCaps" : { + "output" : ">" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "output" : "." + } + }, + "48" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 48, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "49" : { + "caps" : { + "output" : " " + }, + "capsOption" : { + "output" : " " + }, + "keyCode" : 49, + "option" : { + "output" : " " + }, + "shift" : { + "output" : " " + }, + "shiftCaps" : { + "output" : " " + }, + "shiftCapsOption" : { + "output" : " " + }, + "shiftOption" : { + "output" : " " + }, + "unmodified" : { + "output" : " " + } + }, + "5" : { + "caps" : { + "output" : "Г" + }, + "capsOption" : { + "output" : "г" + }, + "keyCode" : 5, + "option" : { + "output" : "ґ" + }, + "shift" : { + "output" : "Г" + }, + "shiftCaps" : { + "output" : "Г" + }, + "shiftCapsOption" : { + "output" : "Ґ" + }, + "shiftOption" : { + "output" : "Ґ" + }, + "unmodified" : { + "output" : "г" + } + }, + "50" : { + "caps" : { + "output" : "Ь" + }, + "capsOption" : { + "output" : "ь" + }, + "keyCode" : 50, + "option" : { + "output" : "ъ" + }, + "shift" : { + "output" : "Ь" + }, + "shiftCaps" : { + "output" : "Ь" + }, + "shiftCapsOption" : { + "output" : "Ъ" + }, + "shiftOption" : { + "output" : "Ъ" + }, + "unmodified" : { + "output" : "ь" + } + }, + "51" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 51, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "52" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 52, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "53" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 53, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "54" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 54, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "55" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 55, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "56" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 56, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "57" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 57, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "58" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 58, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "59" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 59, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "6" : { + "caps" : { + "output" : "З" + }, + "capsOption" : { + "output" : "з" + }, + "keyCode" : 6, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "output" : "З" + }, + "shiftCaps" : { + "output" : "З" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "output" : "з" + } + }, + "60" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 60, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "61" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 61, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "62" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 62, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "63" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 63, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "64" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 64, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "65" : { + "caps" : { + "output" : "," + }, + "capsOption" : { + "output" : "," + }, + "keyCode" : 65, + "option" : { + "output" : "," + }, + "shift" : { + "output" : "," + }, + "shiftCaps" : { + "output" : "," + }, + "shiftCapsOption" : { + "output" : "," + }, + "shiftOption" : { + "output" : "," + }, + "unmodified" : { + "output" : "," + } + }, + "66" : { + "caps" : { + "output" : "*" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 66, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "*" + }, + "shiftCaps" : { + "output" : "*" + }, + "shiftCapsOption" : { + "output" : "*" + }, + "shiftOption" : { + "output" : "*" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "67" : { + "caps" : { + "output" : "*" + }, + "capsOption" : { + "output" : "*" + }, + "keyCode" : 67, + "option" : { + "output" : "*" + }, + "shift" : { + "output" : "*" + }, + "shiftCaps" : { + "output" : "*" + }, + "shiftCapsOption" : { + "output" : "*" + }, + "shiftOption" : { + "output" : "*" + }, + "unmodified" : { + "output" : "*" + } + }, + "68" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 68, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "69" : { + "caps" : { + "output" : "+" + }, + "capsOption" : { + "output" : "+" + }, + "keyCode" : 69, + "option" : { + "output" : "+" + }, + "shift" : { + "output" : "+" + }, + "shiftCaps" : { + "output" : "+" + }, + "shiftCapsOption" : { + "output" : "+" + }, + "shiftOption" : { + "output" : "+" + }, + "unmodified" : { + "output" : "+" + } + }, + "7" : { + "caps" : { + "output" : "Х" + }, + "capsOption" : { + "output" : "х" + }, + "keyCode" : 7, + "option" : { + "output" : "≈" + }, + "shift" : { + "output" : "Х" + }, + "shiftCaps" : { + "output" : "Х" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "output" : "х" + } + }, + "70" : { + "caps" : { + "output" : "+" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 70, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "+" + }, + "shiftCaps" : { + "output" : "+" + }, + "shiftCapsOption" : { + "output" : "+" + }, + "shiftOption" : { + "output" : "+" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "71" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 71, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "72" : { + "caps" : { + "output" : "=" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 72, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "=" + }, + "shiftCaps" : { + "output" : "=" + }, + "shiftCapsOption" : { + "output" : "=" + }, + "shiftOption" : { + "output" : "=" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "73" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 73, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "74" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 74, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "75" : { + "caps" : { + "output" : "\/" + }, + "capsOption" : { + "output" : "\/" + }, + "keyCode" : 75, + "option" : { + "output" : "\/" + }, + "shift" : { + "output" : "\/" + }, + "shiftCaps" : { + "output" : "\/" + }, + "shiftCapsOption" : { + "output" : "\/" + }, + "shiftOption" : { + "output" : "\/" + }, + "unmodified" : { + "output" : "\/" + } + }, + "76" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 76, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "77" : { + "caps" : { + "output" : "\/" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 77, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "\/" + }, + "shiftCaps" : { + "output" : "\/" + }, + "shiftCapsOption" : { + "output" : "\/" + }, + "shiftOption" : { + "output" : "\/" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "78" : { + "caps" : { + "output" : "-" + }, + "capsOption" : { + "output" : "-" + }, + "keyCode" : 78, + "option" : { + "output" : "-" + }, + "shift" : { + "output" : "-" + }, + "shiftCaps" : { + "output" : "-" + }, + "shiftCapsOption" : { + "output" : "-" + }, + "shiftOption" : { + "output" : "-" + }, + "unmodified" : { + "output" : "-" + } + }, + "79" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 79, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "8" : { + "caps" : { + "output" : "Ц" + }, + "capsOption" : { + "output" : "ц" + }, + "keyCode" : 8, + "option" : { + "output" : "©" + }, + "shift" : { + "output" : "Ц" + }, + "shiftCaps" : { + "output" : "Ц" + }, + "shiftCapsOption" : { + "output" : "©" + }, + "shiftOption" : { + "output" : "©" + }, + "unmodified" : { + "output" : "ц" + } + }, + "80" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 80, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "81" : { + "caps" : { + "output" : "=" + }, + "capsOption" : { + "output" : "=" + }, + "keyCode" : 81, + "option" : { + "output" : "=" + }, + "shift" : { + "output" : "=" + }, + "shiftCaps" : { + "output" : "=" + }, + "shiftCapsOption" : { + "output" : "=" + }, + "shiftOption" : { + "output" : "=" + }, + "unmodified" : { + "output" : "=" + } + }, + "82" : { + "caps" : { + "output" : "0" + }, + "capsOption" : { + "output" : "0" + }, + "keyCode" : 82, + "option" : { + "output" : "0" + }, + "shift" : { + "output" : "0" + }, + "shiftCaps" : { + "output" : "0" + }, + "shiftCapsOption" : { + "output" : "0" + }, + "shiftOption" : { + "output" : "0" + }, + "unmodified" : { + "output" : "0" + } + }, + "83" : { + "caps" : { + "output" : "1" + }, + "capsOption" : { + "output" : "1" + }, + "keyCode" : 83, + "option" : { + "output" : "1" + }, + "shift" : { + "output" : "1" + }, + "shiftCaps" : { + "output" : "1" + }, + "shiftCapsOption" : { + "output" : "1" + }, + "shiftOption" : { + "output" : "1" + }, + "unmodified" : { + "output" : "1" + } + }, + "84" : { + "caps" : { + "output" : "2" + }, + "capsOption" : { + "output" : "2" + }, + "keyCode" : 84, + "option" : { + "output" : "2" + }, + "shift" : { + "output" : "2" + }, + "shiftCaps" : { + "output" : "2" + }, + "shiftCapsOption" : { + "output" : "2" + }, + "shiftOption" : { + "output" : "2" + }, + "unmodified" : { + "output" : "2" + } + }, + "85" : { + "caps" : { + "output" : "3" + }, + "capsOption" : { + "output" : "3" + }, + "keyCode" : 85, + "option" : { + "output" : "3" + }, + "shift" : { + "output" : "3" + }, + "shiftCaps" : { + "output" : "3" + }, + "shiftCapsOption" : { + "output" : "3" + }, + "shiftOption" : { + "output" : "3" + }, + "unmodified" : { + "output" : "3" + } + }, + "86" : { + "caps" : { + "output" : "4" + }, + "capsOption" : { + "output" : "4" + }, + "keyCode" : 86, + "option" : { + "output" : "4" + }, + "shift" : { + "output" : "4" + }, + "shiftCaps" : { + "output" : "4" + }, + "shiftCapsOption" : { + "output" : "4" + }, + "shiftOption" : { + "output" : "4" + }, + "unmodified" : { + "output" : "4" + } + }, + "87" : { + "caps" : { + "output" : "5" + }, + "capsOption" : { + "output" : "5" + }, + "keyCode" : 87, + "option" : { + "output" : "5" + }, + "shift" : { + "output" : "5" + }, + "shiftCaps" : { + "output" : "5" + }, + "shiftCapsOption" : { + "output" : "5" + }, + "shiftOption" : { + "output" : "5" + }, + "unmodified" : { + "output" : "5" + } + }, + "88" : { + "caps" : { + "output" : "6" + }, + "capsOption" : { + "output" : "6" + }, + "keyCode" : 88, + "option" : { + "output" : "6" + }, + "shift" : { + "output" : "6" + }, + "shiftCaps" : { + "output" : "6" + }, + "shiftCapsOption" : { + "output" : "6" + }, + "shiftOption" : { + "output" : "6" + }, + "unmodified" : { + "output" : "6" + } + }, + "89" : { + "caps" : { + "output" : "7" + }, + "capsOption" : { + "output" : "7" + }, + "keyCode" : 89, + "option" : { + "output" : "7" + }, + "shift" : { + "output" : "7" + }, + "shiftCaps" : { + "output" : "7" + }, + "shiftCapsOption" : { + "output" : "7" + }, + "shiftOption" : { + "output" : "7" + }, + "unmodified" : { + "output" : "7" + } + }, + "9" : { + "caps" : { + "output" : "В" + }, + "capsOption" : { + "output" : "в" + }, + "keyCode" : 9, + "option" : { + "output" : "√" + }, + "shift" : { + "output" : "В" + }, + "shiftCaps" : { + "output" : "В" + }, + "shiftCapsOption" : { + "output" : "†" + }, + "shiftOption" : { + "output" : "†" + }, + "unmodified" : { + "output" : "в" + } + }, + "90" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 90, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "91" : { + "caps" : { + "output" : "8" + }, + "capsOption" : { + "output" : "8" + }, + "keyCode" : 91, + "option" : { + "output" : "8" + }, + "shift" : { + "output" : "8" + }, + "shiftCaps" : { + "output" : "8" + }, + "shiftCapsOption" : { + "output" : "8" + }, + "shiftOption" : { + "output" : "8" + }, + "unmodified" : { + "output" : "8" + } + }, + "92" : { + "caps" : { + "output" : "9" + }, + "capsOption" : { + "output" : "9" + }, + "keyCode" : 92, + "option" : { + "output" : "9" + }, + "shift" : { + "output" : "9" + }, + "shiftCaps" : { + "output" : "9" + }, + "shiftCapsOption" : { + "output" : "9" + }, + "shiftOption" : { + "output" : "9" + }, + "unmodified" : { + "output" : "9" + } + }, + "93" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 93, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "94" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 94, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "95" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 95, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "96" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 96, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "97" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 97, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "98" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 98, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "99" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 99, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + } + }, + "keyboardType" : 37, + "osBuild" : "24G830", + "osVersion" : "15.7.9", + "schemaVersion" : 1, + "sourceID" : "com.apple.keylayout.Ukrainian-QWERTY" +} \ No newline at end of file diff --git a/Benchmarks/layout-tables/v1/com.apple.keylayout.Ukrainian-QWERTY@91.json b/Benchmarks/layout-tables/v1/com.apple.keylayout.Ukrainian-QWERTY@91.json new file mode 100644 index 0000000..f146c15 --- /dev/null +++ b/Benchmarks/layout-tables/v1/com.apple.keylayout.Ukrainian-QWERTY@91.json @@ -0,0 +1,3465 @@ +{ + "keyOutputs" : { + "0" : { + "caps" : { + "output" : "А" + }, + "capsOption" : { + "output" : "а" + }, + "keyCode" : 0, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "output" : "А" + }, + "shiftCaps" : { + "output" : "А" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "output" : "а" + } + }, + "1" : { + "caps" : { + "output" : "С" + }, + "capsOption" : { + "output" : "с" + }, + "keyCode" : 1, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "output" : "С" + }, + "shiftCaps" : { + "output" : "С" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "output" : "с" + } + }, + "10" : { + "caps" : { + "output" : "§" + }, + "capsOption" : { + "output" : "§" + }, + "keyCode" : 10, + "option" : { + "output" : "`" + }, + "shift" : { + "output" : "±" + }, + "shiftCaps" : { + "output" : "±" + }, + "shiftCapsOption" : { + "output" : "~" + }, + "shiftOption" : { + "output" : "~" + }, + "unmodified" : { + "output" : "§" + } + }, + "100" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 100, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "101" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 101, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "102" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 102, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "103" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 103, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "104" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 104, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "105" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 105, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "106" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 106, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "107" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 107, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "108" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 108, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "109" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 109, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "11" : { + "caps" : { + "output" : "Б" + }, + "capsOption" : { + "output" : "б" + }, + "keyCode" : 11, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "output" : "Б" + }, + "shiftCaps" : { + "output" : "Б" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "output" : "б" + } + }, + "110" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 110, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "111" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 111, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "112" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 112, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "113" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 113, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "114" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 114, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "115" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 115, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "116" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 116, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "117" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 117, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "118" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 118, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "119" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 119, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "12" : { + "caps" : { + "output" : "Я" + }, + "capsOption" : { + "output" : "я" + }, + "keyCode" : 12, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "output" : "Я" + }, + "shiftCaps" : { + "output" : "Я" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "output" : "я" + } + }, + "120" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 120, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "121" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 121, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "122" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 122, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "123" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 123, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "124" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 124, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "125" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 125, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "126" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 126, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "127" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 127, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "13" : { + "caps" : { + "output" : "Ш" + }, + "capsOption" : { + "output" : "ш" + }, + "keyCode" : 13, + "option" : { + "output" : "щ" + }, + "shift" : { + "output" : "Ш" + }, + "shiftCaps" : { + "output" : "Ш" + }, + "shiftCapsOption" : { + "output" : "Щ" + }, + "shiftOption" : { + "output" : "Щ" + }, + "unmodified" : { + "output" : "ш" + } + }, + "14" : { + "caps" : { + "output" : "Е" + }, + "capsOption" : { + "output" : "е" + }, + "keyCode" : 14, + "option" : { + "output" : "ё" + }, + "shift" : { + "output" : "Е" + }, + "shiftCaps" : { + "output" : "Е" + }, + "shiftCapsOption" : { + "output" : "Ё" + }, + "shiftOption" : { + "output" : "Ё" + }, + "unmodified" : { + "output" : "е" + } + }, + "15" : { + "caps" : { + "output" : "Р" + }, + "capsOption" : { + "output" : "р" + }, + "keyCode" : 15, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "output" : "Р" + }, + "shiftCaps" : { + "output" : "Р" + }, + "shiftCapsOption" : { + "output" : "®" + }, + "shiftOption" : { + "output" : "®" + }, + "unmodified" : { + "output" : "р" + } + }, + "16" : { + "caps" : { + "output" : "И" + }, + "capsOption" : { + "output" : "и" + }, + "keyCode" : 16, + "option" : { + "output" : "ы" + }, + "shift" : { + "output" : "И" + }, + "shiftCaps" : { + "output" : "И" + }, + "shiftCapsOption" : { + "output" : "Ы" + }, + "shiftOption" : { + "output" : "Ы" + }, + "unmodified" : { + "output" : "и" + } + }, + "17" : { + "caps" : { + "output" : "Т" + }, + "capsOption" : { + "output" : "т" + }, + "keyCode" : 17, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "output" : "Т" + }, + "shiftCaps" : { + "output" : "Т" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "output" : "т" + } + }, + "18" : { + "caps" : { + "output" : "1" + }, + "capsOption" : { + "output" : "1" + }, + "keyCode" : 18, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "output" : "!" + }, + "shiftCaps" : { + "output" : "!" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "output" : "1" + } + }, + "19" : { + "caps" : { + "output" : "2" + }, + "capsOption" : { + "output" : "2" + }, + "keyCode" : 19, + "option" : { + "output" : "™" + }, + "shift" : { + "output" : "@" + }, + "shiftCaps" : { + "output" : "@" + }, + "shiftCapsOption" : { + "output" : "£" + }, + "shiftOption" : { + "output" : "£" + }, + "unmodified" : { + "output" : "2" + } + }, + "2" : { + "caps" : { + "output" : "Д" + }, + "capsOption" : { + "output" : "д" + }, + "keyCode" : 2, + "option" : { + "output" : "ð" + }, + "shift" : { + "output" : "Д" + }, + "shiftCaps" : { + "output" : "Д" + }, + "shiftCapsOption" : { + "output" : "∆" + }, + "shiftOption" : { + "output" : "∆" + }, + "unmodified" : { + "output" : "д" + } + }, + "20" : { + "caps" : { + "output" : "3" + }, + "capsOption" : { + "output" : "3" + }, + "keyCode" : 20, + "option" : { + "output" : "№" + }, + "shift" : { + "output" : "#" + }, + "shiftCaps" : { + "output" : "#" + }, + "shiftCapsOption" : { + "output" : "€" + }, + "shiftOption" : { + "output" : "€" + }, + "unmodified" : { + "output" : "3" + } + }, + "21" : { + "caps" : { + "output" : "4" + }, + "capsOption" : { + "output" : "4" + }, + "keyCode" : 21, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "output" : "$" + }, + "shiftCaps" : { + "output" : "$" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "output" : "4" + } + }, + "22" : { + "caps" : { + "output" : "6" + }, + "capsOption" : { + "output" : "6" + }, + "keyCode" : 22, + "option" : { + "output" : "§" + }, + "shift" : { + "output" : "^" + }, + "shiftCaps" : { + "output" : "^" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "output" : "6" + } + }, + "23" : { + "caps" : { + "output" : "5" + }, + "capsOption" : { + "output" : "5" + }, + "keyCode" : 23, + "option" : { + "output" : "∞" + }, + "shift" : { + "output" : "%" + }, + "shiftCaps" : { + "output" : "%" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "output" : "5" + } + }, + "24" : { + "caps" : { + "output" : "=" + }, + "capsOption" : { + "output" : "=" + }, + "keyCode" : 24, + "option" : { + "output" : "≠" + }, + "shift" : { + "output" : "+" + }, + "shiftCaps" : { + "output" : "+" + }, + "shiftCapsOption" : { + "output" : "±" + }, + "shiftOption" : { + "output" : "±" + }, + "unmodified" : { + "output" : "=" + } + }, + "25" : { + "caps" : { + "output" : "9" + }, + "capsOption" : { + "output" : "9" + }, + "keyCode" : 25, + "option" : { + "output" : "[" + }, + "shift" : { + "output" : "(" + }, + "shiftCaps" : { + "output" : "(" + }, + "shiftCapsOption" : { + "output" : "{" + }, + "shiftOption" : { + "output" : "{" + }, + "unmodified" : { + "output" : "9" + } + }, + "26" : { + "caps" : { + "output" : "7" + }, + "capsOption" : { + "output" : "7" + }, + "keyCode" : 26, + "option" : { + "output" : "¶" + }, + "shift" : { + "output" : "&" + }, + "shiftCaps" : { + "output" : "&" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "output" : "7" + } + }, + "27" : { + "caps" : { + "output" : "-" + }, + "capsOption" : { + "output" : "-" + }, + "keyCode" : 27, + "option" : { + "output" : "–" + }, + "shift" : { + "output" : "_" + }, + "shiftCaps" : { + "output" : "_" + }, + "shiftCapsOption" : { + "output" : "—" + }, + "shiftOption" : { + "output" : "—" + }, + "unmodified" : { + "output" : "-" + } + }, + "28" : { + "caps" : { + "output" : "8" + }, + "capsOption" : { + "output" : "8" + }, + "keyCode" : 28, + "option" : { + "output" : "•" + }, + "shift" : { + "output" : "*" + }, + "shiftCaps" : { + "output" : "*" + }, + "shiftCapsOption" : { + "output" : "°" + }, + "shiftOption" : { + "output" : "°" + }, + "unmodified" : { + "output" : "8" + } + }, + "29" : { + "caps" : { + "output" : "0" + }, + "capsOption" : { + "output" : "0" + }, + "keyCode" : 29, + "option" : { + "output" : "]" + }, + "shift" : { + "output" : ")" + }, + "shiftCaps" : { + "output" : ")" + }, + "shiftCapsOption" : { + "output" : "}" + }, + "shiftOption" : { + "output" : "}" + }, + "unmodified" : { + "output" : "0" + } + }, + "3" : { + "caps" : { + "output" : "Ф" + }, + "capsOption" : { + "output" : "ф" + }, + "keyCode" : 3, + "option" : { + "output" : "ƒ" + }, + "shift" : { + "output" : "Ф" + }, + "shiftCaps" : { + "output" : "Ф" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "output" : "ф" + } + }, + "30" : { + "caps" : { + "output" : "Ж" + }, + "capsOption" : { + "output" : "ж" + }, + "keyCode" : 30, + "option" : { + "output" : "]" + }, + "shift" : { + "output" : "Ж" + }, + "shiftCaps" : { + "output" : "Ж" + }, + "shiftCapsOption" : { + "output" : "}" + }, + "shiftOption" : { + "output" : "}" + }, + "unmodified" : { + "output" : "ж" + } + }, + "31" : { + "caps" : { + "output" : "О" + }, + "capsOption" : { + "output" : "о" + }, + "keyCode" : 31, + "option" : { + "output" : "«" + }, + "shift" : { + "output" : "О" + }, + "shiftCaps" : { + "output" : "О" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "output" : "о" + } + }, + "32" : { + "caps" : { + "output" : "У" + }, + "capsOption" : { + "output" : "у" + }, + "keyCode" : 32, + "option" : { + "output" : "ў" + }, + "shift" : { + "output" : "У" + }, + "shiftCaps" : { + "output" : "У" + }, + "shiftCapsOption" : { + "output" : "Ў" + }, + "shiftOption" : { + "output" : "Ў" + }, + "unmodified" : { + "output" : "у" + } + }, + "33" : { + "caps" : { + "output" : "Ю" + }, + "capsOption" : { + "output" : "ю" + }, + "keyCode" : 33, + "option" : { + "output" : "[" + }, + "shift" : { + "output" : "Ю" + }, + "shiftCaps" : { + "output" : "Ю" + }, + "shiftCapsOption" : { + "output" : "{" + }, + "shiftOption" : { + "output" : "{" + }, + "unmodified" : { + "output" : "ю" + } + }, + "34" : { + "caps" : { + "output" : "І" + }, + "capsOption" : { + "output" : "і" + }, + "keyCode" : 34, + "option" : { + "output" : "ї" + }, + "shift" : { + "output" : "І" + }, + "shiftCaps" : { + "output" : "І" + }, + "shiftCapsOption" : { + "output" : "Ї" + }, + "shiftOption" : { + "output" : "Ї" + }, + "unmodified" : { + "output" : "і" + } + }, + "35" : { + "caps" : { + "output" : "П" + }, + "capsOption" : { + "output" : "п" + }, + "keyCode" : 35, + "option" : { + "output" : "»" + }, + "shift" : { + "output" : "П" + }, + "shiftCaps" : { + "output" : "П" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "output" : "п" + } + }, + "36" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 36, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "37" : { + "caps" : { + "output" : "Л" + }, + "capsOption" : { + "output" : "л" + }, + "keyCode" : 37, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "output" : "Л" + }, + "shiftCaps" : { + "output" : "Л" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "output" : "л" + } + }, + "38" : { + "caps" : { + "output" : "Й" + }, + "capsOption" : { + "output" : "й" + }, + "keyCode" : 38, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "output" : "Й" + }, + "shiftCaps" : { + "output" : "Й" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "output" : "й" + } + }, + "39" : { + "caps" : { + "output" : "ʼ" + }, + "capsOption" : { + "output" : "ʼ" + }, + "keyCode" : 39, + "option" : { + "output" : "'" + }, + "shift" : { + "output" : "₴" + }, + "shiftCaps" : { + "output" : "₴" + }, + "shiftCapsOption" : { + "output" : "\"" + }, + "shiftOption" : { + "output" : "\"" + }, + "unmodified" : { + "output" : "ʼ" + } + }, + "4" : { + "caps" : { + "output" : "Ч" + }, + "capsOption" : { + "output" : "ч" + }, + "keyCode" : 4, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "output" : "Ч" + }, + "shiftCaps" : { + "output" : "Ч" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "output" : "ч" + } + }, + "40" : { + "caps" : { + "output" : "К" + }, + "capsOption" : { + "output" : "к" + }, + "keyCode" : 40, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "output" : "К" + }, + "shiftCaps" : { + "output" : "К" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "output" : "к" + } + }, + "41" : { + "caps" : { + "output" : ";" + }, + "capsOption" : { + "output" : ";" + }, + "keyCode" : 41, + "option" : { + "output" : "…" + }, + "shift" : { + "output" : ":" + }, + "shiftCaps" : { + "output" : ":" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "output" : ";" + } + }, + "42" : { + "caps" : { + "output" : "Є" + }, + "capsOption" : { + "output" : "є" + }, + "keyCode" : 42, + "option" : { + "output" : "\\" + }, + "shift" : { + "output" : "Є" + }, + "shiftCaps" : { + "output" : "Є" + }, + "shiftCapsOption" : { + "output" : "|" + }, + "shiftOption" : { + "output" : "|" + }, + "unmodified" : { + "output" : "є" + } + }, + "43" : { + "caps" : { + "output" : "," + }, + "capsOption" : { + "output" : "," + }, + "keyCode" : 43, + "option" : { + "output" : "≤" + }, + "shift" : { + "output" : "<" + }, + "shiftCaps" : { + "output" : "<" + }, + "shiftCapsOption" : { + "output" : "„" + }, + "shiftOption" : { + "output" : "„" + }, + "unmodified" : { + "output" : "," + } + }, + "44" : { + "caps" : { + "output" : "\/" + }, + "capsOption" : { + "output" : "\/" + }, + "keyCode" : 44, + "option" : { + "output" : "÷" + }, + "shift" : { + "output" : "?" + }, + "shiftCaps" : { + "output" : "?" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "output" : "\/" + } + }, + "45" : { + "caps" : { + "output" : "Н" + }, + "capsOption" : { + "output" : "н" + }, + "keyCode" : 45, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "output" : "Н" + }, + "shiftCaps" : { + "output" : "Н" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "output" : "н" + } + }, + "46" : { + "caps" : { + "output" : "М" + }, + "capsOption" : { + "output" : "м" + }, + "keyCode" : 46, + "option" : { + "output" : "µ" + }, + "shift" : { + "output" : "М" + }, + "shiftCaps" : { + "output" : "М" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "output" : "м" + } + }, + "47" : { + "caps" : { + "output" : "." + }, + "capsOption" : { + "output" : "." + }, + "keyCode" : 47, + "option" : { + "output" : "≥" + }, + "shift" : { + "output" : ">" + }, + "shiftCaps" : { + "output" : ">" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "output" : "." + } + }, + "48" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 48, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "49" : { + "caps" : { + "output" : " " + }, + "capsOption" : { + "output" : " " + }, + "keyCode" : 49, + "option" : { + "output" : " " + }, + "shift" : { + "output" : " " + }, + "shiftCaps" : { + "output" : " " + }, + "shiftCapsOption" : { + "output" : " " + }, + "shiftOption" : { + "output" : " " + }, + "unmodified" : { + "output" : " " + } + }, + "5" : { + "caps" : { + "output" : "Г" + }, + "capsOption" : { + "output" : "г" + }, + "keyCode" : 5, + "option" : { + "output" : "ґ" + }, + "shift" : { + "output" : "Г" + }, + "shiftCaps" : { + "output" : "Г" + }, + "shiftCapsOption" : { + "output" : "Ґ" + }, + "shiftOption" : { + "output" : "Ґ" + }, + "unmodified" : { + "output" : "г" + } + }, + "50" : { + "caps" : { + "output" : "Ь" + }, + "capsOption" : { + "output" : "ь" + }, + "keyCode" : 50, + "option" : { + "output" : "ъ" + }, + "shift" : { + "output" : "Ь" + }, + "shiftCaps" : { + "output" : "Ь" + }, + "shiftCapsOption" : { + "output" : "Ъ" + }, + "shiftOption" : { + "output" : "Ъ" + }, + "unmodified" : { + "output" : "ь" + } + }, + "51" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 51, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "52" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 52, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "53" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 53, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "54" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 54, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "55" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 55, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "56" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 56, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "57" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 57, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "58" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 58, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "59" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 59, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "6" : { + "caps" : { + "output" : "З" + }, + "capsOption" : { + "output" : "з" + }, + "keyCode" : 6, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "output" : "З" + }, + "shiftCaps" : { + "output" : "З" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "output" : "з" + } + }, + "60" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 60, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "61" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 61, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "62" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 62, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "63" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 63, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "64" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 64, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "65" : { + "caps" : { + "output" : "," + }, + "capsOption" : { + "output" : "," + }, + "keyCode" : 65, + "option" : { + "output" : "," + }, + "shift" : { + "output" : "," + }, + "shiftCaps" : { + "output" : "," + }, + "shiftCapsOption" : { + "output" : "," + }, + "shiftOption" : { + "output" : "," + }, + "unmodified" : { + "output" : "," + } + }, + "66" : { + "caps" : { + "output" : "*" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 66, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "*" + }, + "shiftCaps" : { + "output" : "*" + }, + "shiftCapsOption" : { + "output" : "*" + }, + "shiftOption" : { + "output" : "*" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "67" : { + "caps" : { + "output" : "*" + }, + "capsOption" : { + "output" : "*" + }, + "keyCode" : 67, + "option" : { + "output" : "*" + }, + "shift" : { + "output" : "*" + }, + "shiftCaps" : { + "output" : "*" + }, + "shiftCapsOption" : { + "output" : "*" + }, + "shiftOption" : { + "output" : "*" + }, + "unmodified" : { + "output" : "*" + } + }, + "68" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 68, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "69" : { + "caps" : { + "output" : "+" + }, + "capsOption" : { + "output" : "+" + }, + "keyCode" : 69, + "option" : { + "output" : "+" + }, + "shift" : { + "output" : "+" + }, + "shiftCaps" : { + "output" : "+" + }, + "shiftCapsOption" : { + "output" : "+" + }, + "shiftOption" : { + "output" : "+" + }, + "unmodified" : { + "output" : "+" + } + }, + "7" : { + "caps" : { + "output" : "Х" + }, + "capsOption" : { + "output" : "х" + }, + "keyCode" : 7, + "option" : { + "output" : "≈" + }, + "shift" : { + "output" : "Х" + }, + "shiftCaps" : { + "output" : "Х" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "output" : "х" + } + }, + "70" : { + "caps" : { + "output" : "+" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 70, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "+" + }, + "shiftCaps" : { + "output" : "+" + }, + "shiftCapsOption" : { + "output" : "+" + }, + "shiftOption" : { + "output" : "+" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "71" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 71, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "72" : { + "caps" : { + "output" : "=" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 72, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "=" + }, + "shiftCaps" : { + "output" : "=" + }, + "shiftCapsOption" : { + "output" : "=" + }, + "shiftOption" : { + "output" : "=" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "73" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 73, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "74" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 74, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "75" : { + "caps" : { + "output" : "\/" + }, + "capsOption" : { + "output" : "\/" + }, + "keyCode" : 75, + "option" : { + "output" : "\/" + }, + "shift" : { + "output" : "\/" + }, + "shiftCaps" : { + "output" : "\/" + }, + "shiftCapsOption" : { + "output" : "\/" + }, + "shiftOption" : { + "output" : "\/" + }, + "unmodified" : { + "output" : "\/" + } + }, + "76" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 76, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "77" : { + "caps" : { + "output" : "\/" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 77, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "\/" + }, + "shiftCaps" : { + "output" : "\/" + }, + "shiftCapsOption" : { + "output" : "\/" + }, + "shiftOption" : { + "output" : "\/" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "78" : { + "caps" : { + "output" : "-" + }, + "capsOption" : { + "output" : "-" + }, + "keyCode" : 78, + "option" : { + "output" : "-" + }, + "shift" : { + "output" : "-" + }, + "shiftCaps" : { + "output" : "-" + }, + "shiftCapsOption" : { + "output" : "-" + }, + "shiftOption" : { + "output" : "-" + }, + "unmodified" : { + "output" : "-" + } + }, + "79" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 79, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "8" : { + "caps" : { + "output" : "Ц" + }, + "capsOption" : { + "output" : "ц" + }, + "keyCode" : 8, + "option" : { + "output" : "©" + }, + "shift" : { + "output" : "Ц" + }, + "shiftCaps" : { + "output" : "Ц" + }, + "shiftCapsOption" : { + "output" : "©" + }, + "shiftOption" : { + "output" : "©" + }, + "unmodified" : { + "output" : "ц" + } + }, + "80" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 80, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "81" : { + "caps" : { + "output" : "=" + }, + "capsOption" : { + "output" : "=" + }, + "keyCode" : 81, + "option" : { + "output" : "=" + }, + "shift" : { + "output" : "=" + }, + "shiftCaps" : { + "output" : "=" + }, + "shiftCapsOption" : { + "output" : "=" + }, + "shiftOption" : { + "output" : "=" + }, + "unmodified" : { + "output" : "=" + } + }, + "82" : { + "caps" : { + "output" : "0" + }, + "capsOption" : { + "output" : "0" + }, + "keyCode" : 82, + "option" : { + "output" : "0" + }, + "shift" : { + "output" : "0" + }, + "shiftCaps" : { + "output" : "0" + }, + "shiftCapsOption" : { + "output" : "0" + }, + "shiftOption" : { + "output" : "0" + }, + "unmodified" : { + "output" : "0" + } + }, + "83" : { + "caps" : { + "output" : "1" + }, + "capsOption" : { + "output" : "1" + }, + "keyCode" : 83, + "option" : { + "output" : "1" + }, + "shift" : { + "output" : "1" + }, + "shiftCaps" : { + "output" : "1" + }, + "shiftCapsOption" : { + "output" : "1" + }, + "shiftOption" : { + "output" : "1" + }, + "unmodified" : { + "output" : "1" + } + }, + "84" : { + "caps" : { + "output" : "2" + }, + "capsOption" : { + "output" : "2" + }, + "keyCode" : 84, + "option" : { + "output" : "2" + }, + "shift" : { + "output" : "2" + }, + "shiftCaps" : { + "output" : "2" + }, + "shiftCapsOption" : { + "output" : "2" + }, + "shiftOption" : { + "output" : "2" + }, + "unmodified" : { + "output" : "2" + } + }, + "85" : { + "caps" : { + "output" : "3" + }, + "capsOption" : { + "output" : "3" + }, + "keyCode" : 85, + "option" : { + "output" : "3" + }, + "shift" : { + "output" : "3" + }, + "shiftCaps" : { + "output" : "3" + }, + "shiftCapsOption" : { + "output" : "3" + }, + "shiftOption" : { + "output" : "3" + }, + "unmodified" : { + "output" : "3" + } + }, + "86" : { + "caps" : { + "output" : "4" + }, + "capsOption" : { + "output" : "4" + }, + "keyCode" : 86, + "option" : { + "output" : "4" + }, + "shift" : { + "output" : "4" + }, + "shiftCaps" : { + "output" : "4" + }, + "shiftCapsOption" : { + "output" : "4" + }, + "shiftOption" : { + "output" : "4" + }, + "unmodified" : { + "output" : "4" + } + }, + "87" : { + "caps" : { + "output" : "5" + }, + "capsOption" : { + "output" : "5" + }, + "keyCode" : 87, + "option" : { + "output" : "5" + }, + "shift" : { + "output" : "5" + }, + "shiftCaps" : { + "output" : "5" + }, + "shiftCapsOption" : { + "output" : "5" + }, + "shiftOption" : { + "output" : "5" + }, + "unmodified" : { + "output" : "5" + } + }, + "88" : { + "caps" : { + "output" : "6" + }, + "capsOption" : { + "output" : "6" + }, + "keyCode" : 88, + "option" : { + "output" : "6" + }, + "shift" : { + "output" : "6" + }, + "shiftCaps" : { + "output" : "6" + }, + "shiftCapsOption" : { + "output" : "6" + }, + "shiftOption" : { + "output" : "6" + }, + "unmodified" : { + "output" : "6" + } + }, + "89" : { + "caps" : { + "output" : "7" + }, + "capsOption" : { + "output" : "7" + }, + "keyCode" : 89, + "option" : { + "output" : "7" + }, + "shift" : { + "output" : "7" + }, + "shiftCaps" : { + "output" : "7" + }, + "shiftCapsOption" : { + "output" : "7" + }, + "shiftOption" : { + "output" : "7" + }, + "unmodified" : { + "output" : "7" + } + }, + "9" : { + "caps" : { + "output" : "В" + }, + "capsOption" : { + "output" : "в" + }, + "keyCode" : 9, + "option" : { + "output" : "√" + }, + "shift" : { + "output" : "В" + }, + "shiftCaps" : { + "output" : "В" + }, + "shiftCapsOption" : { + "output" : "†" + }, + "shiftOption" : { + "output" : "†" + }, + "unmodified" : { + "output" : "в" + } + }, + "90" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 90, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "91" : { + "caps" : { + "output" : "8" + }, + "capsOption" : { + "output" : "8" + }, + "keyCode" : 91, + "option" : { + "output" : "8" + }, + "shift" : { + "output" : "8" + }, + "shiftCaps" : { + "output" : "8" + }, + "shiftCapsOption" : { + "output" : "8" + }, + "shiftOption" : { + "output" : "8" + }, + "unmodified" : { + "output" : "8" + } + }, + "92" : { + "caps" : { + "output" : "9" + }, + "capsOption" : { + "output" : "9" + }, + "keyCode" : 92, + "option" : { + "output" : "9" + }, + "shift" : { + "output" : "9" + }, + "shiftCaps" : { + "output" : "9" + }, + "shiftCapsOption" : { + "output" : "9" + }, + "shiftOption" : { + "output" : "9" + }, + "unmodified" : { + "output" : "9" + } + }, + "93" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 93, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "94" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 94, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "95" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 95, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "96" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 96, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "97" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 97, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "98" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 98, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "99" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 99, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + } + }, + "keyboardType" : 91, + "osBuild" : "24G830", + "osVersion" : "15.7.9", + "schemaVersion" : 1, + "sourceID" : "com.apple.keylayout.Ukrainian-QWERTY" +} \ No newline at end of file diff --git a/Benchmarks/layout-tables/v1/com.apple.keylayout.Ukrainian@37.json b/Benchmarks/layout-tables/v1/com.apple.keylayout.Ukrainian@37.json new file mode 100644 index 0000000..ee3e25a --- /dev/null +++ b/Benchmarks/layout-tables/v1/com.apple.keylayout.Ukrainian@37.json @@ -0,0 +1,3465 @@ +{ + "keyOutputs" : { + "0" : { + "caps" : { + "output" : "Ф" + }, + "capsOption" : { + "output" : "ƒ" + }, + "keyCode" : 0, + "option" : { + "output" : "ƒ" + }, + "shift" : { + "output" : "Ф" + }, + "shiftCaps" : { + "output" : "Ф" + }, + "shiftCapsOption" : { + "output" : "₴" + }, + "shiftOption" : { + "output" : "₴" + }, + "unmodified" : { + "output" : "ф" + } + }, + "1" : { + "caps" : { + "output" : "И" + }, + "capsOption" : { + "output" : "Ы" + }, + "keyCode" : 1, + "option" : { + "output" : "ы" + }, + "shift" : { + "output" : "И" + }, + "shiftCaps" : { + "output" : "И" + }, + "shiftCapsOption" : { + "output" : "Ы" + }, + "shiftOption" : { + "output" : "Ы" + }, + "unmodified" : { + "output" : "и" + } + }, + "10" : { + "caps" : { + "output" : ">" + }, + "capsOption" : { + "output" : "§" + }, + "keyCode" : 10, + "option" : { + "output" : "§" + }, + "shift" : { + "output" : "<" + }, + "shiftCaps" : { + "output" : "<" + }, + "shiftCapsOption" : { + "output" : "±" + }, + "shiftOption" : { + "output" : "±" + }, + "unmodified" : { + "output" : ">" + } + }, + "100" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 100, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "101" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 101, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "102" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 102, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "103" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 103, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "104" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 104, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "105" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 105, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "106" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 106, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "107" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 107, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "108" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 108, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "109" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 109, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "11" : { + "caps" : { + "output" : "І" + }, + "capsOption" : { + "output" : "И" + }, + "keyCode" : 11, + "option" : { + "output" : "и" + }, + "shift" : { + "output" : "І" + }, + "shiftCaps" : { + "output" : "І" + }, + "shiftCapsOption" : { + "output" : "И" + }, + "shiftOption" : { + "output" : "И" + }, + "unmodified" : { + "output" : "і" + } + }, + "110" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 110, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "111" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 111, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "112" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 112, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "113" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 113, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "114" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 114, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "115" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 115, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "116" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 116, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "117" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 117, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "118" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 118, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "119" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 119, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "12" : { + "caps" : { + "output" : "Й" + }, + "capsOption" : { + "output" : "Ј" + }, + "keyCode" : 12, + "option" : { + "output" : "ј" + }, + "shift" : { + "output" : "Й" + }, + "shiftCaps" : { + "output" : "Й" + }, + "shiftCapsOption" : { + "output" : "Ј" + }, + "shiftOption" : { + "output" : "Ј" + }, + "unmodified" : { + "output" : "й" + } + }, + "120" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 120, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "121" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 121, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "122" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 122, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "123" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 123, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "124" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 124, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "125" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 125, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "126" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 126, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "127" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 127, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "13" : { + "caps" : { + "output" : "Ц" + }, + "capsOption" : { + "output" : "Џ" + }, + "keyCode" : 13, + "option" : { + "output" : "џ" + }, + "shift" : { + "output" : "Ц" + }, + "shiftCaps" : { + "output" : "Ц" + }, + "shiftCapsOption" : { + "output" : "Џ" + }, + "shiftOption" : { + "output" : "Џ" + }, + "unmodified" : { + "output" : "ц" + } + }, + "14" : { + "caps" : { + "output" : "У" + }, + "capsOption" : { + "output" : "Ќ" + }, + "keyCode" : 14, + "option" : { + "output" : "ќ" + }, + "shift" : { + "output" : "У" + }, + "shiftCaps" : { + "output" : "У" + }, + "shiftCapsOption" : { + "output" : "Ќ" + }, + "shiftOption" : { + "output" : "Ќ" + }, + "unmodified" : { + "output" : "у" + } + }, + "15" : { + "caps" : { + "output" : "К" + }, + "capsOption" : { + "output" : "®" + }, + "keyCode" : 15, + "option" : { + "output" : "®" + }, + "shift" : { + "output" : "К" + }, + "shiftCaps" : { + "output" : "К" + }, + "shiftCapsOption" : { + "output" : "®" + }, + "shiftOption" : { + "output" : "®" + }, + "unmodified" : { + "output" : "к" + } + }, + "16" : { + "caps" : { + "output" : "Н" + }, + "capsOption" : { + "output" : "Њ" + }, + "keyCode" : 16, + "option" : { + "output" : "њ" + }, + "shift" : { + "output" : "Н" + }, + "shiftCaps" : { + "output" : "Н" + }, + "shiftCapsOption" : { + "output" : "Њ" + }, + "shiftOption" : { + "output" : "Њ" + }, + "unmodified" : { + "output" : "н" + } + }, + "17" : { + "caps" : { + "output" : "Е" + }, + "capsOption" : { + "output" : "†" + }, + "keyCode" : 17, + "option" : { + "output" : "†" + }, + "shift" : { + "output" : "Е" + }, + "shiftCaps" : { + "output" : "Е" + }, + "shiftCapsOption" : { + "output" : "†" + }, + "shiftOption" : { + "output" : "†" + }, + "unmodified" : { + "output" : "е" + } + }, + "18" : { + "caps" : { + "output" : "1" + }, + "capsOption" : { + "output" : "!" + }, + "keyCode" : 18, + "option" : { + "output" : "!" + }, + "shift" : { + "output" : "!" + }, + "shiftCaps" : { + "output" : "!" + }, + "shiftCapsOption" : { + "output" : "|" + }, + "shiftOption" : { + "output" : "|" + }, + "unmodified" : { + "output" : "1" + } + }, + "19" : { + "caps" : { + "output" : "2" + }, + "capsOption" : { + "output" : "@" + }, + "keyCode" : 19, + "option" : { + "output" : "@" + }, + "shift" : { + "output" : "\"" + }, + "shiftCaps" : { + "output" : "\"" + }, + "shiftCapsOption" : { + "output" : "\"" + }, + "shiftOption" : { + "output" : "\"" + }, + "unmodified" : { + "output" : "2" + } + }, + "2" : { + "caps" : { + "output" : "В" + }, + "capsOption" : { + "output" : "Ћ" + }, + "keyCode" : 2, + "option" : { + "output" : "ћ" + }, + "shift" : { + "output" : "В" + }, + "shiftCaps" : { + "output" : "В" + }, + "shiftCapsOption" : { + "output" : "Ћ" + }, + "shiftOption" : { + "output" : "Ћ" + }, + "unmodified" : { + "output" : "в" + } + }, + "20" : { + "caps" : { + "output" : "3" + }, + "capsOption" : { + "output" : "#" + }, + "keyCode" : 20, + "option" : { + "output" : "#" + }, + "shift" : { + "output" : "№" + }, + "shiftCaps" : { + "output" : "№" + }, + "shiftCapsOption" : { + "output" : "£" + }, + "shiftOption" : { + "output" : "£" + }, + "unmodified" : { + "output" : "3" + } + }, + "21" : { + "caps" : { + "output" : "4" + }, + "capsOption" : { + "output" : "$" + }, + "keyCode" : 21, + "option" : { + "output" : "$" + }, + "shift" : { + "output" : "%" + }, + "shiftCaps" : { + "output" : "%" + }, + "shiftCapsOption" : { + "output" : "€" + }, + "shiftOption" : { + "output" : "€" + }, + "unmodified" : { + "output" : "4" + } + }, + "22" : { + "caps" : { + "output" : "6" + }, + "capsOption" : { + "output" : "^" + }, + "keyCode" : 22, + "option" : { + "output" : "^" + }, + "shift" : { + "output" : "," + }, + "shiftCaps" : { + "output" : "," + }, + "shiftCapsOption" : { + "output" : "¬" + }, + "shiftOption" : { + "output" : "¬" + }, + "unmodified" : { + "output" : "6" + } + }, + "23" : { + "caps" : { + "output" : "5" + }, + "capsOption" : { + "output" : "%" + }, + "keyCode" : 23, + "option" : { + "output" : "%" + }, + "shift" : { + "output" : ":" + }, + "shiftCaps" : { + "output" : ":" + }, + "shiftCapsOption" : { + "output" : "∞" + }, + "shiftOption" : { + "output" : "∞" + }, + "unmodified" : { + "output" : "5" + } + }, + "24" : { + "caps" : { + "output" : "=" + }, + "capsOption" : { + "output" : "»" + }, + "keyCode" : 24, + "option" : { + "output" : "»" + }, + "shift" : { + "output" : "+" + }, + "shiftCaps" : { + "output" : "+" + }, + "shiftCapsOption" : { + "output" : "«" + }, + "shiftOption" : { + "output" : "«" + }, + "unmodified" : { + "output" : "=" + } + }, + "25" : { + "caps" : { + "output" : "9" + }, + "capsOption" : { + "output" : "(" + }, + "keyCode" : 25, + "option" : { + "output" : "(" + }, + "shift" : { + "output" : "(" + }, + "shiftCaps" : { + "output" : "(" + }, + "shiftCapsOption" : { + "output" : "'" + }, + "shiftOption" : { + "output" : "'" + }, + "unmodified" : { + "output" : "9" + } + }, + "26" : { + "caps" : { + "output" : "7" + }, + "capsOption" : { + "output" : "&" + }, + "keyCode" : 26, + "option" : { + "output" : "&" + }, + "shift" : { + "output" : "." + }, + "shiftCaps" : { + "output" : "." + }, + "shiftCapsOption" : { + "output" : "¶" + }, + "shiftOption" : { + "output" : "¶" + }, + "unmodified" : { + "output" : "7" + } + }, + "27" : { + "caps" : { + "output" : "-" + }, + "capsOption" : { + "output" : "–" + }, + "keyCode" : 27, + "option" : { + "output" : "–" + }, + "shift" : { + "output" : "_" + }, + "shiftCaps" : { + "output" : "_" + }, + "shiftCapsOption" : { + "output" : "—" + }, + "shiftOption" : { + "output" : "—" + }, + "unmodified" : { + "output" : "-" + } + }, + "28" : { + "caps" : { + "output" : "8" + }, + "capsOption" : { + "output" : "*" + }, + "keyCode" : 28, + "option" : { + "output" : "*" + }, + "shift" : { + "output" : ";" + }, + "shiftCaps" : { + "output" : ";" + }, + "shiftCapsOption" : { + "output" : "√" + }, + "shiftOption" : { + "output" : "√" + }, + "unmodified" : { + "output" : "8" + } + }, + "29" : { + "caps" : { + "output" : "0" + }, + "capsOption" : { + "output" : ")" + }, + "keyCode" : 29, + "option" : { + "output" : ")" + }, + "shift" : { + "output" : ")" + }, + "shiftCaps" : { + "output" : ")" + }, + "shiftCapsOption" : { + "output" : "`" + }, + "shiftOption" : { + "output" : "`" + }, + "unmodified" : { + "output" : "0" + } + }, + "3" : { + "caps" : { + "output" : "А" + }, + "capsOption" : { + "output" : "÷" + }, + "keyCode" : 3, + "option" : { + "output" : "÷" + }, + "shift" : { + "output" : "А" + }, + "shiftCaps" : { + "output" : "А" + }, + "shiftCapsOption" : { + "output" : "÷" + }, + "shiftOption" : { + "output" : "÷" + }, + "unmodified" : { + "output" : "а" + } + }, + "30" : { + "caps" : { + "output" : "Ї" + }, + "capsOption" : { + "output" : "Ъ" + }, + "keyCode" : 30, + "option" : { + "output" : "ъ" + }, + "shift" : { + "output" : "Ї" + }, + "shiftCaps" : { + "output" : "Ї" + }, + "shiftCapsOption" : { + "output" : "Ъ" + }, + "shiftOption" : { + "output" : "Ъ" + }, + "unmodified" : { + "output" : "ї" + } + }, + "31" : { + "caps" : { + "output" : "Щ" + }, + "capsOption" : { + "output" : "Ў" + }, + "keyCode" : 31, + "option" : { + "output" : "ў" + }, + "shift" : { + "output" : "Щ" + }, + "shiftCaps" : { + "output" : "Щ" + }, + "shiftCapsOption" : { + "output" : "Ў" + }, + "shiftOption" : { + "output" : "Ў" + }, + "unmodified" : { + "output" : "щ" + } + }, + "32" : { + "caps" : { + "output" : "Г" + }, + "capsOption" : { + "output" : "Ѓ" + }, + "keyCode" : 32, + "option" : { + "output" : "ѓ" + }, + "shift" : { + "output" : "Г" + }, + "shiftCaps" : { + "output" : "Г" + }, + "shiftCapsOption" : { + "output" : "Ѓ" + }, + "shiftOption" : { + "output" : "Ѓ" + }, + "unmodified" : { + "output" : "г" + } + }, + "33" : { + "caps" : { + "output" : "Х" + }, + "capsOption" : { + "output" : "“" + }, + "keyCode" : 33, + "option" : { + "output" : "“" + }, + "shift" : { + "output" : "Х" + }, + "shiftCaps" : { + "output" : "Х" + }, + "shiftCapsOption" : { + "output" : "”" + }, + "shiftOption" : { + "output" : "”" + }, + "unmodified" : { + "output" : "х" + } + }, + "34" : { + "caps" : { + "output" : "Ш" + }, + "capsOption" : { + "output" : "Ѕ" + }, + "keyCode" : 34, + "option" : { + "output" : "ѕ" + }, + "shift" : { + "output" : "Ш" + }, + "shiftCaps" : { + "output" : "Ш" + }, + "shiftCapsOption" : { + "output" : "Ѕ" + }, + "shiftOption" : { + "output" : "Ѕ" + }, + "unmodified" : { + "output" : "ш" + } + }, + "35" : { + "caps" : { + "output" : "З" + }, + "capsOption" : { + "output" : "‘" + }, + "keyCode" : 35, + "option" : { + "output" : "‘" + }, + "shift" : { + "output" : "З" + }, + "shiftCaps" : { + "output" : "З" + }, + "shiftCapsOption" : { + "output" : "’" + }, + "shiftOption" : { + "output" : "’" + }, + "unmodified" : { + "output" : "з" + } + }, + "36" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 36, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "37" : { + "caps" : { + "output" : "Д" + }, + "capsOption" : { + "output" : "∆" + }, + "keyCode" : 37, + "option" : { + "output" : "∆" + }, + "shift" : { + "output" : "Д" + }, + "shiftCaps" : { + "output" : "Д" + }, + "shiftCapsOption" : { + "output" : "∆" + }, + "shiftOption" : { + "output" : "∆" + }, + "unmodified" : { + "output" : "д" + } + }, + "38" : { + "caps" : { + "output" : "О" + }, + "capsOption" : { + "output" : "°" + }, + "keyCode" : 38, + "option" : { + "output" : "°" + }, + "shift" : { + "output" : "О" + }, + "shiftCaps" : { + "output" : "О" + }, + "shiftCapsOption" : { + "output" : "•" + }, + "shiftOption" : { + "output" : "•" + }, + "unmodified" : { + "output" : "о" + } + }, + "39" : { + "caps" : { + "output" : "Є" + }, + "capsOption" : { + "output" : "Э" + }, + "keyCode" : 39, + "option" : { + "output" : "э" + }, + "shift" : { + "output" : "Є" + }, + "shiftCaps" : { + "output" : "Є" + }, + "shiftCapsOption" : { + "output" : "Э" + }, + "shiftOption" : { + "output" : "Э" + }, + "unmodified" : { + "output" : "є" + } + }, + "4" : { + "caps" : { + "output" : "Р" + }, + "capsOption" : { + "output" : "}" + }, + "keyCode" : 4, + "option" : { + "output" : "}" + }, + "shift" : { + "output" : "Р" + }, + "shiftCaps" : { + "output" : "Р" + }, + "shiftCapsOption" : { + "output" : "{" + }, + "shiftOption" : { + "output" : "{" + }, + "unmodified" : { + "output" : "р" + } + }, + "40" : { + "caps" : { + "output" : "Л" + }, + "capsOption" : { + "output" : "Љ" + }, + "keyCode" : 40, + "option" : { + "output" : "љ" + }, + "shift" : { + "output" : "Л" + }, + "shiftCaps" : { + "output" : "Л" + }, + "shiftCapsOption" : { + "output" : "Љ" + }, + "shiftOption" : { + "output" : "Љ" + }, + "unmodified" : { + "output" : "л" + } + }, + "41" : { + "caps" : { + "output" : "Ж" + }, + "capsOption" : { + "output" : "…" + }, + "keyCode" : 41, + "option" : { + "output" : "…" + }, + "shift" : { + "output" : "Ж" + }, + "shiftCaps" : { + "output" : "Ж" + }, + "shiftCapsOption" : { + "output" : "…" + }, + "shiftOption" : { + "output" : "…" + }, + "unmodified" : { + "output" : "ж" + } + }, + "42" : { + "caps" : { + "output" : "Ґ" + }, + "capsOption" : { + "output" : "Ё" + }, + "keyCode" : 42, + "option" : { + "output" : "ё" + }, + "shift" : { + "output" : "Ґ" + }, + "shiftCaps" : { + "output" : "Ґ" + }, + "shiftCapsOption" : { + "output" : "Ё" + }, + "shiftOption" : { + "output" : "Ё" + }, + "unmodified" : { + "output" : "ґ" + } + }, + "43" : { + "caps" : { + "output" : "Б" + }, + "capsOption" : { + "output" : "≤" + }, + "keyCode" : 43, + "option" : { + "output" : "≤" + }, + "shift" : { + "output" : "Б" + }, + "shiftCaps" : { + "output" : "Б" + }, + "shiftCapsOption" : { + "output" : "<" + }, + "shiftOption" : { + "output" : "<" + }, + "unmodified" : { + "output" : "б" + } + }, + "44" : { + "caps" : { + "output" : "\/" + }, + "capsOption" : { + "output" : "“" + }, + "keyCode" : 44, + "option" : { + "output" : "“" + }, + "shift" : { + "output" : "?" + }, + "shiftCaps" : { + "output" : "?" + }, + "shiftCapsOption" : { + "output" : "„" + }, + "shiftOption" : { + "output" : "„" + }, + "unmodified" : { + "output" : "\/" + } + }, + "45" : { + "caps" : { + "output" : "Т" + }, + "capsOption" : { + "output" : "™" + }, + "keyCode" : 45, + "option" : { + "output" : "™" + }, + "shift" : { + "output" : "Т" + }, + "shiftCaps" : { + "output" : "Т" + }, + "shiftCapsOption" : { + "output" : "™" + }, + "shiftOption" : { + "output" : "™" + }, + "unmodified" : { + "output" : "т" + } + }, + "46" : { + "caps" : { + "output" : "Ь" + }, + "capsOption" : { + "output" : "~" + }, + "keyCode" : 46, + "option" : { + "output" : "~" + }, + "shift" : { + "output" : "Ь" + }, + "shiftCaps" : { + "output" : "Ь" + }, + "shiftCapsOption" : { + "output" : "~" + }, + "shiftOption" : { + "output" : "~" + }, + "unmodified" : { + "output" : "ь" + } + }, + "47" : { + "caps" : { + "output" : "Ю" + }, + "capsOption" : { + "output" : "≥" + }, + "keyCode" : 47, + "option" : { + "output" : "≥" + }, + "shift" : { + "output" : "Ю" + }, + "shiftCaps" : { + "output" : "Ю" + }, + "shiftCapsOption" : { + "output" : ">" + }, + "shiftOption" : { + "output" : ">" + }, + "unmodified" : { + "output" : "ю" + } + }, + "48" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 48, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "49" : { + "caps" : { + "output" : " " + }, + "capsOption" : { + "output" : " " + }, + "keyCode" : 49, + "option" : { + "output" : " " + }, + "shift" : { + "output" : " " + }, + "shiftCaps" : { + "output" : " " + }, + "shiftCapsOption" : { + "output" : " " + }, + "shiftOption" : { + "output" : " " + }, + "unmodified" : { + "output" : " " + } + }, + "5" : { + "caps" : { + "output" : "П" + }, + "capsOption" : { + "output" : "©" + }, + "keyCode" : 5, + "option" : { + "output" : "©" + }, + "shift" : { + "output" : "П" + }, + "shiftCaps" : { + "output" : "П" + }, + "shiftCapsOption" : { + "output" : "©" + }, + "shiftOption" : { + "output" : "©" + }, + "unmodified" : { + "output" : "п" + } + }, + "50" : { + "caps" : { + "output" : "'" + }, + "capsOption" : { + "output" : "]" + }, + "keyCode" : 50, + "option" : { + "output" : "]" + }, + "shift" : { + "output" : "~" + }, + "shiftCaps" : { + "output" : "~" + }, + "shiftCapsOption" : { + "output" : "[" + }, + "shiftOption" : { + "output" : "[" + }, + "unmodified" : { + "output" : "'" + } + }, + "51" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 51, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "52" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 52, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "53" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 53, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "54" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 54, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "55" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 55, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "56" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 56, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "57" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 57, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "58" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 58, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "59" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 59, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "6" : { + "caps" : { + "output" : "Я" + }, + "capsOption" : { + "output" : "Ђ" + }, + "keyCode" : 6, + "option" : { + "output" : "ђ" + }, + "shift" : { + "output" : "Я" + }, + "shiftCaps" : { + "output" : "Я" + }, + "shiftCapsOption" : { + "output" : "Ђ" + }, + "shiftOption" : { + "output" : "Ђ" + }, + "unmodified" : { + "output" : "я" + } + }, + "60" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 60, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "61" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 61, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "62" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 62, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "63" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 63, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "64" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 64, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "65" : { + "caps" : { + "output" : "," + }, + "capsOption" : { + "output" : "," + }, + "keyCode" : 65, + "option" : { + "output" : "," + }, + "shift" : { + "output" : "." + }, + "shiftCaps" : { + "output" : "." + }, + "shiftCapsOption" : { + "output" : "." + }, + "shiftOption" : { + "output" : "." + }, + "unmodified" : { + "output" : "," + } + }, + "66" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 66, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "*" + }, + "shiftCaps" : { + "output" : "*" + }, + "shiftCapsOption" : { + "output" : "*" + }, + "shiftOption" : { + "output" : "*" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "67" : { + "caps" : { + "output" : "*" + }, + "capsOption" : { + "output" : "*" + }, + "keyCode" : 67, + "option" : { + "output" : "*" + }, + "shift" : { + "output" : "*" + }, + "shiftCaps" : { + "output" : "*" + }, + "shiftCapsOption" : { + "output" : "*" + }, + "shiftOption" : { + "output" : "*" + }, + "unmodified" : { + "output" : "*" + } + }, + "68" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 68, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "69" : { + "caps" : { + "output" : "+" + }, + "capsOption" : { + "output" : "+" + }, + "keyCode" : 69, + "option" : { + "output" : "+" + }, + "shift" : { + "output" : "+" + }, + "shiftCaps" : { + "output" : "+" + }, + "shiftCapsOption" : { + "output" : "+" + }, + "shiftOption" : { + "output" : "+" + }, + "unmodified" : { + "output" : "+" + } + }, + "7" : { + "caps" : { + "output" : "Ч" + }, + "capsOption" : { + "output" : "≈" + }, + "keyCode" : 7, + "option" : { + "output" : "≈" + }, + "shift" : { + "output" : "Ч" + }, + "shiftCaps" : { + "output" : "Ч" + }, + "shiftCapsOption" : { + "output" : "≈" + }, + "shiftOption" : { + "output" : "≈" + }, + "unmodified" : { + "output" : "ч" + } + }, + "70" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 70, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "+" + }, + "shiftCaps" : { + "output" : "+" + }, + "shiftCapsOption" : { + "output" : "+" + }, + "shiftOption" : { + "output" : "+" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "71" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 71, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "72" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 72, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "=" + }, + "shiftCaps" : { + "output" : "=" + }, + "shiftCapsOption" : { + "output" : "=" + }, + "shiftOption" : { + "output" : "=" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "73" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 73, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "74" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 74, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "75" : { + "caps" : { + "output" : "\/" + }, + "capsOption" : { + "output" : "\/" + }, + "keyCode" : 75, + "option" : { + "output" : "\/" + }, + "shift" : { + "output" : "\/" + }, + "shiftCaps" : { + "output" : "\/" + }, + "shiftCapsOption" : { + "output" : "\/" + }, + "shiftOption" : { + "output" : "\/" + }, + "unmodified" : { + "output" : "\/" + } + }, + "76" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 76, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "77" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 77, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "\/" + }, + "shiftCaps" : { + "output" : "\/" + }, + "shiftCapsOption" : { + "output" : "\/" + }, + "shiftOption" : { + "output" : "\/" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "78" : { + "caps" : { + "output" : "-" + }, + "capsOption" : { + "output" : "-" + }, + "keyCode" : 78, + "option" : { + "output" : "-" + }, + "shift" : { + "output" : "-" + }, + "shiftCaps" : { + "output" : "-" + }, + "shiftCapsOption" : { + "output" : "-" + }, + "shiftOption" : { + "output" : "-" + }, + "unmodified" : { + "output" : "-" + } + }, + "79" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 79, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "8" : { + "caps" : { + "output" : "С" + }, + "capsOption" : { + "output" : "≠" + }, + "keyCode" : 8, + "option" : { + "output" : "≠" + }, + "shift" : { + "output" : "С" + }, + "shiftCaps" : { + "output" : "С" + }, + "shiftCapsOption" : { + "output" : "≠" + }, + "shiftOption" : { + "output" : "≠" + }, + "unmodified" : { + "output" : "с" + } + }, + "80" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 80, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "81" : { + "caps" : { + "output" : "=" + }, + "capsOption" : { + "output" : "=" + }, + "keyCode" : 81, + "option" : { + "output" : "=" + }, + "shift" : { + "output" : "=" + }, + "shiftCaps" : { + "output" : "=" + }, + "shiftCapsOption" : { + "output" : "=" + }, + "shiftOption" : { + "output" : "=" + }, + "unmodified" : { + "output" : "=" + } + }, + "82" : { + "caps" : { + "output" : "0" + }, + "capsOption" : { + "output" : "0" + }, + "keyCode" : 82, + "option" : { + "output" : "0" + }, + "shift" : { + "output" : "0" + }, + "shiftCaps" : { + "output" : "0" + }, + "shiftCapsOption" : { + "output" : "0" + }, + "shiftOption" : { + "output" : "0" + }, + "unmodified" : { + "output" : "0" + } + }, + "83" : { + "caps" : { + "output" : "1" + }, + "capsOption" : { + "output" : "1" + }, + "keyCode" : 83, + "option" : { + "output" : "1" + }, + "shift" : { + "output" : "1" + }, + "shiftCaps" : { + "output" : "1" + }, + "shiftCapsOption" : { + "output" : "1" + }, + "shiftOption" : { + "output" : "1" + }, + "unmodified" : { + "output" : "1" + } + }, + "84" : { + "caps" : { + "output" : "2" + }, + "capsOption" : { + "output" : "2" + }, + "keyCode" : 84, + "option" : { + "output" : "2" + }, + "shift" : { + "output" : "2" + }, + "shiftCaps" : { + "output" : "2" + }, + "shiftCapsOption" : { + "output" : "2" + }, + "shiftOption" : { + "output" : "2" + }, + "unmodified" : { + "output" : "2" + } + }, + "85" : { + "caps" : { + "output" : "3" + }, + "capsOption" : { + "output" : "3" + }, + "keyCode" : 85, + "option" : { + "output" : "3" + }, + "shift" : { + "output" : "3" + }, + "shiftCaps" : { + "output" : "3" + }, + "shiftCapsOption" : { + "output" : "3" + }, + "shiftOption" : { + "output" : "3" + }, + "unmodified" : { + "output" : "3" + } + }, + "86" : { + "caps" : { + "output" : "4" + }, + "capsOption" : { + "output" : "4" + }, + "keyCode" : 86, + "option" : { + "output" : "4" + }, + "shift" : { + "output" : "4" + }, + "shiftCaps" : { + "output" : "4" + }, + "shiftCapsOption" : { + "output" : "4" + }, + "shiftOption" : { + "output" : "4" + }, + "unmodified" : { + "output" : "4" + } + }, + "87" : { + "caps" : { + "output" : "5" + }, + "capsOption" : { + "output" : "5" + }, + "keyCode" : 87, + "option" : { + "output" : "5" + }, + "shift" : { + "output" : "5" + }, + "shiftCaps" : { + "output" : "5" + }, + "shiftCapsOption" : { + "output" : "5" + }, + "shiftOption" : { + "output" : "5" + }, + "unmodified" : { + "output" : "5" + } + }, + "88" : { + "caps" : { + "output" : "6" + }, + "capsOption" : { + "output" : "6" + }, + "keyCode" : 88, + "option" : { + "output" : "6" + }, + "shift" : { + "output" : "6" + }, + "shiftCaps" : { + "output" : "6" + }, + "shiftCapsOption" : { + "output" : "6" + }, + "shiftOption" : { + "output" : "6" + }, + "unmodified" : { + "output" : "6" + } + }, + "89" : { + "caps" : { + "output" : "7" + }, + "capsOption" : { + "output" : "7" + }, + "keyCode" : 89, + "option" : { + "output" : "7" + }, + "shift" : { + "output" : "7" + }, + "shiftCaps" : { + "output" : "7" + }, + "shiftCapsOption" : { + "output" : "7" + }, + "shiftOption" : { + "output" : "7" + }, + "unmodified" : { + "output" : "7" + } + }, + "9" : { + "caps" : { + "output" : "М" + }, + "capsOption" : { + "output" : "µ" + }, + "keyCode" : 9, + "option" : { + "output" : "µ" + }, + "shift" : { + "output" : "М" + }, + "shiftCaps" : { + "output" : "М" + }, + "shiftCapsOption" : { + "output" : "µ" + }, + "shiftOption" : { + "output" : "µ" + }, + "unmodified" : { + "output" : "м" + } + }, + "90" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 90, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "91" : { + "caps" : { + "output" : "8" + }, + "capsOption" : { + "output" : "8" + }, + "keyCode" : 91, + "option" : { + "output" : "8" + }, + "shift" : { + "output" : "8" + }, + "shiftCaps" : { + "output" : "8" + }, + "shiftCapsOption" : { + "output" : "8" + }, + "shiftOption" : { + "output" : "8" + }, + "unmodified" : { + "output" : "8" + } + }, + "92" : { + "caps" : { + "output" : "9" + }, + "capsOption" : { + "output" : "9" + }, + "keyCode" : 92, + "option" : { + "output" : "9" + }, + "shift" : { + "output" : "9" + }, + "shiftCaps" : { + "output" : "9" + }, + "shiftCapsOption" : { + "output" : "9" + }, + "shiftOption" : { + "output" : "9" + }, + "unmodified" : { + "output" : "9" + } + }, + "93" : { + "caps" : { + "output" : ">" + }, + "capsOption" : { + "output" : "§" + }, + "keyCode" : 93, + "option" : { + "output" : "§" + }, + "shift" : { + "output" : "<" + }, + "shiftCaps" : { + "output" : "<" + }, + "shiftCapsOption" : { + "output" : "±" + }, + "shiftOption" : { + "output" : "±" + }, + "unmodified" : { + "output" : ">" + } + }, + "94" : { + "caps" : { + "output" : "'" + }, + "capsOption" : { + "output" : "]" + }, + "keyCode" : 94, + "option" : { + "output" : "]" + }, + "shift" : { + "output" : "~" + }, + "shiftCaps" : { + "output" : "~" + }, + "shiftCapsOption" : { + "output" : "[" + }, + "shiftOption" : { + "output" : "[" + }, + "unmodified" : { + "output" : "'" + } + }, + "95" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 95, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "96" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 96, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "97" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 97, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "98" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 98, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "99" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 99, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + } + }, + "keyboardType" : 37, + "osBuild" : "24G830", + "osVersion" : "15.7.9", + "schemaVersion" : 1, + "sourceID" : "com.apple.keylayout.Ukrainian" +} \ No newline at end of file diff --git a/Benchmarks/layout-tables/v1/com.apple.keylayout.Ukrainian@91.json b/Benchmarks/layout-tables/v1/com.apple.keylayout.Ukrainian@91.json new file mode 100644 index 0000000..a074a42 --- /dev/null +++ b/Benchmarks/layout-tables/v1/com.apple.keylayout.Ukrainian@91.json @@ -0,0 +1,3465 @@ +{ + "keyOutputs" : { + "0" : { + "caps" : { + "output" : "Ф" + }, + "capsOption" : { + "output" : "ƒ" + }, + "keyCode" : 0, + "option" : { + "output" : "ƒ" + }, + "shift" : { + "output" : "Ф" + }, + "shiftCaps" : { + "output" : "Ф" + }, + "shiftCapsOption" : { + "output" : "₴" + }, + "shiftOption" : { + "output" : "₴" + }, + "unmodified" : { + "output" : "ф" + } + }, + "1" : { + "caps" : { + "output" : "И" + }, + "capsOption" : { + "output" : "Ы" + }, + "keyCode" : 1, + "option" : { + "output" : "ы" + }, + "shift" : { + "output" : "И" + }, + "shiftCaps" : { + "output" : "И" + }, + "shiftCapsOption" : { + "output" : "Ы" + }, + "shiftOption" : { + "output" : "Ы" + }, + "unmodified" : { + "output" : "и" + } + }, + "10" : { + "caps" : { + "output" : ">" + }, + "capsOption" : { + "output" : "§" + }, + "keyCode" : 10, + "option" : { + "output" : "§" + }, + "shift" : { + "output" : "<" + }, + "shiftCaps" : { + "output" : "<" + }, + "shiftCapsOption" : { + "output" : "±" + }, + "shiftOption" : { + "output" : "±" + }, + "unmodified" : { + "output" : ">" + } + }, + "100" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 100, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "101" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 101, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "102" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 102, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "103" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 103, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "104" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 104, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "105" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 105, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "106" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 106, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "107" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 107, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "108" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 108, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "109" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 109, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "11" : { + "caps" : { + "output" : "І" + }, + "capsOption" : { + "output" : "И" + }, + "keyCode" : 11, + "option" : { + "output" : "и" + }, + "shift" : { + "output" : "І" + }, + "shiftCaps" : { + "output" : "І" + }, + "shiftCapsOption" : { + "output" : "И" + }, + "shiftOption" : { + "output" : "И" + }, + "unmodified" : { + "output" : "і" + } + }, + "110" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 110, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "111" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 111, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "112" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 112, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "113" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 113, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "114" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 114, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "115" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 115, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "116" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 116, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "117" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 117, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "118" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 118, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "119" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 119, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "12" : { + "caps" : { + "output" : "Й" + }, + "capsOption" : { + "output" : "Ј" + }, + "keyCode" : 12, + "option" : { + "output" : "ј" + }, + "shift" : { + "output" : "Й" + }, + "shiftCaps" : { + "output" : "Й" + }, + "shiftCapsOption" : { + "output" : "Ј" + }, + "shiftOption" : { + "output" : "Ј" + }, + "unmodified" : { + "output" : "й" + } + }, + "120" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 120, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "121" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 121, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "122" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 122, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "123" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 123, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "124" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 124, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "125" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 125, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "126" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 126, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "127" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 127, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "13" : { + "caps" : { + "output" : "Ц" + }, + "capsOption" : { + "output" : "Џ" + }, + "keyCode" : 13, + "option" : { + "output" : "џ" + }, + "shift" : { + "output" : "Ц" + }, + "shiftCaps" : { + "output" : "Ц" + }, + "shiftCapsOption" : { + "output" : "Џ" + }, + "shiftOption" : { + "output" : "Џ" + }, + "unmodified" : { + "output" : "ц" + } + }, + "14" : { + "caps" : { + "output" : "У" + }, + "capsOption" : { + "output" : "Ќ" + }, + "keyCode" : 14, + "option" : { + "output" : "ќ" + }, + "shift" : { + "output" : "У" + }, + "shiftCaps" : { + "output" : "У" + }, + "shiftCapsOption" : { + "output" : "Ќ" + }, + "shiftOption" : { + "output" : "Ќ" + }, + "unmodified" : { + "output" : "у" + } + }, + "15" : { + "caps" : { + "output" : "К" + }, + "capsOption" : { + "output" : "®" + }, + "keyCode" : 15, + "option" : { + "output" : "®" + }, + "shift" : { + "output" : "К" + }, + "shiftCaps" : { + "output" : "К" + }, + "shiftCapsOption" : { + "output" : "®" + }, + "shiftOption" : { + "output" : "®" + }, + "unmodified" : { + "output" : "к" + } + }, + "16" : { + "caps" : { + "output" : "Н" + }, + "capsOption" : { + "output" : "Њ" + }, + "keyCode" : 16, + "option" : { + "output" : "њ" + }, + "shift" : { + "output" : "Н" + }, + "shiftCaps" : { + "output" : "Н" + }, + "shiftCapsOption" : { + "output" : "Њ" + }, + "shiftOption" : { + "output" : "Њ" + }, + "unmodified" : { + "output" : "н" + } + }, + "17" : { + "caps" : { + "output" : "Е" + }, + "capsOption" : { + "output" : "†" + }, + "keyCode" : 17, + "option" : { + "output" : "†" + }, + "shift" : { + "output" : "Е" + }, + "shiftCaps" : { + "output" : "Е" + }, + "shiftCapsOption" : { + "output" : "†" + }, + "shiftOption" : { + "output" : "†" + }, + "unmodified" : { + "output" : "е" + } + }, + "18" : { + "caps" : { + "output" : "1" + }, + "capsOption" : { + "output" : "!" + }, + "keyCode" : 18, + "option" : { + "output" : "!" + }, + "shift" : { + "output" : "!" + }, + "shiftCaps" : { + "output" : "!" + }, + "shiftCapsOption" : { + "output" : "|" + }, + "shiftOption" : { + "output" : "|" + }, + "unmodified" : { + "output" : "1" + } + }, + "19" : { + "caps" : { + "output" : "2" + }, + "capsOption" : { + "output" : "@" + }, + "keyCode" : 19, + "option" : { + "output" : "@" + }, + "shift" : { + "output" : "\"" + }, + "shiftCaps" : { + "output" : "\"" + }, + "shiftCapsOption" : { + "output" : "\"" + }, + "shiftOption" : { + "output" : "\"" + }, + "unmodified" : { + "output" : "2" + } + }, + "2" : { + "caps" : { + "output" : "В" + }, + "capsOption" : { + "output" : "Ћ" + }, + "keyCode" : 2, + "option" : { + "output" : "ћ" + }, + "shift" : { + "output" : "В" + }, + "shiftCaps" : { + "output" : "В" + }, + "shiftCapsOption" : { + "output" : "Ћ" + }, + "shiftOption" : { + "output" : "Ћ" + }, + "unmodified" : { + "output" : "в" + } + }, + "20" : { + "caps" : { + "output" : "3" + }, + "capsOption" : { + "output" : "#" + }, + "keyCode" : 20, + "option" : { + "output" : "#" + }, + "shift" : { + "output" : "№" + }, + "shiftCaps" : { + "output" : "№" + }, + "shiftCapsOption" : { + "output" : "£" + }, + "shiftOption" : { + "output" : "£" + }, + "unmodified" : { + "output" : "3" + } + }, + "21" : { + "caps" : { + "output" : "4" + }, + "capsOption" : { + "output" : "$" + }, + "keyCode" : 21, + "option" : { + "output" : "$" + }, + "shift" : { + "output" : "%" + }, + "shiftCaps" : { + "output" : "%" + }, + "shiftCapsOption" : { + "output" : "€" + }, + "shiftOption" : { + "output" : "€" + }, + "unmodified" : { + "output" : "4" + } + }, + "22" : { + "caps" : { + "output" : "6" + }, + "capsOption" : { + "output" : "^" + }, + "keyCode" : 22, + "option" : { + "output" : "^" + }, + "shift" : { + "output" : "," + }, + "shiftCaps" : { + "output" : "," + }, + "shiftCapsOption" : { + "output" : "¬" + }, + "shiftOption" : { + "output" : "¬" + }, + "unmodified" : { + "output" : "6" + } + }, + "23" : { + "caps" : { + "output" : "5" + }, + "capsOption" : { + "output" : "%" + }, + "keyCode" : 23, + "option" : { + "output" : "%" + }, + "shift" : { + "output" : ":" + }, + "shiftCaps" : { + "output" : ":" + }, + "shiftCapsOption" : { + "output" : "∞" + }, + "shiftOption" : { + "output" : "∞" + }, + "unmodified" : { + "output" : "5" + } + }, + "24" : { + "caps" : { + "output" : "=" + }, + "capsOption" : { + "output" : "»" + }, + "keyCode" : 24, + "option" : { + "output" : "»" + }, + "shift" : { + "output" : "+" + }, + "shiftCaps" : { + "output" : "+" + }, + "shiftCapsOption" : { + "output" : "«" + }, + "shiftOption" : { + "output" : "«" + }, + "unmodified" : { + "output" : "=" + } + }, + "25" : { + "caps" : { + "output" : "9" + }, + "capsOption" : { + "output" : "(" + }, + "keyCode" : 25, + "option" : { + "output" : "(" + }, + "shift" : { + "output" : "(" + }, + "shiftCaps" : { + "output" : "(" + }, + "shiftCapsOption" : { + "output" : "'" + }, + "shiftOption" : { + "output" : "'" + }, + "unmodified" : { + "output" : "9" + } + }, + "26" : { + "caps" : { + "output" : "7" + }, + "capsOption" : { + "output" : "&" + }, + "keyCode" : 26, + "option" : { + "output" : "&" + }, + "shift" : { + "output" : "." + }, + "shiftCaps" : { + "output" : "." + }, + "shiftCapsOption" : { + "output" : "¶" + }, + "shiftOption" : { + "output" : "¶" + }, + "unmodified" : { + "output" : "7" + } + }, + "27" : { + "caps" : { + "output" : "-" + }, + "capsOption" : { + "output" : "–" + }, + "keyCode" : 27, + "option" : { + "output" : "–" + }, + "shift" : { + "output" : "_" + }, + "shiftCaps" : { + "output" : "_" + }, + "shiftCapsOption" : { + "output" : "—" + }, + "shiftOption" : { + "output" : "—" + }, + "unmodified" : { + "output" : "-" + } + }, + "28" : { + "caps" : { + "output" : "8" + }, + "capsOption" : { + "output" : "*" + }, + "keyCode" : 28, + "option" : { + "output" : "*" + }, + "shift" : { + "output" : ";" + }, + "shiftCaps" : { + "output" : ";" + }, + "shiftCapsOption" : { + "output" : "√" + }, + "shiftOption" : { + "output" : "√" + }, + "unmodified" : { + "output" : "8" + } + }, + "29" : { + "caps" : { + "output" : "0" + }, + "capsOption" : { + "output" : ")" + }, + "keyCode" : 29, + "option" : { + "output" : ")" + }, + "shift" : { + "output" : ")" + }, + "shiftCaps" : { + "output" : ")" + }, + "shiftCapsOption" : { + "output" : "`" + }, + "shiftOption" : { + "output" : "`" + }, + "unmodified" : { + "output" : "0" + } + }, + "3" : { + "caps" : { + "output" : "А" + }, + "capsOption" : { + "output" : "÷" + }, + "keyCode" : 3, + "option" : { + "output" : "÷" + }, + "shift" : { + "output" : "А" + }, + "shiftCaps" : { + "output" : "А" + }, + "shiftCapsOption" : { + "output" : "÷" + }, + "shiftOption" : { + "output" : "÷" + }, + "unmodified" : { + "output" : "а" + } + }, + "30" : { + "caps" : { + "output" : "Ї" + }, + "capsOption" : { + "output" : "Ъ" + }, + "keyCode" : 30, + "option" : { + "output" : "ъ" + }, + "shift" : { + "output" : "Ї" + }, + "shiftCaps" : { + "output" : "Ї" + }, + "shiftCapsOption" : { + "output" : "Ъ" + }, + "shiftOption" : { + "output" : "Ъ" + }, + "unmodified" : { + "output" : "ї" + } + }, + "31" : { + "caps" : { + "output" : "Щ" + }, + "capsOption" : { + "output" : "Ў" + }, + "keyCode" : 31, + "option" : { + "output" : "ў" + }, + "shift" : { + "output" : "Щ" + }, + "shiftCaps" : { + "output" : "Щ" + }, + "shiftCapsOption" : { + "output" : "Ў" + }, + "shiftOption" : { + "output" : "Ў" + }, + "unmodified" : { + "output" : "щ" + } + }, + "32" : { + "caps" : { + "output" : "Г" + }, + "capsOption" : { + "output" : "Ѓ" + }, + "keyCode" : 32, + "option" : { + "output" : "ѓ" + }, + "shift" : { + "output" : "Г" + }, + "shiftCaps" : { + "output" : "Г" + }, + "shiftCapsOption" : { + "output" : "Ѓ" + }, + "shiftOption" : { + "output" : "Ѓ" + }, + "unmodified" : { + "output" : "г" + } + }, + "33" : { + "caps" : { + "output" : "Х" + }, + "capsOption" : { + "output" : "“" + }, + "keyCode" : 33, + "option" : { + "output" : "“" + }, + "shift" : { + "output" : "Х" + }, + "shiftCaps" : { + "output" : "Х" + }, + "shiftCapsOption" : { + "output" : "”" + }, + "shiftOption" : { + "output" : "”" + }, + "unmodified" : { + "output" : "х" + } + }, + "34" : { + "caps" : { + "output" : "Ш" + }, + "capsOption" : { + "output" : "Ѕ" + }, + "keyCode" : 34, + "option" : { + "output" : "ѕ" + }, + "shift" : { + "output" : "Ш" + }, + "shiftCaps" : { + "output" : "Ш" + }, + "shiftCapsOption" : { + "output" : "Ѕ" + }, + "shiftOption" : { + "output" : "Ѕ" + }, + "unmodified" : { + "output" : "ш" + } + }, + "35" : { + "caps" : { + "output" : "З" + }, + "capsOption" : { + "output" : "‘" + }, + "keyCode" : 35, + "option" : { + "output" : "‘" + }, + "shift" : { + "output" : "З" + }, + "shiftCaps" : { + "output" : "З" + }, + "shiftCapsOption" : { + "output" : "’" + }, + "shiftOption" : { + "output" : "’" + }, + "unmodified" : { + "output" : "з" + } + }, + "36" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 36, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "37" : { + "caps" : { + "output" : "Д" + }, + "capsOption" : { + "output" : "∆" + }, + "keyCode" : 37, + "option" : { + "output" : "∆" + }, + "shift" : { + "output" : "Д" + }, + "shiftCaps" : { + "output" : "Д" + }, + "shiftCapsOption" : { + "output" : "∆" + }, + "shiftOption" : { + "output" : "∆" + }, + "unmodified" : { + "output" : "д" + } + }, + "38" : { + "caps" : { + "output" : "О" + }, + "capsOption" : { + "output" : "°" + }, + "keyCode" : 38, + "option" : { + "output" : "°" + }, + "shift" : { + "output" : "О" + }, + "shiftCaps" : { + "output" : "О" + }, + "shiftCapsOption" : { + "output" : "•" + }, + "shiftOption" : { + "output" : "•" + }, + "unmodified" : { + "output" : "о" + } + }, + "39" : { + "caps" : { + "output" : "Є" + }, + "capsOption" : { + "output" : "Э" + }, + "keyCode" : 39, + "option" : { + "output" : "э" + }, + "shift" : { + "output" : "Є" + }, + "shiftCaps" : { + "output" : "Є" + }, + "shiftCapsOption" : { + "output" : "Э" + }, + "shiftOption" : { + "output" : "Э" + }, + "unmodified" : { + "output" : "є" + } + }, + "4" : { + "caps" : { + "output" : "Р" + }, + "capsOption" : { + "output" : "}" + }, + "keyCode" : 4, + "option" : { + "output" : "}" + }, + "shift" : { + "output" : "Р" + }, + "shiftCaps" : { + "output" : "Р" + }, + "shiftCapsOption" : { + "output" : "{" + }, + "shiftOption" : { + "output" : "{" + }, + "unmodified" : { + "output" : "р" + } + }, + "40" : { + "caps" : { + "output" : "Л" + }, + "capsOption" : { + "output" : "Љ" + }, + "keyCode" : 40, + "option" : { + "output" : "љ" + }, + "shift" : { + "output" : "Л" + }, + "shiftCaps" : { + "output" : "Л" + }, + "shiftCapsOption" : { + "output" : "Љ" + }, + "shiftOption" : { + "output" : "Љ" + }, + "unmodified" : { + "output" : "л" + } + }, + "41" : { + "caps" : { + "output" : "Ж" + }, + "capsOption" : { + "output" : "…" + }, + "keyCode" : 41, + "option" : { + "output" : "…" + }, + "shift" : { + "output" : "Ж" + }, + "shiftCaps" : { + "output" : "Ж" + }, + "shiftCapsOption" : { + "output" : "…" + }, + "shiftOption" : { + "output" : "…" + }, + "unmodified" : { + "output" : "ж" + } + }, + "42" : { + "caps" : { + "output" : "Ґ" + }, + "capsOption" : { + "output" : "Ё" + }, + "keyCode" : 42, + "option" : { + "output" : "ё" + }, + "shift" : { + "output" : "Ґ" + }, + "shiftCaps" : { + "output" : "Ґ" + }, + "shiftCapsOption" : { + "output" : "Ё" + }, + "shiftOption" : { + "output" : "Ё" + }, + "unmodified" : { + "output" : "ґ" + } + }, + "43" : { + "caps" : { + "output" : "Б" + }, + "capsOption" : { + "output" : "≤" + }, + "keyCode" : 43, + "option" : { + "output" : "≤" + }, + "shift" : { + "output" : "Б" + }, + "shiftCaps" : { + "output" : "Б" + }, + "shiftCapsOption" : { + "output" : "<" + }, + "shiftOption" : { + "output" : "<" + }, + "unmodified" : { + "output" : "б" + } + }, + "44" : { + "caps" : { + "output" : "\/" + }, + "capsOption" : { + "output" : "“" + }, + "keyCode" : 44, + "option" : { + "output" : "“" + }, + "shift" : { + "output" : "?" + }, + "shiftCaps" : { + "output" : "?" + }, + "shiftCapsOption" : { + "output" : "„" + }, + "shiftOption" : { + "output" : "„" + }, + "unmodified" : { + "output" : "\/" + } + }, + "45" : { + "caps" : { + "output" : "Т" + }, + "capsOption" : { + "output" : "™" + }, + "keyCode" : 45, + "option" : { + "output" : "™" + }, + "shift" : { + "output" : "Т" + }, + "shiftCaps" : { + "output" : "Т" + }, + "shiftCapsOption" : { + "output" : "™" + }, + "shiftOption" : { + "output" : "™" + }, + "unmodified" : { + "output" : "т" + } + }, + "46" : { + "caps" : { + "output" : "Ь" + }, + "capsOption" : { + "output" : "~" + }, + "keyCode" : 46, + "option" : { + "output" : "~" + }, + "shift" : { + "output" : "Ь" + }, + "shiftCaps" : { + "output" : "Ь" + }, + "shiftCapsOption" : { + "output" : "~" + }, + "shiftOption" : { + "output" : "~" + }, + "unmodified" : { + "output" : "ь" + } + }, + "47" : { + "caps" : { + "output" : "Ю" + }, + "capsOption" : { + "output" : "≥" + }, + "keyCode" : 47, + "option" : { + "output" : "≥" + }, + "shift" : { + "output" : "Ю" + }, + "shiftCaps" : { + "output" : "Ю" + }, + "shiftCapsOption" : { + "output" : ">" + }, + "shiftOption" : { + "output" : ">" + }, + "unmodified" : { + "output" : "ю" + } + }, + "48" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 48, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "49" : { + "caps" : { + "output" : " " + }, + "capsOption" : { + "output" : " " + }, + "keyCode" : 49, + "option" : { + "output" : " " + }, + "shift" : { + "output" : " " + }, + "shiftCaps" : { + "output" : " " + }, + "shiftCapsOption" : { + "output" : " " + }, + "shiftOption" : { + "output" : " " + }, + "unmodified" : { + "output" : " " + } + }, + "5" : { + "caps" : { + "output" : "П" + }, + "capsOption" : { + "output" : "©" + }, + "keyCode" : 5, + "option" : { + "output" : "©" + }, + "shift" : { + "output" : "П" + }, + "shiftCaps" : { + "output" : "П" + }, + "shiftCapsOption" : { + "output" : "©" + }, + "shiftOption" : { + "output" : "©" + }, + "unmodified" : { + "output" : "п" + } + }, + "50" : { + "caps" : { + "output" : "'" + }, + "capsOption" : { + "output" : "]" + }, + "keyCode" : 50, + "option" : { + "output" : "]" + }, + "shift" : { + "output" : "~" + }, + "shiftCaps" : { + "output" : "~" + }, + "shiftCapsOption" : { + "output" : "[" + }, + "shiftOption" : { + "output" : "[" + }, + "unmodified" : { + "output" : "'" + } + }, + "51" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 51, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "52" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 52, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "53" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 53, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "54" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 54, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "55" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 55, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "56" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 56, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "57" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 57, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "58" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 58, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "59" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 59, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "6" : { + "caps" : { + "output" : "Я" + }, + "capsOption" : { + "output" : "Ђ" + }, + "keyCode" : 6, + "option" : { + "output" : "ђ" + }, + "shift" : { + "output" : "Я" + }, + "shiftCaps" : { + "output" : "Я" + }, + "shiftCapsOption" : { + "output" : "Ђ" + }, + "shiftOption" : { + "output" : "Ђ" + }, + "unmodified" : { + "output" : "я" + } + }, + "60" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 60, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "61" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 61, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "62" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 62, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "63" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 63, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "64" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 64, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "65" : { + "caps" : { + "output" : "," + }, + "capsOption" : { + "output" : "," + }, + "keyCode" : 65, + "option" : { + "output" : "," + }, + "shift" : { + "output" : "." + }, + "shiftCaps" : { + "output" : "." + }, + "shiftCapsOption" : { + "output" : "." + }, + "shiftOption" : { + "output" : "." + }, + "unmodified" : { + "output" : "," + } + }, + "66" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 66, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "*" + }, + "shiftCaps" : { + "output" : "*" + }, + "shiftCapsOption" : { + "output" : "*" + }, + "shiftOption" : { + "output" : "*" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "67" : { + "caps" : { + "output" : "*" + }, + "capsOption" : { + "output" : "*" + }, + "keyCode" : 67, + "option" : { + "output" : "*" + }, + "shift" : { + "output" : "*" + }, + "shiftCaps" : { + "output" : "*" + }, + "shiftCapsOption" : { + "output" : "*" + }, + "shiftOption" : { + "output" : "*" + }, + "unmodified" : { + "output" : "*" + } + }, + "68" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 68, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "69" : { + "caps" : { + "output" : "+" + }, + "capsOption" : { + "output" : "+" + }, + "keyCode" : 69, + "option" : { + "output" : "+" + }, + "shift" : { + "output" : "+" + }, + "shiftCaps" : { + "output" : "+" + }, + "shiftCapsOption" : { + "output" : "+" + }, + "shiftOption" : { + "output" : "+" + }, + "unmodified" : { + "output" : "+" + } + }, + "7" : { + "caps" : { + "output" : "Ч" + }, + "capsOption" : { + "output" : "≈" + }, + "keyCode" : 7, + "option" : { + "output" : "≈" + }, + "shift" : { + "output" : "Ч" + }, + "shiftCaps" : { + "output" : "Ч" + }, + "shiftCapsOption" : { + "output" : "≈" + }, + "shiftOption" : { + "output" : "≈" + }, + "unmodified" : { + "output" : "ч" + } + }, + "70" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 70, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "+" + }, + "shiftCaps" : { + "output" : "+" + }, + "shiftCapsOption" : { + "output" : "+" + }, + "shiftOption" : { + "output" : "+" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "71" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 71, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "72" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 72, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "=" + }, + "shiftCaps" : { + "output" : "=" + }, + "shiftCapsOption" : { + "output" : "=" + }, + "shiftOption" : { + "output" : "=" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "73" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 73, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "74" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 74, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "75" : { + "caps" : { + "output" : "\/" + }, + "capsOption" : { + "output" : "\/" + }, + "keyCode" : 75, + "option" : { + "output" : "\/" + }, + "shift" : { + "output" : "\/" + }, + "shiftCaps" : { + "output" : "\/" + }, + "shiftCapsOption" : { + "output" : "\/" + }, + "shiftOption" : { + "output" : "\/" + }, + "unmodified" : { + "output" : "\/" + } + }, + "76" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 76, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "77" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 77, + "option" : { + "rejection" : "control" + }, + "shift" : { + "output" : "\/" + }, + "shiftCaps" : { + "output" : "\/" + }, + "shiftCapsOption" : { + "output" : "\/" + }, + "shiftOption" : { + "output" : "\/" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "78" : { + "caps" : { + "output" : "-" + }, + "capsOption" : { + "output" : "-" + }, + "keyCode" : 78, + "option" : { + "output" : "-" + }, + "shift" : { + "output" : "-" + }, + "shiftCaps" : { + "output" : "-" + }, + "shiftCapsOption" : { + "output" : "-" + }, + "shiftOption" : { + "output" : "-" + }, + "unmodified" : { + "output" : "-" + } + }, + "79" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 79, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "8" : { + "caps" : { + "output" : "С" + }, + "capsOption" : { + "output" : "≠" + }, + "keyCode" : 8, + "option" : { + "output" : "≠" + }, + "shift" : { + "output" : "С" + }, + "shiftCaps" : { + "output" : "С" + }, + "shiftCapsOption" : { + "output" : "≠" + }, + "shiftOption" : { + "output" : "≠" + }, + "unmodified" : { + "output" : "с" + } + }, + "80" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 80, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "81" : { + "caps" : { + "output" : "=" + }, + "capsOption" : { + "output" : "=" + }, + "keyCode" : 81, + "option" : { + "output" : "=" + }, + "shift" : { + "output" : "=" + }, + "shiftCaps" : { + "output" : "=" + }, + "shiftCapsOption" : { + "output" : "=" + }, + "shiftOption" : { + "output" : "=" + }, + "unmodified" : { + "output" : "=" + } + }, + "82" : { + "caps" : { + "output" : "0" + }, + "capsOption" : { + "output" : "0" + }, + "keyCode" : 82, + "option" : { + "output" : "0" + }, + "shift" : { + "output" : "0" + }, + "shiftCaps" : { + "output" : "0" + }, + "shiftCapsOption" : { + "output" : "0" + }, + "shiftOption" : { + "output" : "0" + }, + "unmodified" : { + "output" : "0" + } + }, + "83" : { + "caps" : { + "output" : "1" + }, + "capsOption" : { + "output" : "1" + }, + "keyCode" : 83, + "option" : { + "output" : "1" + }, + "shift" : { + "output" : "1" + }, + "shiftCaps" : { + "output" : "1" + }, + "shiftCapsOption" : { + "output" : "1" + }, + "shiftOption" : { + "output" : "1" + }, + "unmodified" : { + "output" : "1" + } + }, + "84" : { + "caps" : { + "output" : "2" + }, + "capsOption" : { + "output" : "2" + }, + "keyCode" : 84, + "option" : { + "output" : "2" + }, + "shift" : { + "output" : "2" + }, + "shiftCaps" : { + "output" : "2" + }, + "shiftCapsOption" : { + "output" : "2" + }, + "shiftOption" : { + "output" : "2" + }, + "unmodified" : { + "output" : "2" + } + }, + "85" : { + "caps" : { + "output" : "3" + }, + "capsOption" : { + "output" : "3" + }, + "keyCode" : 85, + "option" : { + "output" : "3" + }, + "shift" : { + "output" : "3" + }, + "shiftCaps" : { + "output" : "3" + }, + "shiftCapsOption" : { + "output" : "3" + }, + "shiftOption" : { + "output" : "3" + }, + "unmodified" : { + "output" : "3" + } + }, + "86" : { + "caps" : { + "output" : "4" + }, + "capsOption" : { + "output" : "4" + }, + "keyCode" : 86, + "option" : { + "output" : "4" + }, + "shift" : { + "output" : "4" + }, + "shiftCaps" : { + "output" : "4" + }, + "shiftCapsOption" : { + "output" : "4" + }, + "shiftOption" : { + "output" : "4" + }, + "unmodified" : { + "output" : "4" + } + }, + "87" : { + "caps" : { + "output" : "5" + }, + "capsOption" : { + "output" : "5" + }, + "keyCode" : 87, + "option" : { + "output" : "5" + }, + "shift" : { + "output" : "5" + }, + "shiftCaps" : { + "output" : "5" + }, + "shiftCapsOption" : { + "output" : "5" + }, + "shiftOption" : { + "output" : "5" + }, + "unmodified" : { + "output" : "5" + } + }, + "88" : { + "caps" : { + "output" : "6" + }, + "capsOption" : { + "output" : "6" + }, + "keyCode" : 88, + "option" : { + "output" : "6" + }, + "shift" : { + "output" : "6" + }, + "shiftCaps" : { + "output" : "6" + }, + "shiftCapsOption" : { + "output" : "6" + }, + "shiftOption" : { + "output" : "6" + }, + "unmodified" : { + "output" : "6" + } + }, + "89" : { + "caps" : { + "output" : "7" + }, + "capsOption" : { + "output" : "7" + }, + "keyCode" : 89, + "option" : { + "output" : "7" + }, + "shift" : { + "output" : "7" + }, + "shiftCaps" : { + "output" : "7" + }, + "shiftCapsOption" : { + "output" : "7" + }, + "shiftOption" : { + "output" : "7" + }, + "unmodified" : { + "output" : "7" + } + }, + "9" : { + "caps" : { + "output" : "М" + }, + "capsOption" : { + "output" : "µ" + }, + "keyCode" : 9, + "option" : { + "output" : "µ" + }, + "shift" : { + "output" : "М" + }, + "shiftCaps" : { + "output" : "М" + }, + "shiftCapsOption" : { + "output" : "µ" + }, + "shiftOption" : { + "output" : "µ" + }, + "unmodified" : { + "output" : "м" + } + }, + "90" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 90, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "91" : { + "caps" : { + "output" : "8" + }, + "capsOption" : { + "output" : "8" + }, + "keyCode" : 91, + "option" : { + "output" : "8" + }, + "shift" : { + "output" : "8" + }, + "shiftCaps" : { + "output" : "8" + }, + "shiftCapsOption" : { + "output" : "8" + }, + "shiftOption" : { + "output" : "8" + }, + "unmodified" : { + "output" : "8" + } + }, + "92" : { + "caps" : { + "output" : "9" + }, + "capsOption" : { + "output" : "9" + }, + "keyCode" : 92, + "option" : { + "output" : "9" + }, + "shift" : { + "output" : "9" + }, + "shiftCaps" : { + "output" : "9" + }, + "shiftCapsOption" : { + "output" : "9" + }, + "shiftOption" : { + "output" : "9" + }, + "unmodified" : { + "output" : "9" + } + }, + "93" : { + "caps" : { + "output" : ">" + }, + "capsOption" : { + "output" : "§" + }, + "keyCode" : 93, + "option" : { + "output" : "§" + }, + "shift" : { + "output" : "<" + }, + "shiftCaps" : { + "output" : "<" + }, + "shiftCapsOption" : { + "output" : "±" + }, + "shiftOption" : { + "output" : "±" + }, + "unmodified" : { + "output" : ">" + } + }, + "94" : { + "caps" : { + "output" : "'" + }, + "capsOption" : { + "output" : "]" + }, + "keyCode" : 94, + "option" : { + "output" : "]" + }, + "shift" : { + "output" : "~" + }, + "shiftCaps" : { + "output" : "~" + }, + "shiftCapsOption" : { + "output" : "[" + }, + "shiftOption" : { + "output" : "[" + }, + "unmodified" : { + "output" : "'" + } + }, + "95" : { + "caps" : { + "rejection" : "empty" + }, + "capsOption" : { + "rejection" : "empty" + }, + "keyCode" : 95, + "option" : { + "rejection" : "empty" + }, + "shift" : { + "rejection" : "empty" + }, + "shiftCaps" : { + "rejection" : "empty" + }, + "shiftCapsOption" : { + "rejection" : "empty" + }, + "shiftOption" : { + "rejection" : "empty" + }, + "unmodified" : { + "rejection" : "empty" + } + }, + "96" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 96, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "97" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 97, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "98" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 98, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + }, + "99" : { + "caps" : { + "rejection" : "control" + }, + "capsOption" : { + "rejection" : "control" + }, + "keyCode" : 99, + "option" : { + "rejection" : "control" + }, + "shift" : { + "rejection" : "control" + }, + "shiftCaps" : { + "rejection" : "control" + }, + "shiftCapsOption" : { + "rejection" : "control" + }, + "shiftOption" : { + "rejection" : "control" + }, + "unmodified" : { + "rejection" : "control" + } + } + }, + "keyboardType" : 91, + "osBuild" : "24G830", + "osVersion" : "15.7.9", + "schemaVersion" : 1, + "sourceID" : "com.apple.keylayout.Ukrainian" +} \ No newline at end of file diff --git a/Package.swift b/Package.swift index a10af66..3d05af7 100644 --- a/Package.swift +++ b/Package.swift @@ -49,5 +49,10 @@ let package = Package( dependencies: ["Core", "Utils"], path: "Sources/InputPipelineTestRunner" ), + .executableTarget( + name: "LayoutBenchmark", + dependencies: ["Core", "Dictionary", "Utils"], + path: "Sources/LayoutBenchmark" + ), ] ) diff --git a/README.md b/README.md index 212373b..5725159 100644 --- a/README.md +++ b/README.md @@ -23,13 +23,15 @@ A macOS menu bar utility that automatically corrects keyboard layout mistakes. T ## Features -- **Automatic correction** — detects wrong-layout words on space/enter and corrects them instantly. +- **Automatic correction** — detects wrong-layout words on space/enter; when a later word confirms the mistake, it can also correct preceding words in the same typing run. +- **Personal exceptions** — protect names, abbreviations, and other words from automatic correction in Settings. Explicit hotkey and layout-switch corrections still work. - **Hotkey mode** — correct only when you press Ctrl+Shift+Space (configurable). - **Selection correction** — select text and press the hotkey to convert it. - **Permissions indicator** — shows the status of required macOS permissions in the app menu. - **Undo** — `Cmd+Z` within 5 seconds reverts the last correction. - **Revert hotkey** — `CapsLock` reverts the last correction (configurable). -- **Three layouts** — English (US/ABC/British/Dvorak/Colemak), Ukrainian, and Russian. +- **Three languages** — English (US/ABC/British/Dvorak/Colemak), Ukrainian, and Russian, with native macOS language metadata used to discover installed layouts. +- **Safer layout pairing** — English converts to your most recently used Cyrillic layout; Ukrainian and Russian text are never converted into each other. - **Smart filtering** — skips password fields, URLs, emails, camelCase, mixed scripts. - **App blacklist** — disabled in terminals, IDEs, and code editors by default (toggle per app). - **Launch at Login** — optional auto-start. diff --git a/Resources/Info.plist b/Resources/Info.plist index a3e0463..38b7d4c 100644 --- a/Resources/Info.plist +++ b/Resources/Info.plist @@ -19,9 +19,9 @@ CFBundlePackageType APPL CFBundleShortVersionString - 0.0.11 + 0.0.12 CFBundleVersion - 11 + 12 LSMinimumSystemVersion 13.0 LSUIElement diff --git a/Sources/Core/CapturedInput.swift b/Sources/Core/CapturedInput.swift index 183edd3..40fd29a 100644 --- a/Sources/Core/CapturedInput.swift +++ b/Sources/Core/CapturedInput.swift @@ -209,6 +209,15 @@ public final class CaptureStateStore { } } + /// Increment correction epoch to invalidate in-flight plans on configuration changes (exceptions, mappings). + @discardableResult + public func invalidateCorrectionConfiguration() -> UInt64 { + state.withLock { value in + value.correctionEpoch &+= 1 + return value.correctionEpoch + } + } + public func hotkeyConfiguration() -> HotkeyConfiguration { state.withLock { $0.hotkeys } } diff --git a/Sources/Core/CorrectionTrigger.swift b/Sources/Core/CorrectionTrigger.swift new file mode 100644 index 0000000..0312e81 --- /dev/null +++ b/Sources/Core/CorrectionTrigger.swift @@ -0,0 +1,7 @@ +import Foundation + +public enum CorrectionTrigger: String, Codable, Equatable, Sendable { + case automaticBoundary + case hotkey + case layoutSwitch +} diff --git a/Sources/Core/DetectionRequestAssembly.swift b/Sources/Core/DetectionRequestAssembly.swift new file mode 100644 index 0000000..cc478d9 --- /dev/null +++ b/Sources/Core/DetectionRequestAssembly.swift @@ -0,0 +1,109 @@ +import Foundation +import Utils + +public struct DetectionConfiguration: Equatable, Sendable { + public var allowedLayouts: Set + public var preferredCyrillicLayout: Layout? + public var activeSourceSupportedLayouts: Set + public var ukrainianFromVariant: UkrainianKeyboardVariant + public var ukrainianToVariant: UkrainianKeyboardVariant + public var personalExceptions: PersonalExceptionsPolicy + + public init( + allowedLayouts: Set = Set(Layout.allCases), + preferredCyrillicLayout: Layout? = nil, + activeSourceSupportedLayouts: Set = [.english], + ukrainianFromVariant: UkrainianKeyboardVariant = .standard, + ukrainianToVariant: UkrainianKeyboardVariant = .standard, + personalExceptions: PersonalExceptionsPolicy = .empty + ) { + self.allowedLayouts = allowedLayouts + self.preferredCyrillicLayout = preferredCyrillicLayout + self.activeSourceSupportedLayouts = activeSourceSupportedLayouts + self.ukrainianFromVariant = ukrainianFromVariant + self.ukrainianToVariant = ukrainianToVariant + self.personalExceptions = personalExceptions + } +} + +public func assemblePrecedingRun(from run: [RunToken], currentSequence: UInt64) -> [RunToken] { + return run.last?.id == currentSequence ? Array(run.dropLast()) : [] +} + +public func assembleDetectionRequest( + trigger: CorrectionTrigger, + word: String, + boundary: String, + sequence: UInt64, + editGeneration: UInt64, + correctionEpoch: UInt64, + context: InputContextSnapshot, + run: [RunToken] +) -> DetectionRequest { + DetectionRequest( + trigger: trigger, + word: word, + boundary: boundary, + sequence: sequence, + editGeneration: editGeneration, + correctionEpoch: correctionEpoch, + context: context, + precedingRun: assemblePrecedingRun(from: run, currentSequence: sequence) + ) +} + +public func evaluateDetection( + detector: LayoutDetector, + request: DetectionRequest, + configuration: DetectionConfiguration +) -> DetectionResult? { + if request.context.layout.isCyrillic, + request.word.unicodeScalars.contains(where: { $0.isASCII && $0.properties.isAlphabetic }), + !request.word.unicodeScalars.contains(where: { (0x0400...0x04FF).contains($0.value) }) { + SwitchFixLog.engine.debug("capture mismatch: Latin characters captured under \(request.context.layout.rawValue) layout source=\(request.context.inputSourceID)") + } + detector.currentLayout = request.context.layout + detector.currentInputSourceID = request.context.inputSourceID + detector.currentTrigger = request.trigger + detector.personalExceptions = configuration.personalExceptions + detector.activeSourceSupportedLayouts = configuration.activeSourceSupportedLayouts + detector.allowedLayouts = configuration.allowedLayouts + detector.preferredCyrillicLayout = configuration.preferredCyrillicLayout + detector.ukrainianFromVariant = configuration.ukrainianFromVariant + detector.ukrainianToVariant = configuration.ukrainianToVariant + detector.syncRun(request.precedingRun, currentTokenID: request.sequence) + detector.discardBuffer() + detector.addCharacter(request.word) + return detector.flushBuffer( + boundaryCharacter: request.boundary.isEmpty ? nil : request.boundary + ) +} + +/// The text edit and layout switch a detection result asks for, before live-state checks. +public struct CorrectionEdit: Equatable, Sendable { + public let deleteCount: Int + public let replacementText: String + public let switchTarget: Layout? +} + +public enum CorrectionEditRejection: String, Error, Sendable { + case wordTooLong = "word-too-long" + case textIdenticalNoSwitch = "text-identical-no-switch" +} + +/// Shared by `InputEngine` and the benchmark replay, so both turn a result into the same edit. +/// A layout-only result deletes and retypes nothing. +public func planCorrectionEdit(result: DetectionResult, boundary: String) -> Result { + let isTextIdentical = result.originalWord == result.convertedWord + if result.originalWord.count > 64 { + return .failure(.wordTooLong) + } + if isTextIdentical && !result.shouldSwitchLayout { + return .failure(.textIdenticalNoSwitch) + } + return .success(CorrectionEdit( + deleteCount: isTextIdentical ? 0 : result.originalWord.count + boundary.count, + replacementText: isTextIdentical ? "" : result.convertedWord + boundary, + switchTarget: result.shouldSwitchLayout ? result.targetLayout : nil + )) +} diff --git a/Sources/Core/InputEngine.swift b/Sources/Core/InputEngine.swift index 58016ca..b1a6948 100644 --- a/Sources/Core/InputEngine.swift +++ b/Sources/Core/InputEngine.swift @@ -3,27 +3,34 @@ import os import Utils public struct DetectionRequest: Equatable { + public let trigger: CorrectionTrigger public let word: String public let boundary: String public let sequence: UInt64 public let editGeneration: UInt64 public let correctionEpoch: UInt64 public let context: InputContextSnapshot + /// Words still on screen right before `word`, typed in the same uninterrupted run. + public let precedingRun: [RunToken] public init( + trigger: CorrectionTrigger, word: String, boundary: String, sequence: UInt64, editGeneration: UInt64, correctionEpoch: UInt64, - context: InputContextSnapshot + context: InputContextSnapshot, + precedingRun: [RunToken] = [] ) { + self.trigger = trigger self.word = word self.boundary = boundary self.sequence = sequence self.editGeneration = editGeneration self.correctionEpoch = correctionEpoch self.context = context + self.precedingRun = precedingRun } } @@ -31,17 +38,11 @@ public final class InputEngine { public typealias ExactDetection = (DetectionRequest) -> DetectionResult? public typealias CorrectionEmission = (CorrectionPlan) -> Bool public typealias SelectedTextRequest = (pid_t, UInt64, @escaping (String?) -> Void) -> Void + /// Test seam replacing the pasteboard-based selected-text correction. + public typealias SelectionEmission = (_ trigger: CorrectionTrigger, _ selectedText: String, _ convertedText: String) -> Void public var onFocusMayChange: ((pid_t, UInt64) -> Void)? - private struct DetectionConfiguration { - var allowedLayouts = Set(Layout.allCases) - var allowCyrillicToCyrillic: Bool = false - var activeSourceSupportedLayouts: Set = [.english] - var ukrainianFromVariant: UkrainianKeyboardVariant = .standard - var ukrainianToVariant: UkrainianKeyboardVariant = .standard - } - private let inputQueue = DispatchQueue(label: "com.switchfix.input-engine", qos: .userInteractive) private let detectionQueue = DispatchQueue(label: "com.switchfix.detection", qos: .userInitiated) private let correctionQueue = DispatchQueue(label: "com.switchfix.correction", qos: .userInteractive) @@ -53,6 +54,7 @@ public final class InputEngine { private let customDetection: ExactDetection? private let customEmission: CorrectionEmission? private let selectedTextRequest: SelectedTextRequest? + private let customSelectionEmission: SelectionEmission? private let detectionConfiguration = OSAllocatedUnfairLock(initialState: DetectionConfiguration()) private var correctionEpoch: UInt64 private var latestProcessedSequence: UInt64 = 0 @@ -67,7 +69,8 @@ public final class InputEngine { corrector: TextCorrector = TextCorrector(), exactDetection: ExactDetection? = nil, correctionEmission: CorrectionEmission? = nil, - selectedTextRequest: SelectedTextRequest? = nil + selectedTextRequest: SelectedTextRequest? = nil, + selectionEmission: SelectionEmission? = nil ) { self.captureState = captureState self.stateMachine = InputStateMachine(context: initialContext, preferences: preferences) @@ -76,6 +79,7 @@ public final class InputEngine { self.customDetection = exactDetection self.customEmission = correctionEmission self.selectedTextRequest = selectedTextRequest + self.customSelectionEmission = selectionEmission correctionEpoch = captureState.updateCorrectionEnabled(preferences.isEnabled) } @@ -126,6 +130,23 @@ public final class InputEngine { } } + public func updatePersonalExceptions(_ policy: PersonalExceptionsPolicy, epoch: UInt64) { + inputQueue.async { [weak self] in + guard let self else { return } + self.correctionEpoch = epoch + self.stateMachine.resetForConfigurationChange() + self.detectionConfiguration.withLock { value in + value.personalExceptions = policy + } + self.resetDetectorState() + } + } + + public func updatePersonalExceptions(_ policy: PersonalExceptionsPolicy) { + let epoch = captureState.invalidateCorrectionConfiguration() + updatePersonalExceptions(policy, epoch: epoch) + } + public func handleGeneratedLayoutContext(_ context: InputContextSnapshot) { let generation = captureState.snapshot().editGeneration inputQueue.async { [weak self] in @@ -158,8 +179,8 @@ public final class InputEngine { return } - let configuration = self.detectionConfiguration.withLock { $0 } - guard !oldLayout.isCyrillic || !newLayout.isCyrillic || configuration.allowCyrillicToCyrillic else { + // Cyrillic ↔ Cyrillic (Ukrainian ↔ Russian) conversion is not supported. + guard !(oldLayout.isCyrillic && newLayout.isCyrillic) else { return } @@ -187,6 +208,7 @@ public final class InputEngine { self.prepareCorrection( result: result, request: DetectionRequest( + trigger: .layoutSwitch, word: bufferedWord, boundary: "", sequence: latest.latestPhysicalSequence, @@ -224,7 +246,8 @@ public final class InputEngine { ukrainianToVariant: toVariant ) guard converted != selectedText else { return } - self.corrector.performSelectionCorrection( + self.emitSelectionCorrection( + trigger: .layoutSwitch, selectedText: selectedText, convertedText: converted, targetLayout: newLayout, @@ -233,8 +256,7 @@ public final class InputEngine { sequence: latest.latestPhysicalSequence, context: context, editGeneration: latest.editGeneration, - correctionEpoch: requestCorrectionEpoch, - latestCaptureState: self.captureState.snapshot + correctionEpoch: requestCorrectionEpoch ) } else { applyBufferedCorrection() @@ -247,17 +269,23 @@ public final class InputEngine { public func updateDetectionConfiguration( allowedLayouts: Set, - allowCyrillicToCyrillic: Bool = false, + preferredCyrillicLayout: Layout? = nil, activeSourceSupportedLayouts: Set = [.english], ukrainianFromVariant: UkrainianKeyboardVariant, ukrainianToVariant: UkrainianKeyboardVariant ) { - detectionConfiguration.withLock { value in - value.allowedLayouts = allowedLayouts - value.allowCyrillicToCyrillic = allowCyrillicToCyrillic - value.activeSourceSupportedLayouts = activeSourceSupportedLayouts - value.ukrainianFromVariant = ukrainianFromVariant - value.ukrainianToVariant = ukrainianToVariant + let epoch = captureState.invalidateCorrectionConfiguration() + inputQueue.async { [weak self] in + guard let self else { return } + self.correctionEpoch = epoch + self.detectionConfiguration.withLock { value in + value.allowedLayouts = allowedLayouts + value.preferredCyrillicLayout = preferredCyrillicLayout + value.activeSourceSupportedLayouts = activeSourceSupportedLayouts + value.ukrainianFromVariant = ukrainianFromVariant + value.ukrainianToVariant = ukrainianToVariant + } + self.resetDetectorState() } } @@ -265,6 +293,23 @@ public final class InputEngine { inputQueue.async { completion() } } + /// Drain all pipeline queues in order: inputQueue -> detectionQueue -> inputQueue -> correctionQueue -> detectionQueue. + /// This guarantees that any in-flight inputs, detection, and correction preparation/execution have settled. + public func drainPipeline(completion: @escaping () -> Void) { + inputQueue.async { [weak self] in + guard let self else { completion(); return } + self.detectionQueue.async { [weak self] in + guard let self else { completion(); return } + self.inputQueue.async { [weak self] in + guard let self else { completion(); return } + self.correctionQueue.async { + self.detectionQueue.async { completion() } + } + } + } + } + } + public func inspectState( completion: @escaping (_ buffer: String, _ invalidUntilBoundary: Bool, _ sequence: UInt64) -> Void ) { @@ -322,14 +367,19 @@ public final class InputEngine { case .flush(let word, let boundary, let sequence, let context): logger.notice("word flushed length=\(word.count) boundaryLength=\(boundary.count) seq=\(sequence) layout=\(context.layout.rawValue)") let latest = captureState.snapshot() - runDetection(DetectionRequest( + let request = assembleDetectionRequest( + trigger: .automaticBoundary, word: word, boundary: boundary, sequence: sequence, editGeneration: latest.editGeneration, correctionEpoch: correctionEpoch, - context: context - )) + context: context, + run: stateMachine.run + ) + runDetection(request) + case .untrackedWordHeld(let length): + logger.debug("untracked word held length=\(length) until neighboring layout evidence") case .requestManualCorrection(let word, let sequence, let context): logger.notice("hotkey correction requested hasWord=\(word != nil) seq=\(sequence)") requestManualCorrection(word: word, sequence: sequence, context: context) @@ -360,22 +410,33 @@ public final class InputEngine { guard let self else { return } let startedAt = DispatchTime.now().uptimeNanoseconds let result: DetectionResult? + var commitJournal: (() -> Void)? if let customDetection = self.customDetection { - result = customDetection(request) + let configuration = self.detectionConfiguration.withLock { $0 } + if request.trigger == .automaticBoundary && configuration.personalExceptions.matches(rawToken: request.word) { + result = nil + } else if let rawResult = customDetection(request) { + if request.trigger == .automaticBoundary && configuration.personalExceptions.matches(rawToken: rawResult.originalWord) { + result = nil + } else if request.trigger == .automaticBoundary && request.precedingRun.contains(where: { configuration.personalExceptions.matches(rawToken: $0.text) }) && rawResult.originalWord != request.word { + result = nil + } else { + result = rawResult + } + } else { + result = nil + } } else { let configuration = self.detectionConfiguration.withLock { $0 } - self.detector.currentLayout = request.context.layout - self.detector.currentInputSourceID = request.context.inputSourceID - self.detector.activeSourceSupportedLayouts = configuration.activeSourceSupportedLayouts - self.detector.allowedLayouts = configuration.allowedLayouts - self.detector.allowCyrillicToCyrillic = configuration.allowCyrillicToCyrillic - self.detector.ukrainianFromVariant = configuration.ukrainianFromVariant - self.detector.ukrainianToVariant = configuration.ukrainianToVariant - self.detector.discardBuffer() - self.detector.addCharacter(request.word) - result = self.detector.flushBuffer( - boundaryCharacter: request.boundary.isEmpty ? nil : request.boundary - ) + let staged = self.detector.stageJournalCorrection { + evaluateDetection( + detector: self.detector, + request: request, + configuration: configuration + ) + } + result = staged.0 + commitJournal = staged.1 } let duration = DispatchTime.now().uptimeNanoseconds &- startedAt if let result { @@ -388,16 +449,17 @@ public final class InputEngine { ) } guard let result else { return } + let confirmation = commitJournal self.inputQueue.async { - self.prepareCorrection(result: result, request: request) + self.prepareCorrection(result: result, request: request, commitJournal: confirmation) } } } - private func prepareCorrection(result: DetectionResult, request: DetectionRequest) { + private func prepareCorrection(result: DetectionResult, request: DetectionRequest, commitJournal: (() -> Void)? = nil) { let latest = captureState.snapshot() let cancelReason: String? - let isTextIdentical = result.originalWord == result.convertedWord + let plannedEdit = planCorrectionEdit(result: result, boundary: request.boundary) if latest.latestPhysicalSequence != request.sequence { cancelReason = "sequence-drift" @@ -413,25 +475,24 @@ public final class InputEngine { cancelReason = "app-not-allowed" } else if !latest.correctionAllowed { cancelReason = "correction-disallowed" - } else if result.originalWord.count > 64 { - cancelReason = "word-too-long" - } else if isTextIdentical && !result.shouldSwitchLayout { - cancelReason = "text-identical-no-switch" + } else if case .failure(let rejection) = plannedEdit { + cancelReason = rejection.rawValue } else { cancelReason = nil } - guard cancelReason == nil else { - logger.debug("correction cancelled reason=\(cancelReason!) chars=\(result.originalWord.count)") + guard cancelReason == nil, case .success(let edit) = plannedEdit else { + logger.debug("correction cancelled reason=\(cancelReason ?? "none") chars=\(result.originalWord.count)") return } let boundary = request.boundary - let deleteCount = isTextIdentical ? 0 : result.originalWord.count + boundary.count - let replacementText = isTextIdentical ? "" : result.convertedWord + boundary + let deleteCount = edit.deleteCount + let replacementText = edit.replacementText logger.notice( "correction planned chars=\(result.originalWord.count) replacementChars=\(result.convertedWord.count) deletes=\(deleteCount) pid=\(request.context.frontmostPID) layoutSwitch=\(result.shouldSwitchLayout ? result.targetLayout.rawValue : "none")" ) let plan = CorrectionPlan( + trigger: request.trigger, boundarySequence: request.sequence, contextEpoch: request.context.epoch, targetPID: request.context.frontmostPID, @@ -443,7 +504,7 @@ public final class InputEngine { correctedText: result.convertedWord, boundaryText: boundary, originalLayout: result.sourceLayout, - targetLayout: result.shouldSwitchLayout ? result.targetLayout : nil + targetLayout: edit.switchTarget ) correctionQueue.async { [weak self] in @@ -452,10 +513,14 @@ public final class InputEngine { SwitchFixLog.corrector.debug("emission skipped: state changed before apply chars=\(plan.originalText.count)") return } + let emitted: Bool if let customEmission = self.customEmission { - _ = customEmission(plan) + emitted = customEmission(plan) } else { - _ = self.corrector.apply(plan, latestCaptureState: self.captureState.snapshot) + emitted = self.corrector.apply(plan, latestCaptureState: self.captureState.snapshot) + } + if emitted, let commitJournal { + self.detectionQueue.async { commitJournal() } } } } @@ -472,6 +537,7 @@ public final class InputEngine { guard let selectedTextRequest else { if let word { runDetection(DetectionRequest( + trigger: .hotkey, word: word, boundary: "", sequence: sequence, @@ -502,9 +568,19 @@ public final class InputEngine { from: context.layout, ukrainianFromVariant: configuration.ukrainianFromVariant, ukrainianToVariant: configuration.ukrainianToVariant - ).first, + ).first(where: { target, _ in + LayoutMapper.isEligibleTarget( + target, + source: context.layout, + allowed: configuration.allowedLayouts, + current: context.layout, + activeSourceSupported: configuration.activeSourceSupportedLayouts, + preferredCyrillic: configuration.preferredCyrillicLayout + ) + }), converted != selectedText { - self.corrector.performSelectionCorrection( + self.emitSelectionCorrection( + trigger: .hotkey, selectedText: selectedText, convertedText: converted, targetLayout: targetLayout, @@ -513,11 +589,11 @@ public final class InputEngine { sequence: sequence, context: context, editGeneration: generation, - correctionEpoch: requestCorrectionEpoch, - latestCaptureState: self.captureState.snapshot + correctionEpoch: requestCorrectionEpoch ) } else if let word { self.runDetection(DetectionRequest( + trigger: .hotkey, word: word, boundary: "", sequence: sequence, @@ -531,6 +607,37 @@ public final class InputEngine { } } + private func emitSelectionCorrection( + trigger: CorrectionTrigger, + selectedText: String, + convertedText: String, + targetLayout: Layout, + shouldSwitchLayout: Bool, + originalLayout: Layout, + sequence: UInt64, + context: InputContextSnapshot, + editGeneration: UInt64, + correctionEpoch: UInt64 + ) { + if let customSelectionEmission { + customSelectionEmission(trigger, selectedText, convertedText) + return + } + corrector.performSelectionCorrection( + trigger: trigger, + selectedText: selectedText, + convertedText: convertedText, + targetLayout: targetLayout, + shouldSwitchLayout: shouldSwitchLayout, + originalLayout: originalLayout, + sequence: sequence, + context: context, + editGeneration: editGeneration, + correctionEpoch: correctionEpoch, + latestCaptureState: captureState.snapshot + ) + } + private func resetDetectorState() { detectionQueue.async { [weak self] in self?.detector.reset() diff --git a/Sources/Core/InputSourceDiscoveryEngine.swift b/Sources/Core/InputSourceDiscoveryEngine.swift index bdf195e..56dc8b2 100644 --- a/Sources/Core/InputSourceDiscoveryEngine.swift +++ b/Sources/Core/InputSourceDiscoveryEngine.swift @@ -8,10 +8,13 @@ public struct InputSourceDiscoveryEngine { } var layouts = Set() + let isAppleNative = provider.id.hasPrefix("com.apple.keylayout.") // Tier 1: ISO Languages from kTISPropertyInputSourceLanguages if let languages = provider.languages { - for lang in languages { + // Apple's native layouts list every language their script can write (Ukrainian also + // lists ru, be, bg, …); only the first entry is the language the layout is for. + for lang in isAppleNative ? Array(languages.prefix(1)) : languages { let code = lang.prefix(2).lowercased() if code == "uk" { layouts.insert(.ukrainian) } if code == "ru" { layouts.insert(.russian) } @@ -60,7 +63,6 @@ public struct InputSourceDiscoveryEngine { // Probe Option / AltGr state for third-party custom hybrid layouts (e.g., typography layouts). // Apple native layouts and single-language layouts provide secondary characters via Option // for convenience, which should not classify them as full hybrid layouts. - let isAppleNative = provider.id.hasPrefix("com.apple.keylayout.") let allowsOptionProbing = !isAppleNative && (provider.languages == nil || provider.languages?.isEmpty == true) if allowsOptionProbing { diff --git a/Sources/Core/InputStateMachine.swift b/Sources/Core/InputStateMachine.swift index 4015ca2..901c589 100644 --- a/Sources/Core/InputStateMachine.swift +++ b/Sources/Core/InputStateMachine.swift @@ -38,6 +38,26 @@ public enum InputStateCommand: Equatable { case requestManualCorrection(word: String?, sequence: UInt64, context: InputContextSnapshot) case requestRevert(word: String?, sequence: UInt64, context: InputContextSnapshot) case nativeUndo + case untrackedWordHeld(length: Int) +} + +/// A word typed back-to-back with its neighbours: `text` followed by `boundary` sits on screen +/// immediately before the next token (or the caret). A correction may have replaced `text` +/// on screen with its same-length conversion. +public struct RunToken: Equatable { + public let id: UInt64 + public let text: String + public let boundary: String + /// False when the word was typed after deletes the buffer could not follow, so it may be + /// only the tail of a longer on-screen word. + public let isAnchored: Bool + + public init(id: UInt64, text: String, boundary: String, isAnchored: Bool) { + self.id = id + self.text = text + self.boundary = boundary + self.isAnchored = isAnchored + } } public struct InputStateMachine { @@ -45,10 +65,15 @@ public struct InputStateMachine { public private(set) var isInvalidUntilBoundary = false public private(set) var context: InputContextSnapshot public private(set) var preferences: InputPreferencesSnapshot + /// Words committed back-to-back since the caret last jumped, oldest first. + public private(set) var run: [RunToken] = [] - private var committedWord: String? - private var committedBoundary: String? private var untrackedDeleteCount = 0 + /// Characters typed after repeated untracked deletes. They are never judged on their own, + /// but stay recorded so a later correction of the same run can still reach them. + private var untrackedFragment: String? + + private static let maxRunLength = 16 public init(context: InputContextSnapshot, preferences: InputPreferencesSnapshot) { self.context = context @@ -60,8 +85,8 @@ public struct InputStateMachine { self.context = context guard changed else { return [] } currentBuffer = "" - committedWord = nil - committedBoundary = nil + run = [] + untrackedFragment = nil untrackedDeleteCount = 0 isInvalidUntilBoundary = false return [.invalidate(.contextChanged)] @@ -73,14 +98,21 @@ public struct InputStateMachine { invalidate(untilBoundary: true) } + /// Correction configuration changed: drop the run so no earlier word is judged under the new + /// configuration. A word already in progress is skipped until its boundary, because only part + /// of it would remain buffered; with nothing in progress the next word starts normally. + public mutating func resetForConfigurationChange() { + invalidate(untilBoundary: isInvalidUntilBoundary || !currentBuffer.isEmpty || untrackedFragment != nil) + } + public mutating func updatePreferences(_ preferences: InputPreferencesSnapshot) -> [InputStateCommand] { let previous = self.preferences self.preferences = preferences guard previous != preferences else { return [] } currentBuffer = "" - committedWord = nil - committedBoundary = nil + run = [] + untrackedFragment = nil untrackedDeleteCount = 0 isInvalidUntilBoundary = false @@ -127,8 +159,8 @@ public struct InputStateMachine { // so the buffer must drop them; keeping them desyncs the buffer from // the screen and makes the next flush delete already-corrected text. currentBuffer = "" - committedWord = nil - committedBoundary = nil + run = [] + untrackedFragment = nil return [.requestManualCorrection(word: word, sequence: input.sequence, context: input.context)] case .revertHotkey: let word = currentBuffer.isEmpty ? nil : currentBuffer @@ -136,56 +168,64 @@ public struct InputStateMachine { // restored) or falls back to a manual correction, the on-screen word // is rewritten without buffer-visible events. currentBuffer = "" - committedWord = nil - committedBoundary = nil + run = [] + untrackedFragment = nil return [.requestRevert(word: word, sequence: input.sequence, context: input.context)] case .delete: guard canBuffer(input.context) else { - committedWord = nil - committedBoundary = nil untrackedDeleteCount = 0 return invalidateForContext(input.context) } - if !currentBuffer.isEmpty && !isInvalidUntilBoundary { + if isInvalidUntilBoundary { + // Still contiguous with the caret: forget the last recorded character, if any. + if untrackedFragment?.isEmpty == false { + untrackedFragment?.removeLast() + } + return [.invalidate(.navigation)] + } + if !currentBuffer.isEmpty { currentBuffer.removeLast() untrackedDeleteCount = 0 return [.deleteLast] } - if !isInvalidUntilBoundary, var boundary = committedBoundary, !boundary.isEmpty { - boundary.removeLast() - committedBoundary = boundary + if let last = run.popLast() { untrackedDeleteCount = 0 - if boundary.isEmpty { - currentBuffer = committedWord ?? "" - committedWord = nil - committedBoundary = nil - isInvalidUntilBoundary = false + let boundary = String(last.boundary.dropLast()) + if !boundary.isEmpty { + run.append(RunToken(id: last.id, text: last.text, boundary: boundary, isAnchored: last.isAnchored)) + } else if last.isAnchored { + currentBuffer = last.text + } else { + untrackedFragment = last.text + isInvalidUntilBoundary = true } return [.deleteLast] } - committedWord = nil - committedBoundary = nil - if !isInvalidUntilBoundary { - // One delete beyond the tracked buffer can still be the normal - // "delete then type" flow. Repeated untracked deletes imply - // that the caret has entered existing text, so correction must - // stay disabled until a boundary. ponytail: keep this small - // two-delete ceiling until editors expose reliable caret context. - untrackedDeleteCount += 1 - invalidate(untilBoundary: untrackedDeleteCount >= 2) + // One delete beyond the tracked buffer can still be the normal + // "delete then type" flow. Repeated untracked deletes imply + // that the caret has entered existing text, so the next word may + // be a fragment and must not be judged on its own. ponytail: keep + // this small two-delete ceiling until editors expose reliable + // caret context. + untrackedDeleteCount += 1 + let caretEnteredExistingText = untrackedDeleteCount >= 2 + invalidate(untilBoundary: caretEnteredExistingText) + if caretEnteredExistingText { + untrackedFragment = "" } return [.invalidate(.navigation)] case .character(let text): guard canBuffer(input.context) else { - committedWord = nil - committedBoundary = nil untrackedDeleteCount = 0 return invalidateForContext(input.context) } - guard !isInvalidUntilBoundary else { return [] } + guard !isInvalidUntilBoundary else { + if let fragment = untrackedFragment { + untrackedFragment = fragment.count + text.count <= 64 ? fragment + text : nil + } + return [] + } untrackedDeleteCount = 0 - committedWord = nil - committedBoundary = nil let nextCount = currentBuffer.count + text.count guard nextCount <= 64 else { invalidate(untilBoundary: true) @@ -195,33 +235,36 @@ public struct InputStateMachine { return [.append(text)] case .boundary(let boundary): let flushedWord = (!isInvalidUntilBoundary && !currentBuffer.isEmpty) ? currentBuffer : nil - defer { - if let flushedWord { - committedWord = flushedWord - committedBoundary = boundary - } else { - committedWord = nil - committedBoundary = nil - } - currentBuffer = "" - isInvalidUntilBoundary = false - untrackedDeleteCount = 0 - } + let heldFragment = isInvalidUntilBoundary ? untrackedFragment : nil + currentBuffer = "" + isInvalidUntilBoundary = false + untrackedDeleteCount = 0 + untrackedFragment = nil guard canBuffer(input.context) else { return invalidateForContext(input.context) } - guard !isInvalidUntilBoundary, !currentBuffer.isEmpty else { return [] } - switch preferences.correctionMode { - case .automatic: - return [.flush( - word: currentBuffer, - boundary: boundary, - sequence: input.sequence, - context: input.context - )] - case .hotkey, .layoutSwitch: - return [] + if let flushedWord { + commit(RunToken(id: input.sequence, text: flushedWord, boundary: boundary, isAnchored: true)) + switch preferences.correctionMode { + case .automatic: + return [.flush( + word: flushedWord, + boundary: boundary, + sequence: input.sequence, + context: input.context + )] + case .hotkey, .layoutSwitch: + return [] + } + } + // A boundary with no tracked word before it puts a character on screen + // that no token accounts for, so earlier words are no longer adjacent. + run = [] + if let heldFragment, !heldFragment.isEmpty, preferences.correctionMode == .automatic { + commit(RunToken(id: input.sequence, text: heldFragment, boundary: boundary, isAnchored: false)) + return [.untrackedWordHeld(length: heldFragment.count)] } + return [] } } @@ -239,8 +282,19 @@ public struct InputStateMachine { private mutating func invalidate(untilBoundary: Bool) { currentBuffer = "" - committedWord = nil - committedBoundary = nil + run = [] + untrackedFragment = nil isInvalidUntilBoundary = untilBoundary } + + private mutating func commit(_ token: RunToken) { + guard !token.boundary.isEmpty else { + run = [] + return + } + run.append(token) + if run.count > InputStateMachine.maxRunLength { + run.removeFirst(run.count - InputStateMachine.maxRunLength) + } + } } diff --git a/Sources/Core/KeyboardMonitor.swift b/Sources/Core/KeyboardMonitor.swift index be1acaf..4c7979e 100644 --- a/Sources/Core/KeyboardMonitor.swift +++ b/Sources/Core/KeyboardMonitor.swift @@ -396,10 +396,15 @@ public final class KeyboardMonitor { guard let text = translated ?? KeyboardMonitor.eventCharacterString(from: event) else { return .navigation } + return KeyboardMonitor.capturedKind(forText: text) + } + + /// Classifies the text a key produced as part of a word or as a word boundary. + public static func capturedKind(forText text: String) -> CapturedInput.Kind { if text.count == 1, let scalar = text.unicodeScalars.first, - KeyboardMonitor.boundaryCharacterSet.contains(scalar), - !KeyboardMonitor.softBoundaryCharacterSet.contains(scalar) { + boundaryCharacterSet.contains(scalar), + !softBoundaryCharacterSet.contains(scalar) { return .boundary(text) } return .character(text) diff --git a/Sources/Core/LayoutDetector.swift b/Sources/Core/LayoutDetector.swift index e609aea..0b5db92 100644 --- a/Sources/Core/LayoutDetector.swift +++ b/Sources/Core/LayoutDetector.swift @@ -61,7 +61,9 @@ public class LayoutDetector { private var pendingSwitchLayout: Layout? private var pendingSwitchCount: Int = 0 private var recentOutcomes: [RecentOutcome] = [] - private var pendingSuppressedShort: SuppressedShort? + private var journal: [JournalToken] = [] + private var currentTokenID: UInt64? + private var pendingSweepMarks: [(index: Int, display: String, target: Layout)] = [] private var isOutOfSync: Bool = false private let validator = WordValidator.shared @@ -69,8 +71,10 @@ public class LayoutDetector { /// Layouts that are allowed as correction targets (defaults to all). public var allowedLayouts: Set = Set(Layout.allCases) - /// Whether to allow corrections between different Cyrillic layouts (e.g. Ukrainian ↔ Russian). - public var allowCyrillicToCyrillic: Bool = false + /// The Cyrillic layout the user is currently on, or was last on. Latin text is only ever + /// converted to this Cyrillic layout; `nil` means no history, so every allowed one is tried. + /// Cyrillic ↔ Cyrillic (Ukrainian ↔ Russian) conversion is intentionally unsupported. + public var preferredCyrillicLayout: Layout? private enum RecentOutcome { case valid(Layout) @@ -78,14 +82,27 @@ public class LayoutDetector { case unknown } - private struct SuppressedShort { - let originalWord: String - let convertedWord: String + /// A word flushed earlier in the current typing run that is still on screen right before + /// the word being checked. `display` is what the screen shows now: `typed`, or its + /// same-length conversion once a correction has covered it. + private struct JournalToken { + let id: UInt64? + let typed: String + let boundary: String + var display: String + var convertedTo: Layout? + } + + private struct RunSweep { + let original: String + let converted: String let sourceLayout: Layout - let targetLayout: Layout - let boundaryAfterWord: String } + private static let maxJournalLength = 16 + /// InputEngine refuses corrections longer than this, so a sweep must stay within it. + private static let maxSweepLength = 64 + private static let boundaryCharacterSet: CharacterSet = { var set = CharacterSet.punctuationCharacters.union(.symbols) set.subtract(CharacterSet(charactersIn: "'’`-")) @@ -121,6 +138,12 @@ public class LayoutDetector { InputSourceManager.shared.sourceID(for: layout) } + /// The trigger provenance for the current detection request. + public var currentTrigger: CorrectionTrigger = .automaticBoundary + + /// Personal exceptions protecting specific words from automatic correction. + public var personalExceptions: PersonalExceptionsPolicy = .empty + public init() {} /// Add a character to the word buffer. @@ -134,17 +157,23 @@ public class LayoutDetector { /// - Parameter boundaryCharacter: The character that triggered the flush (e.g. " ", "\n"), or nil for hotkey-triggered flush. @discardableResult public func flushBuffer(boundaryCharacter: String? = nil) -> DetectionResult? { + let tokenID = currentTokenID + currentTokenID = nil guard !wordBuffer.isEmpty else { + // A boundary with no word before it: earlier words are no longer adjacent. + journal.removeAll() state = .idle isOutOfSync = false return nil } // Split trailing punctuation from the buffer (e.g. "hello," -> "hello" + ",") + let typedWord = wordBuffer let (coreWord, trailingPunctuation) = splitTrailingBoundary(from: wordBuffer) wordBuffer = coreWord guard !wordBuffer.isEmpty else { + record(typed: typedWord, display: typedWord, convertedTo: nil, boundary: boundaryCharacter, id: tokenID) state = .idle isOutOfSync = false return nil @@ -155,11 +184,32 @@ public class LayoutDetector { pendingBoundaryCharacter = boundary.isEmpty ? nil : boundary // Check buffer at word boundaries (short words are handled by WordValidator whitelist) + pendingSweepMarks = [] let result = isOutOfSync ? nil : checkBuffer() if let result { delegate?.layoutDetector(self, didDetectWrongLayout: result, boundaryCharacter: pendingBoundaryCharacter) } + if isOutOfSync { + journal.removeAll() + } else if let result { + for mark in pendingSweepMarks { + journal[mark.index].display = mark.display + journal[mark.index].convertedTo = mark.target + } + // Conversion maps key to key, so the current word is the tail of the corrected span. + record( + typed: typedWord, + display: String(result.convertedWord.suffix(coreWord.count)) + trailingPunctuation, + convertedTo: result.targetLayout, + boundary: boundaryCharacter, + id: tokenID + ) + } else { + record(typed: typedWord, display: typedWord, convertedTo: nil, boundary: boundaryCharacter, id: tokenID) + } + pendingSweepMarks = [] + // Reset buffer pendingBoundaryCharacter = nil wordBuffer = "" @@ -168,10 +218,42 @@ public class LayoutDetector { return result } + /// Evaluate normally, but retain only observed text until the caller confirms emission. + /// The returned commit must run on the same serial queue as detection. Token identities + /// let late confirmations update surviving tokens without resurrecting an invalidated run. + func stageJournalCorrection(_ evaluate: () -> DetectionResult?) -> (DetectionResult?, () -> Void) { + let before = journal + let result = evaluate() + let proposed = journal + journal = proposed.map { token in + var observed = token + let previous = before.first { $0.id == token.id && $0.typed == token.typed && $0.boundary == token.boundary } + observed.display = previous?.display ?? token.typed + observed.convertedTo = previous?.convertedTo + return observed + } + let observed = journal + return (result, { [weak self] in + guard let self else { return } + for index in self.journal.indices { + let token = self.journal[index] + guard let baseline = observed.first(where: { + $0.id == token.id && $0.typed == token.typed && $0.boundary == token.boundary + }), token.display == baseline.display, token.convertedTo == baseline.convertedTo, + let confirmed = proposed.first(where: { + $0.id == token.id && $0.typed == token.typed && $0.boundary == token.boundary + }) else { continue } + self.journal[index].display = confirmed.display + self.journal[index].convertedTo = confirmed.convertedTo + } + }) + } + /// Called when backspace/delete is pressed — remove last character from buffer. public func deleteLastCharacter() { guard !wordBuffer.isEmpty else { isOutOfSync = true + journal.removeAll() return } wordBuffer.removeLast() @@ -192,6 +274,24 @@ public class LayoutDetector { wordBuffer = "" state = .idle isOutOfSync = true + journal.removeAll() + } + + /// Replace the run journal with the words the caller knows are still on screen right before + /// the word about to be flushed, keeping what earlier corrections changed about them. + public func syncRun(_ preceding: [RunToken], currentTokenID: UInt64) { + let known = journal + journal = preceding.map { token in + let match = known.first { $0.id == token.id && $0.typed == token.text } + return JournalToken( + id: token.id, + typed: token.text, + boundary: token.boundary, + display: match?.display ?? token.text, + convertedTo: match?.convertedTo + ) + } + self.currentTokenID = currentTokenID } /// Reset all state (e.g., when app loses focus). @@ -203,7 +303,8 @@ public class LayoutDetector { pendingSwitchLayout = nil pendingSwitchCount = 0 recentOutcomes = [] - pendingSuppressedShort = nil + journal.removeAll() + currentTokenID = nil isOutOfSync = false } @@ -216,9 +317,19 @@ public class LayoutDetector { private func checkBuffer() -> DetectionResult? { state = .detecting - let suppressedShort = consumePendingSuppressedShort() let word = wordBuffer + + if currentTrigger == .automaticBoundary && personalExceptions.matches(rawToken: word) { + SwitchFixLog.detector.debug("personal exception matched chars=\(word.count) — suppressing automatic correction") + consecutiveWrongCount = 0 + lastDetectionResult = nil + pendingSwitchLayout = nil + pendingSwitchCount = 0 + state = .buffering + return nil + } + let sourceLayout = resolvedSourceLayout(for: word) // Skip if the word contains mixed scripts (both Latin and Cyrillic) @@ -234,53 +345,33 @@ public class LayoutDetector { let currentWordParts = splitTokenForValidation(word) let currentValidationInput = currentWordParts.core.isEmpty ? word : currentWordParts.core - let activeLayouts = activeSourceSupportedLayouts.contains(currentLayout) ? activeSourceSupportedLayouts : [currentLayout] - let isLocallyValid = activeLayouts.contains { layout in - let lang = languageForLayout(layout) - return validator.validate(currentValidationInput, language: lang, allowSuggestion: false).isValid - } + let activeLayouts = activeLayoutSet + let isLocallyValid = isValidInActiveLayouts(word) if isLocallyValid { - if let merged = mergeSuppressedShort( - suppressedShort, + if let sweep = sweepRun( + toward: sourceLayout, currentOriginal: word, currentConverted: word, - targetLayout: sourceLayout, isLowConfidence: word.count <= lowConfidenceMaxLength, shouldSwitch: false - ), let suppressed = suppressedShort { + ) { let result = DetectionResult( - sourceLayout: suppressed.sourceLayout, + sourceLayout: sweep.sourceLayout, targetLayout: sourceLayout, - convertedWord: merged.converted, - originalWord: merged.original, + convertedWord: sweep.converted, + originalWord: sweep.original, shouldSwitchLayout: false ) consecutiveWrongCount = 0 lastDetectionResult = nil - recordOutcome(.corrected(from: suppressed.sourceLayout, to: sourceLayout)) + recordOutcome(.corrected(from: sweep.sourceLayout, to: sourceLayout)) state = .buffering return result } - // A short word valid in both layouts is undecidable in isolation. - // Keep it pending and only merge it when the next word confirms the - // alternative layout. - if let ambiguous = validAlternative(for: word, sourceLayout: sourceLayout, activeLayouts: activeLayouts), - let boundary = pendingBoundaryCharacter, - !boundary.isEmpty { - pendingSuppressedShort = SuppressedShort( - originalWord: word, - convertedWord: ambiguous.convertedWord, - sourceLayout: sourceLayout, - targetLayout: ambiguous.targetLayout, - boundaryAfterWord: boundary - ) - SwitchFixLog.detector.info( - "deferred ambiguous valid word chars=\(word.count) until neighboring layout evidence" - ) - } - + // A word valid in both layouts is undecidable in isolation. It stays in the run + // journal, where a later word that confirms the other layout can still reach it. SwitchFixLog.detector.debug("valid in active layout set (\(activeLayouts.map(\.rawValue).joined(separator: ", "))): chars=\(word.count) — no correction") consecutiveWrongCount = 0 lastDetectionResult = nil @@ -294,31 +385,30 @@ public class LayoutDetector { // If the word is typed in a script not supported by active layouts, but is ALREADY valid // in an allowed alternative layout, switch layout without modifying the text (layout-only switch). if !activeLayouts.contains(sourceLayout), allowedLayouts.contains(sourceLayout) { - let isCyrillicCrossSwitch = !allowCyrillicToCyrillic && currentLayout.isCyrillic && sourceLayout.isCyrillic + let isCyrillicCrossSwitch = currentLayout.isCyrillic && sourceLayout.isCyrillic if !isCyrillicCrossSwitch { let candidateLanguage = languageForLayout(sourceLayout) if validator.validate(currentValidationInput, language: candidateLanguage, allowSuggestion: false).isValid { let isLowConfidence = word.count <= lowConfidenceMaxLength let shouldSwitch = shouldSwitchLayout(isLowConfidence: isLowConfidence, targetLayout: sourceLayout) - if let merged = mergeSuppressedShort( - suppressedShort, + if let sweep = sweepRun( + toward: sourceLayout, currentOriginal: word, currentConverted: word, - targetLayout: sourceLayout, isLowConfidence: isLowConfidence, shouldSwitch: shouldSwitch - ), let suppressed = suppressedShort { + ) { let result = DetectionResult( - sourceLayout: suppressed.sourceLayout, + sourceLayout: sweep.sourceLayout, targetLayout: sourceLayout, - convertedWord: merged.converted, - originalWord: merged.original, + convertedWord: sweep.converted, + originalWord: sweep.original, shouldSwitchLayout: shouldSwitch ) consecutiveWrongCount = 0 lastDetectionResult = nil - recordOutcome(.corrected(from: suppressed.sourceLayout, to: sourceLayout)) + recordOutcome(.corrected(from: sweep.sourceLayout, to: sourceLayout)) state = .buffering return result } @@ -334,15 +424,6 @@ public class LayoutDetector { SwitchFixLog.detector.info("suppressed desynchronized word chars=\(word.count) (weak evidence, deferring)") consecutiveWrongCount = 0 lastDetectionResult = nil - if let boundary = pendingBoundaryCharacter, !boundary.isEmpty { - pendingSuppressedShort = SuppressedShort( - originalWord: word, - convertedWord: word, - sourceLayout: sourceLayout, - targetLayout: sourceLayout, - boundaryAfterWord: boundary - ) - } recordOutcome(.unknown) state = .buffering return nil @@ -392,11 +473,7 @@ public class LayoutDetector { ukrainianToVariant: ukrainianToVariant ) .filter { targetLayout, _ in - guard allowedLayouts.contains(targetLayout) else { return false } - if !allowCyrillicToCyrillic && sourceLayout.isCyrillic && targetLayout.isCyrillic { - return false - } - return true + isEligibleTarget(targetLayout, sourceLayout: sourceLayout) } for (targetLayout, converted) in alternatives { // Self-switch suppression: if target layout uses the exact same physical input source already active @@ -484,32 +561,22 @@ public class LayoutDetector { SwitchFixLog.detector.info("suppressed short word chars=\(word.count) (weak evidence, deferring)") consecutiveWrongCount = 0 lastDetectionResult = nil - if let boundary = pendingBoundaryCharacter, !boundary.isEmpty { - pendingSuppressedShort = SuppressedShort( - originalWord: word, - convertedWord: finalWord, - sourceLayout: sourceLayout, - targetLayout: targetLayout, - boundaryAfterWord: boundary - ) - } recordOutcome(.unknown) state = .buffering return nil } var correctionSourceLayout = sourceLayout - if let merged = mergeSuppressedShort( - suppressedShort, + if let sweep = sweepRun( + toward: targetLayout, currentOriginal: word, currentConverted: finalWord, - targetLayout: targetLayout, isLowConfidence: isLowConfidence, shouldSwitch: shouldSwitch ) { - originalForCorrection = merged.original - finalWord = merged.converted - correctionSourceLayout = suppressedShort?.sourceLayout ?? sourceLayout + originalForCorrection = sweep.original + finalWord = sweep.converted + correctionSourceLayout = sweep.sourceLayout } consecutiveWrongCount += 1 @@ -608,42 +675,15 @@ public class LayoutDetector { return false } - private func validAlternative( - for word: String, - sourceLayout: Layout, - activeLayouts: Set - ) -> (targetLayout: Layout, convertedWord: String)? { - guard word.count <= lowConfidenceMaxLength else { return nil } - - let alternatives = LayoutMapper.convertToAlternatives( - word, - from: sourceLayout, - ukrainianFromVariant: ukrainianFromVariant, - ukrainianToVariant: ukrainianToVariant + private func isEligibleTarget(_ targetLayout: Layout, sourceLayout: Layout) -> Bool { + LayoutMapper.isEligibleTarget( + targetLayout, + source: sourceLayout, + allowed: allowedLayouts, + current: currentLayout, + activeSourceSupported: activeSourceSupportedLayouts, + preferredCyrillic: preferredCyrillicLayout ) - - for (targetLayout, converted) in alternatives where allowedLayouts.contains(targetLayout) { - guard targetLayout != sourceLayout, - !activeLayouts.contains(targetLayout), - !isSelfSwitch(targetLayout) else { - continue - } - - let parts = splitTokenForValidation(converted) - let validationInput = parts.core.isEmpty ? converted : parts.core - guard validator.validate( - validationInput, - language: languageForLayout(targetLayout), - allowSuggestion: false - ).isValid else { - continue - } - - let recomposed = parts.prefix + validationInput + parts.suffix - return (targetLayout, applyCase(from: word, to: recomposed)) - } - - return nil } private func isSelfSwitch(_ targetLayout: Layout) -> Bool { @@ -690,33 +730,173 @@ public class LayoutDetector { return hasStrongCurrentContext(for: sourceLayout) } - private func consumePendingSuppressedShort() -> SuppressedShort? { - let value = pendingSuppressedShort - pendingSuppressedShort = nil - return value + private var activeLayoutSet: Set { + activeSourceSupportedLayouts.contains(currentLayout) ? activeSourceSupportedLayouts : [currentLayout] + } + + private func isValidInActiveLayouts(_ word: String) -> Bool { + let parts = splitTokenForValidation(word) + let validationInput = parts.core.isEmpty ? word : parts.core + return activeLayoutSet.contains { layout in + validator.validate(validationInput, language: languageForLayout(layout), allowSuggestion: false).isValid + } + } + + private func record(typed: String, display: String, convertedTo: Layout?, boundary: String?, id: UInt64?) { + // Without a boundary after it the word is not committed (hotkey flush), and the + // correction it may trigger rewrites text the journal cannot follow. + guard let boundary, !boundary.isEmpty else { + journal.removeAll() + return + } + journal.append(JournalToken( + id: id, + typed: typed, + boundary: boundary, + display: display, + convertedTo: convertedTo + )) + if journal.count > LayoutDetector.maxJournalLength { + journal.removeFirst(journal.count - LayoutDetector.maxJournalLength) + } } - private func mergeSuppressedShort( - _ suppressed: SuppressedShort?, + /// The word as it would read on `targetLayout`, when that reading is a valid word there. + private func validConversion(of word: String, from sourceLayout: Layout, to targetLayout: Layout) -> String? { + guard !word.isEmpty, + !containsMixedScripts(word), + isEligibleTarget(targetLayout, sourceLayout: sourceLayout), + !isSelfSwitch(targetLayout) else { + return nil + } + if sourceLayout == .english, word.count >= 2, isAllUppercase(word) { + return nil + } + + var candidates = [LayoutMapper.convert( + word, + from: sourceLayout, + to: targetLayout, + ukrainianFromVariant: ukrainianFromVariant, + ukrainianToVariant: ukrainianToVariant + )] + if sourceLayout == .ukrainian && targetLayout == .english { + candidates.append(LayoutMapper.convert( + word, + from: sourceLayout, + to: targetLayout, + ukrainianFromVariant: ukrainianFromVariant == .legacy ? .standard : .legacy, + ukrainianToVariant: ukrainianToVariant + )) + } else if sourceLayout == .english && targetLayout == .ukrainian { + candidates.append(LayoutMapper.convert( + word, + from: sourceLayout, + to: targetLayout, + ukrainianFromVariant: ukrainianFromVariant, + ukrainianToVariant: ukrainianToVariant == .legacy ? .standard : .legacy + )) + } + + let originalParts = splitTokenForValidation(word) + for candidate in candidates where candidate != word { + let parts = splitTokenForValidation(candidate) + // Same guards as a direct correction: no letter that turns into leading + // punctuation, no punctuation that turns into letters, no short flags. + if parts.prefix.count > originalParts.prefix.count { continue } + if originalParts.core.isEmpty || parts.core.isEmpty { continue } + if originalParts.prefix.contains("-") && originalParts.core.count <= shortWordSuppressionLength { continue } + guard validator.validate( + parts.core, + language: languageForLayout(targetLayout), + allowSuggestion: false + ).isValid else { + continue + } + return applyCase(from: word, to: candidate) + } + return nil + } + + /// Extends a correction of the current word back over the words typed right before it in + /// the same run. The layout cannot change mid-run, so once the current word shows the run + /// is on the wrong layout, every earlier word that also reads as a valid `targetLayout` + /// word belongs to the same mistake — including ones that looked fine on their own. + private func sweepRun( + toward targetLayout: Layout, currentOriginal: String, currentConverted: String, - targetLayout: Layout, isLowConfidence: Bool, shouldSwitch: Bool - ) -> (original: String, converted: String)? { - guard let suppressed else { return nil } - guard suppressed.targetLayout == targetLayout else { return nil } - - // Merge only when the current word provides stronger evidence than the suppressed short word. + ) -> RunSweep? { + // Rewrite earlier words only when the current word is stronger evidence than a lone short one. let hasStrongCurrentSignal = currentOriginal.count > shortWordSuppressionLength || shouldSwitch || !isLowConfidence - guard hasStrongCurrentSignal else { return nil } + guard hasStrongCurrentSignal, !journal.isEmpty else { return nil } + + // Nearest word first. `converted == nil` means the screen already shows the target layout. + var swept: [(index: Int, converted: String?, isAmbiguous: Bool)] = [] + var sweepSource: Layout? + var stoppedAtCurrentLayoutWord = false + var spanLength = currentOriginal.count + + for index in journal.indices.reversed() { + let token = journal[index] + // Enter may have submitted the earlier text; never rewrite across it. + if token.boundary.contains(where: \.isNewline) { break } + let tokenLength = token.typed.count + token.boundary.count + if spanLength + tokenLength > LayoutDetector.maxSweepLength { break } + + // Personal exceptions barrier: backward sweep stops before any protected token + if currentTrigger == .automaticBoundary && (personalExceptions.matches(rawToken: token.typed) || personalExceptions.matches(rawToken: token.display)) { + break + } - let bridge = suppressed.boundaryAfterWord - guard !bridge.isEmpty else { return nil } + if let convertedTo = token.convertedTo { + guard convertedTo == targetLayout else { break } + swept.append((index, nil, false)) + } else { + let tokenSource = resolvedSourceLayout(for: token.typed) + guard tokenSource != targetLayout, sweepSource == nil || sweepSource == tokenSource else { break } + let (core, trailing) = splitTrailingBoundary(from: token.typed) + let isValidAsTyped = isValidInActiveLayouts(core) + guard let converted = validConversion(of: core, from: tokenSource, to: targetLayout) else { + stoppedAtCurrentLayoutWord = isValidAsTyped + break + } + sweepSource = tokenSource + swept.append((index, converted + trailing, isValidAsTyped)) + } + spanLength += tokenLength + } - return ( - original: suppressed.originalWord + bridge + currentOriginal, - converted: suppressed.convertedWord + bridge + currentConverted + // A word valid in both layouts sides with its neighbours: right after a word that only + // makes sense as typed, it is far more likely part of that text than of the mistake. + if stoppedAtCurrentLayoutWord { + while let farthest = swept.last, farthest.converted != nil, farthest.isAmbiguous { + swept.removeLast() + } + } + // Nothing to rewrite before the farthest word that actually changes. + while let farthest = swept.last, farthest.converted == nil { + swept.removeLast() + } + guard !swept.isEmpty, let sourceLayout = sweepSource else { return nil } + + var original = "" + var converted = "" + for entry in swept.reversed() { + let token = journal[entry.index] + original += token.display + token.boundary + converted += (entry.converted ?? token.display) + token.boundary + if let display = entry.converted { + pendingSweepMarks.append((entry.index, display, targetLayout)) + } + } + SwitchFixLog.detector.info("run sweep: correction extended over \(swept.count) earlier words") + return RunSweep( + original: original + currentOriginal, + converted: converted + currentConverted, + sourceLayout: sourceLayout ) } @@ -944,6 +1124,11 @@ public class LayoutDetector { } private func splitTokenForValidation(_ token: String) -> (prefix: String, core: String, suffix: String) { + Self.splitTokenForValidation(token) + } + + /// Shared with `PersonalExceptionsPolicy`, so exception matching uses the detector's own token rules. + static func splitTokenForValidation(_ token: String) -> (prefix: String, core: String, suffix: String) { let chars = Array(token) if chars.isEmpty { return ("", "", "") @@ -975,6 +1160,11 @@ public class LayoutDetector { /// Split trailing punctuation/symbols from a word. private func splitTrailingBoundary(from text: String) -> (core: String, trailing: String) { + Self.splitTrailingBoundary(from: text) + } + + /// Shared with `PersonalExceptionsPolicy`, so exception matching uses the detector's own token rules. + static func splitTrailingBoundary(from text: String) -> (core: String, trailing: String) { var core = text var trailing = "" while let last = core.last, isBoundaryChar(last) { @@ -984,7 +1174,7 @@ public class LayoutDetector { return (core, trailing) } - private func isBoundaryChar(_ char: Character) -> Bool { + private static func isBoundaryChar(_ char: Character) -> Bool { guard let scalar = char.unicodeScalars.first else { return false } return LayoutDetector.boundaryCharacterSet.contains(scalar) } diff --git a/Sources/Core/LayoutMapper.swift b/Sources/Core/LayoutMapper.swift index cb70b7a..6002ffa 100644 --- a/Sources/Core/LayoutMapper.swift +++ b/Sources/Core/LayoutMapper.swift @@ -102,23 +102,10 @@ public class LayoutMapper { return map }() - // RU → UK mapping for characters that differ between Russian and Ukrainian layouts. - private static let ruToUkStandard: [Character: Character] = [ - "ы": "і", "э": "є", "ъ": "ї", "ё": "ґ", - "Ы": "І", "Э": "Є", "Ъ": "Ї", "Ё": "Ґ", - ] - - private static let ruToUkLegacy: [Character: Character] = [ - "ы": "и", "и": "і", "э": "є", "ъ": "ї", "ё": "ґ", - "Ы": "И", "И": "І", "Э": "Є", "Ъ": "Ї", "Ё": "Ґ", - ] - // Pre-built reverse mappings private static let ruToEn: [Character: Character] = buildReverse(enToRu) private static let ukStandardToEn: [Character: Character] = buildReverse(enToUkStandard) private static let ukLegacyToEn: [Character: Character] = buildReverse(enToUkLegacy) - private static let ukStandardToRu: [Character: Character] = buildReverse(ruToUkStandard) - private static let ukLegacyToRu: [Character: Character] = buildReverse(ruToUkLegacy) private static func buildReverse(_ map: [Character: Character]) -> [Character: Character] { var result = [Character: Character]() @@ -142,10 +129,7 @@ public class LayoutMapper { case (.russian, .english): return ruToEn case (.ukrainian, .english): return ukrainianFromVariant == .legacy ? ukLegacyToEn : ukStandardToEn - case (.russian, .ukrainian): - return ukrainianToVariant == .legacy ? ruToUkLegacy : ruToUkStandard - case (.ukrainian, .russian): - return ukrainianFromVariant == .legacy ? ukLegacyToRu : ukStandardToRu + // Cyrillic ↔ Cyrillic (Ukrainian ↔ Russian) conversion is intentionally unsupported. default: return nil } } @@ -215,4 +199,28 @@ public class LayoutMapper { } return results } + + /// Whether `target` may be used when correcting text typed on `source`. + /// Only Cyrillic ↔ Latin conversions are allowed. Latin text maps to exactly one Cyrillic + /// layout: `currentCyrillic` when the user is on a Cyrillic layout (plus the other Cyrillic + /// layouts of a hybrid active source), otherwise `preferredCyrillic`; with no history every + /// allowed Cyrillic layout is eligible. + public static func isEligibleTarget( + _ target: Layout, + source: Layout, + allowed: Set, + current: Layout, + activeSourceSupported: Set, + preferredCyrillic: Layout? + ) -> Bool { + guard allowed.contains(target) else { return false } + guard target.isCyrillic else { return true } + if source.isCyrillic { return false } + if current.isCyrillic { + if target == current { return true } + return activeSourceSupported.contains(current) && activeSourceSupported.contains(target) + } + guard let preferred = preferredCyrillic, allowed.contains(preferred) else { return true } + return target == preferred + } } diff --git a/Sources/Core/PersonalExceptionsPolicy.swift b/Sources/Core/PersonalExceptionsPolicy.swift new file mode 100644 index 0000000..63eb4ef --- /dev/null +++ b/Sources/Core/PersonalExceptionsPolicy.swift @@ -0,0 +1,184 @@ +import Foundation + +public enum ExceptionValidationError: Error, Equatable, Sendable { + case empty + case tooLong(Int) + case byteLimitExceeded(Int) + case noLetters + case invalidCharacter(Character) + case invalidPunctuationPosition + case capacityExceeded + case duplicate + + public var localizedDescription: String { + switch self { + case .empty: + return "Word cannot be empty." + case .tooLong(let count): + return "Word exceeds 64 characters (\(count))." + case .byteLimitExceeded(let bytes): + return "Word exceeds 256 bytes (\(bytes))." + case .noLetters: + return "Word must contain at least one letter." + case .invalidCharacter(let char): + return "Invalid character: '\(char)'." + case .invalidPunctuationPosition: + return "Punctuation cannot be at the start, end, or adjacent to other punctuation." + case .capacityExceeded: + return "Maximum exception list capacity (1,000 words) reached." + case .duplicate: + return "Already in the list." + } + } +} + +public struct PersonalExceptionsPolicy: Equatable, Sendable { + public static let maxEntries = 1_000 + public static let maxCharacters = 64 + public static let maxBytes = 256 + + public let words: Set + + public init(words: Set = []) { + self.words = words + } + + public static let empty = PersonalExceptionsPolicy(words: []) + + public var isEmpty: Bool { + return words.isEmpty + } + + public var count: Int { + return words.count + } + + public func contains(_ canonicalWord: String) -> Bool { + return words.contains(canonicalWord) + } + + public func matches(rawToken: String) -> Bool { + guard !words.isEmpty else { return false } + guard let key = PersonalExceptionsPolicy.deriveMatchingKey(from: rawToken) else { + return false + } + return words.contains(key) + } + + /// Canonicalization per contract: + /// 1. NFC normalization + /// 2. Locale-independent lowercased + /// 3. Replacement of U+2019 (’) and U+02BC (ʼ) with ASCII apostrophe (') + /// 4. NFC normalization again + public static func canonicalize(_ text: String) -> String { + let nfc = text.precomposedStringWithCanonicalMapping + let lower = nfc.lowercased() + var substituted = "" + for scalar in lower.unicodeScalars { + if scalar.value == 0x2019 || scalar.value == 0x02BC { + substituted.append("'") + } else { + substituted.append(String(scalar)) + } + } + return substituted.precomposedStringWithCanonicalMapping + } + + /// Validation per contract: + /// - 1–64 Characters and <= 256 UTF-8 bytes after normalization + /// - At least one Unicode letter + /// - Allowed: letters, combining marks, ASCII digits, internal (', `, -) + /// - Punctuation may not be first, last, or adjacent to another punctuation + /// - Rejects whitespace, controls, format chars, emoji, paths, URLs, multiple tokens + public static func validate(candidate: String) -> Result { + let trimmed = candidate.trimmingCharacters(in: .whitespacesAndNewlines) + if trimmed.isEmpty { + return .failure(.empty) + } + + let canonical = canonicalize(trimmed) + if canonical.isEmpty { + return .failure(.empty) + } + + if canonical.count > maxCharacters { + return .failure(.tooLong(canonical.count)) + } + + if canonical.utf8.count > maxBytes { + return .failure(.byteLimitExceeded(canonical.utf8.count)) + } + + var hasLetter = false + let allowedPunctuation: Set = ["'", "`", "-"] + + let scalars = Array(canonical.unicodeScalars) + for scalar in scalars { + switch scalar.properties.generalCategory { + case .uppercaseLetter, .lowercaseLetter, .titlecaseLetter, .modifierLetter, .otherLetter: + hasLetter = true + continue + case .nonspacingMark, .spacingMark, .enclosingMark: + // Combining marks are allowed but do not count as a letter. + continue + default: + break + } + if scalar.value >= 0x30 && scalar.value <= 0x39 { + // ASCII digits allowed + } else if allowedPunctuation.contains(scalar) { + // Allowed internal punctuation + } else { + return .failure(.invalidCharacter(Character(scalar))) + } + } + + guard hasLetter else { + return .failure(.noLetters) + } + + // Combining marks may not appear standalone at the start of a word + if let first = scalars.first { + switch first.properties.generalCategory { + case .nonspacingMark, .spacingMark, .enclosingMark: + return .failure(.invalidCharacter(Character(first))) + default: + break + } + } + + // Punctuation positioning check: + // May not be first, last, or adjacent to another punctuation character + if let first = scalars.first, allowedPunctuation.contains(first) { + return .failure(.invalidPunctuationPosition) + } + if let last = scalars.last, allowedPunctuation.contains(last) { + return .failure(.invalidPunctuationPosition) + } + + for i in 0..<(scalars.count - 1) { + if allowedPunctuation.contains(scalars[i]) && allowedPunctuation.contains(scalars[i + 1]) { + return .failure(.invalidPunctuationPosition) + } + } + + // The stored canonical entry must match its own derived key so it is always reachable + guard let matchingKey = deriveMatchingKey(from: canonical), matchingKey == canonical else { + return .failure(.invalidCharacter(canonical.first ?? " ")) + } + + return .success(canonical) + } + + /// Derive the matching key from a raw captured token with the detector's own tokenizers: + /// 1. Take the token after `splitTrailingBoundary` + /// 2. Drop the leading and trailing non-letter, non-digit characters (`splitTokenForValidation`) + /// 3. Canonicalize + public static func deriveMatchingKey(from text: String) -> String? { + let core = LayoutDetector.splitTrailingBoundary(from: text).core + let stripped = LayoutDetector.splitTokenForValidation(core).core + guard !stripped.isEmpty else { return nil } + let canonical = canonicalize(stripped) + return canonical.isEmpty ? nil : canonical + } +} diff --git a/Sources/Core/PersonalExceptionsPublisher.swift b/Sources/Core/PersonalExceptionsPublisher.swift new file mode 100644 index 0000000..d258589 --- /dev/null +++ b/Sources/Core/PersonalExceptionsPublisher.swift @@ -0,0 +1,21 @@ +import Foundation + +/// Forwards exception snapshots to the engine only when the set genuinely changes. Every +/// preference edit posts the same notification; republishing an unchanged set would invalidate +/// pending corrections and reset detector context for no reason. +public final class PersonalExceptionsPublisher { + private var published: PersonalExceptionsPolicy? + private let publish: (PersonalExceptionsPolicy) -> Void + + public init(publish: @escaping (PersonalExceptionsPolicy) -> Void) { + self.publish = publish + } + + @discardableResult + public func publishIfChanged(_ policy: PersonalExceptionsPolicy) -> Bool { + guard policy != published else { return false } + published = policy + publish(policy) + return true + } +} diff --git a/Sources/Core/PersonalExceptionsStore.swift b/Sources/Core/PersonalExceptionsStore.swift new file mode 100644 index 0000000..530bbb9 --- /dev/null +++ b/Sources/Core/PersonalExceptionsStore.swift @@ -0,0 +1,138 @@ +import Foundation +import os + +public enum AddExceptionResult: Equatable, Sendable { + case success(PersonalExceptionsPolicy, addedWord: String) + case duplicate(String) + case invalid(ExceptionValidationError) +} + +/// Why a stored list could not be used as saved. Carries no stored words. +public enum PersonalExceptionsLoadIssue: Equatable, Sendable { + /// Wrong type, unknown schema version, or more than the allowed entries: nothing was loaded + /// and the stored value is left untouched until the next explicit edit. + case unreadable + /// The envelope was valid, but this many entries failed validation and were ignored. + case skippedEntries(Int) +} + +public final class PersonalExceptionsStore: @unchecked Sendable { + public static let preferenceKey = "SwitchFix_personalExceptions_v1" + public static let currentSchemaVersion = 1 + + private let defaults: UserDefaults + private let lock = OSAllocatedUnfairLock(initialState: ()) + + public init(defaults: UserDefaults = .standard) { + self.defaults = defaults + } + + /// Load validated policy snapshot from storage. + /// If storage is empty, returns empty policy. + /// If envelope is invalid/oversized/unknown-version, leaves stored value untouched and returns empty policy. + public func loadPolicy() -> PersonalExceptionsPolicy { + return lock.withLock { + loadLocked().policy + } + } + + /// Load the validated policy together with a recoverable issue for Settings to report. + public func loadPolicyReportingIssue() -> (policy: PersonalExceptionsPolicy, issue: PersonalExceptionsLoadIssue?) { + return lock.withLock { + loadLocked() + } + } + + private func loadPolicyLocked() -> PersonalExceptionsPolicy { + loadLocked().policy + } + + private func loadLocked() -> (policy: PersonalExceptionsPolicy, issue: PersonalExceptionsLoadIssue?) { + guard let stored = defaults.object(forKey: Self.preferenceKey) else { + return (.empty, nil) + } + + guard let dict = stored as? [String: Any], + let version = dict["schemaVersion"] as? Int, version == Self.currentSchemaVersion, + let wordsArray = dict["words"] as? [Any], + wordsArray.count <= PersonalExceptionsPolicy.maxEntries else { + return (.empty, .unreadable) + } + + var validWords: Set = [] + var skipped = 0 + for value in wordsArray { + if let word = value as? String, + case .success(let canonical) = PersonalExceptionsPolicy.validate(candidate: word) { + validWords.insert(canonical) + } else { + skipped += 1 + } + } + + return (PersonalExceptionsPolicy(words: validWords), skipped > 0 ? .skippedEntries(skipped) : nil) + } + + /// Add a word to personal exceptions. + public func add(candidate: String) -> AddExceptionResult { + return lock.withLock { + let validationResult = PersonalExceptionsPolicy.validate(candidate: candidate) + guard case .success(let canonical) = validationResult else { + if case .failure(let error) = validationResult { + return .invalid(error) + } + return .invalid(.empty) + } + + var current = loadPolicyLocked().words + if current.contains(canonical) { + return .duplicate(canonical) + } + + guard current.count < PersonalExceptionsPolicy.maxEntries else { + return .invalid(.capacityExceeded) + } + + current.insert(canonical) + persistLocked(words: current) + let newPolicy = PersonalExceptionsPolicy(words: current) + return .success(newPolicy, addedWord: canonical) + } + } + + /// Remove a word from personal exceptions. + @discardableResult + public func remove(word: String) -> PersonalExceptionsPolicy { + return remove(words: [word]) + } + + /// Remove multiple words from personal exceptions in a single batch. + @discardableResult + public func remove(words: Set) -> PersonalExceptionsPolicy { + return lock.withLock { + guard !words.isEmpty else { return loadPolicyLocked() } + let canonicalWords = Set(words.map { PersonalExceptionsPolicy.canonicalize($0) }) + var current = loadPolicyLocked().words + current.subtract(canonicalWords) + persistLocked(words: current) + return PersonalExceptionsPolicy(words: current) + } + } + + /// Clear all exceptions (for tests or user reset). + public func removeAll() { + lock.withLock { + defaults.removeObject(forKey: Self.preferenceKey) + } + } + + /// Direct persistence helper. + private func persistLocked(words: Set) { + let sortedWords = words.sorted() + let dict: [String: Any] = [ + "schemaVersion": Self.currentSchemaVersion, + "words": sortedWords + ] + defaults.set(dict, forKey: Self.preferenceKey) + } +} diff --git a/Sources/Core/TextCorrector.swift b/Sources/Core/TextCorrector.swift index 710011c..530c6ea 100644 --- a/Sources/Core/TextCorrector.swift +++ b/Sources/Core/TextCorrector.swift @@ -5,6 +5,7 @@ import os import Utils public struct CorrectionPlan: Equatable { + public let trigger: CorrectionTrigger public let boundarySequence: UInt64 public let contextEpoch: UInt64 public let targetPID: pid_t @@ -19,6 +20,7 @@ public struct CorrectionPlan: Equatable { public let targetLayout: Layout? public init( + trigger: CorrectionTrigger, boundarySequence: UInt64, contextEpoch: UInt64, targetPID: pid_t, @@ -32,6 +34,7 @@ public struct CorrectionPlan: Equatable { originalLayout: Layout, targetLayout: Layout? ) { + self.trigger = trigger self.boundarySequence = boundarySequence self.contextEpoch = contextEpoch self.targetPID = targetPID @@ -208,6 +211,7 @@ public final class TextCorrector { } let plan = current.plan value = UndoState(plan: CorrectionPlan( + trigger: plan.trigger, boundarySequence: plan.boundarySequence, contextEpoch: context.epoch, targetPID: context.frontmostPID, @@ -258,6 +262,7 @@ public final class TextCorrector { let replacement = undo.plan.originalText + undo.plan.boundaryText let inverse = CorrectionPlan( + trigger: undo.plan.trigger, boundarySequence: sequence, contextEpoch: latest.context.epoch, targetPID: latest.context.frontmostPID, @@ -304,6 +309,7 @@ public final class TextCorrector { } public func performSelectionCorrection( + trigger: CorrectionTrigger, selectedText: String, convertedText: String, targetLayout: Layout, @@ -376,6 +382,7 @@ public final class TextCorrector { } let plan = CorrectionPlan( + trigger: trigger, boundarySequence: sequence, contextEpoch: context.epoch, targetPID: context.frontmostPID, diff --git a/Sources/Dictionary/Resources/overrides/en_US_allow.txt b/Sources/Dictionary/Resources/overrides/en_US_allow.txt index 4d72eff..1753fa1 100644 --- a/Sources/Dictionary/Resources/overrides/en_US_allow.txt +++ b/Sources/Dictionary/Resources/overrides/en_US_allow.txt @@ -27,3 +27,4 @@ replaced replacing gsd vs +vnc diff --git a/Sources/InputPipelineTestRunner/main.swift b/Sources/InputPipelineTestRunner/main.swift index 1a5915c..2784253 100644 --- a/Sources/InputPipelineTestRunner/main.swift +++ b/Sources/InputPipelineTestRunner/main.swift @@ -203,6 +203,179 @@ run("deleting previous word completely and typing new word") { check(flushes == ["Ш"], "Ш must flush cleanly after previous word deletion") } +private func type(_ text: String, into machine: inout InputStateMachine, from sequence: inout UInt64, context: InputContextSnapshot) { + for character in text { + sequence += 1 + _ = machine.consume(input(sequence: sequence, kind: .character(String(character)), context: context)) + } +} + +private func press(_ kind: CapturedInput.Kind, into machine: inout InputStateMachine, from sequence: inout UInt64, context: InputContextSnapshot) -> [InputStateCommand] { + sequence += 1 + return machine.consume(input(sequence: sequence, kind: kind, context: context)) +} + +run("run keeps the words typed back-to-back") { + let current = context() + var machine = automaticMachine(current) + var sequence: UInt64 = 0 + type("cat", into: &machine, from: &sequence, context: current) + _ = press(.boundary(" "), into: &machine, from: &sequence, context: current) + type("dog", into: &machine, from: &sequence, context: current) + _ = press(.boundary(" "), into: &machine, from: &sequence, context: current) + check(machine.run.map(\.text) == ["cat", "dog"], "each flushed word must join the run in order") + check(machine.run.allSatisfy { $0.boundary == " " && $0.isAnchored }, "flushed words are anchored and keep their boundary") + check(machine.run.map(\.id) == [4, 8], "a run token is identified by the boundary that committed it") + + _ = press(.delete, into: &machine, from: &sequence, context: current) + check(machine.currentBuffer == "dog" && machine.run.map(\.text) == ["cat"], "deleting the boundary reopens the last word and takes it off the run") + for _ in 0..<3 { _ = press(.delete, into: &machine, from: &sequence, context: current) } + check(machine.currentBuffer.isEmpty && machine.run.map(\.text) == ["cat"], "emptying the reopened word leaves the earlier run intact") + let reopened = press(.delete, into: &machine, from: &sequence, context: current) + check(reopened == [.deleteLast] && machine.currentBuffer == "cat" && machine.run.isEmpty, "the next delete reopens the word before it") +} + +run("run ends wherever the screen and the journal may disagree") { + let current = context() + var sequence: UInt64 = 0 + + var machine = automaticMachine(current) + type("cat", into: &machine, from: &sequence, context: current) + _ = press(.boundary(" "), into: &machine, from: &sequence, context: current) + _ = press(.boundary(" "), into: &machine, from: &sequence, context: current) + check(machine.run.isEmpty, "a boundary with no word before it is on screen but in no token") + + for kind in [CapturedInput.Kind.navigation, .focusMayChange, .undo, .hotkey, .revertHotkey, .tapReset, .queueOverflow] { + machine = automaticMachine(current) + type("cat", into: &machine, from: &sequence, context: current) + _ = press(.boundary(" "), into: &machine, from: &sequence, context: current) + _ = press(kind, into: &machine, from: &sequence, context: current) + check(machine.run.isEmpty, "\(kind) must end the run") + } + + machine = automaticMachine(current) + type("cat", into: &machine, from: &sequence, context: current) + _ = press(.boundary(" "), into: &machine, from: &sequence, context: current) + _ = machine.updateContext(context(epoch: 2)) + check(machine.run.isEmpty, "a context change must end the run") +} + +run("word typed after untracked deletes is held, not judged") { + let current = context() + var machine = automaticMachine(current) + var sequence: UInt64 = 0 + _ = press(.delete, into: &machine, from: &sequence, context: current) + _ = press(.delete, into: &machine, from: &sequence, context: current) + type("asx", into: &machine, from: &sequence, context: current) + _ = press(.delete, into: &machine, from: &sequence, context: current) + type("k", into: &machine, from: &sequence, context: current) + check(machine.currentBuffer.isEmpty, "a possible fragment must never enter the tracked buffer") + let held = press(.boundary(" "), into: &machine, from: &sequence, context: current) + check(held == [.untrackedWordHeld(length: 3)], "the fragment is held without a flush") + check( + machine.run == [RunToken(id: sequence, text: "ask", boundary: " ", isAnchored: false)], + "the held word starts the run unanchored, with in-fragment deletes applied" + ) + + type("me", into: &machine, from: &sequence, context: current) + let flushed = press(.boundary(" "), into: &machine, from: &sequence, context: current) + check(flushed.count == 1 && machine.run.map(\.text) == ["ask", "me"], "the next word flushes with the held word before it") + + _ = press(.delete, into: &machine, from: &sequence, context: current) + for _ in 0..<2 { _ = press(.delete, into: &machine, from: &sequence, context: current) } + _ = press(.delete, into: &machine, from: &sequence, context: current) + check( + machine.isInvalidUntilBoundary && machine.currentBuffer.isEmpty && machine.run.isEmpty, + "reopening a held word must not promote it to a tracked word" + ) + type("s", into: &machine, from: &sequence, context: current) + let reheld = press(.boundary(" "), into: &machine, from: &sequence, context: current) + check(reheld == [.untrackedWordHeld(length: 4)] && machine.run.map(\.text) == ["asks"], "a reopened held word keeps recording") +} + +run("held words are limited to delete-caused gaps in automatic mode") { + let current = context() + var sequence: UInt64 = 0 + + var machine = automaticMachine(current) + _ = press(.navigation, into: &machine, from: &sequence, context: current) + type("ask", into: &machine, from: &sequence, context: current) + check(press(.boundary(" "), into: &machine, from: &sequence, context: current).isEmpty && machine.run.isEmpty, "typing after navigation stays unrecorded") + + machine = automaticMachine(current) + _ = press(.delete, into: &machine, from: &sequence, context: current) + _ = press(.delete, into: &machine, from: &sequence, context: current) + _ = press(.queueOverflow, into: &machine, from: &sequence, context: current) + type("ask", into: &machine, from: &sequence, context: current) + check(press(.boundary(" "), into: &machine, from: &sequence, context: current).isEmpty && machine.run.isEmpty, "dropped events must stop the recording") + + machine = InputStateMachine( + context: current, + preferences: InputPreferencesSnapshot(isEnabled: true, correctionMode: .hotkey) + ) + _ = press(.delete, into: &machine, from: &sequence, context: current) + _ = press(.delete, into: &machine, from: &sequence, context: current) + type("ask", into: &machine, from: &sequence, context: current) + check(press(.boundary(" "), into: &machine, from: &sequence, context: current).isEmpty && machine.run.isEmpty, "hotkey mode holds nothing") +} + +run("detection request carries the preceding run") { + let current = context() + let store = CaptureStateStore(context: current, hotkeys: HotkeyConfiguration(hotkeyModifiers: 0)) + let lock = NSLock() + var requests: [DetectionRequest] = [] + let detected = DispatchSemaphore(value: 0) + let engine = InputEngine( + captureState: store, + initialContext: current, + preferences: InputPreferencesSnapshot(isEnabled: true, correctionMode: .automatic), + exactDetection: { request in + lock.lock() + requests.append(request) + lock.unlock() + detected.signal() + return nil + }, + correctionEmission: { _ in true } + ) + func send(_ kind: CapturedInput.Kind) { + engine.enqueue(store.capture( + timestamp: 0, + kind: kind, + keyCode: 0, + flagsRawValue: 0, + isAutorepeat: false, + sourcePID: 1, + sourceUserData: 0 + )) + } + send(.delete) + send(.delete) + for character in ["a", "s", "k"] { send(.character(character)) } + send(.boundary(" ")) + for character in ["m", "e"] { send(.character(character)) } + send(.boundary(" ")) + check(detected.wait(timeout: .now() + 1) == .success, "the tracked word must reach detection") + for character in ["n", "o", "w"] { send(.character(character)) } + send(.boundary(" ")) + check(detected.wait(timeout: .now() + 1) == .success, "the next word must reach detection") + send(.delete) + for character in ["s"] { send(.character(character)) } + send(.boundary(" ")) + check(detected.wait(timeout: .now() + 1) == .success, "the reopened word must reach detection") + + lock.lock() + let seen = requests + lock.unlock() + check(seen.map(\.word) == ["me", "now", "nows"], "the held word itself must never be detected on its own") + check( + seen.first?.precedingRun.map(\.text) == ["ask"] && seen.first?.precedingRun.first?.isAnchored == false, + "the first tracked word must be offered the held word before it" + ) + check(seen.dropFirst().first?.precedingRun.map(\.text) == ["ask", "me"], "each request lists the words still on screen before it") + check(seen.last?.precedingRun.map(\.text) == ["ask", "me"], "a reopened word must not appear in its own preceding run") +} + run("manual hotkey resyncs buffer") { let current = context() var machine = automaticMachine(current) @@ -293,6 +466,7 @@ run("native undo invalidates detector state") { run("correction sequence and context gates") { let base = context() let plan = CorrectionPlan( + trigger: .automaticBoundary, boundarySequence: 10, contextEpoch: base.epoch, targetPID: base.frontmostPID, @@ -586,6 +760,7 @@ run("tap reset recovery") { run("bounded tagged event batch") { let current = context() let plan = CorrectionPlan( + trigger: .automaticBoundary, boundarySequence: 1, contextEpoch: 1, targetPID: current.frontmostPID, @@ -613,6 +788,7 @@ run("bounded tagged event batch") { check(events.allSatisfy { $0.sourceUserData == switchFixEventMarker }, "every generated event must carry the marker") let longPlan = CorrectionPlan( + trigger: .automaticBoundary, boundarySequence: 1, contextEpoch: 1, targetPID: 100, @@ -638,6 +814,7 @@ run("bounded tagged event batch") { run("undo generation") { let originalContext = context(epoch: 4, pid: 100) let plan = CorrectionPlan( + trigger: .automaticBoundary, boundarySequence: 7, contextEpoch: 4, targetPID: 100, @@ -1125,6 +1302,433 @@ run("blocked collaborators") { assertRoutingContinues(while: .correction) } +run("drainPipeline and request assembly") { + let current = context() + let store = CaptureStateStore(context: current, hotkeys: HotkeyConfiguration(hotkeyModifiers: 0)) + var emissionCalled = false + let engine = InputEngine( + captureState: store, + initialContext: current, + preferences: InputPreferencesSnapshot(isEnabled: true, correctionMode: .automatic), + exactDetection: { request in + check(request.word == "test", "assembled word must match") + check(request.boundary == " ", "assembled boundary must match") + return DetectionResult( + sourceLayout: .english, + targetLayout: .ukrainian, + convertedWord: "тест", + originalWord: request.word, + shouldSwitchLayout: false + ) + }, + correctionEmission: { plan in + emissionCalled = true + return true + } + ) + + engine.enqueue(store.capture(timestamp: 1, kind: .character("t"), keyCode: 17, flagsRawValue: 0, isAutorepeat: false, sourcePID: 1, sourceUserData: 0)) + engine.enqueue(store.capture(timestamp: 2, kind: .character("e"), keyCode: 14, flagsRawValue: 0, isAutorepeat: false, sourcePID: 1, sourceUserData: 0)) + engine.enqueue(store.capture(timestamp: 3, kind: .character("s"), keyCode: 1, flagsRawValue: 0, isAutorepeat: false, sourcePID: 1, sourceUserData: 0)) + engine.enqueue(store.capture(timestamp: 4, kind: .character("t"), keyCode: 17, flagsRawValue: 0, isAutorepeat: false, sourcePID: 1, sourceUserData: 0)) + engine.enqueue(store.capture(timestamp: 5, kind: .boundary(" "), keyCode: 49, flagsRawValue: 0, isAutorepeat: false, sourcePID: 1, sourceUserData: 0)) + + let drained = DispatchSemaphore(value: 0) + engine.drainPipeline { + drained.signal() + } + check(drained.wait(timeout: .now() + 2) == .success, "drainPipeline must hop all queues and complete") + check(emissionCalled, "drainPipeline must guarantee all correction stages have executed") +} + +run("personal exceptions pipeline integration and invalidation") { + let current = context() + let store = CaptureStateStore(context: current, hotkeys: HotkeyConfiguration(hotkeyModifiers: 0)) + var emissionCount = 0 + let engine = InputEngine( + captureState: store, + initialContext: current, + preferences: InputPreferencesSnapshot(isEnabled: true, correctionMode: .automatic), + exactDetection: { request in + return DetectionResult( + sourceLayout: .english, + targetLayout: .ukrainian, + convertedWord: "тест", + originalWord: request.word, + shouldSwitchLayout: false + ) + }, + correctionEmission: { plan in + emissionCount += 1 + return true + } + ) + + // Set personal exceptions containing "protected" + engine.updatePersonalExceptions(PersonalExceptionsPolicy(words: ["protected"])) + let drained1 = DispatchSemaphore(value: 0) + engine.drainPipeline { drained1.signal() } + check(drained1.wait(timeout: .now() + 2) == .success, "pipeline drain after exception update") + + // 1. Enqueue protected word "protected " + for (idx, char) in "protected".enumerated() { + engine.enqueue(store.capture(timestamp: UInt64(idx + 1), kind: .character(String(char)), keyCode: 0, flagsRawValue: 0, isAutorepeat: false, sourcePID: 1, sourceUserData: 0)) + } + engine.enqueue(store.capture(timestamp: 10, kind: .boundary(" "), keyCode: 49, flagsRawValue: 0, isAutorepeat: false, sourcePID: 1, sourceUserData: 0)) + + let drained2 = DispatchSemaphore(value: 0) + engine.drainPipeline { drained2.signal() } + check(drained2.wait(timeout: .now() + 2) == .success, "pipeline drain after protected word") + check(emissionCount == 0, "protected word must not emit correction even with exactDetection") + + // 2. Invalidate configuration increments epoch without changing enabled state + let initialEpoch = store.snapshot().correctionEpoch + let newEpoch = store.invalidateCorrectionConfiguration() + check(newEpoch == initialEpoch + 1, "invalidateCorrectionConfiguration must increment epoch") + check(store.snapshot().correctionAllowed, "enabled state remains allowed after invalidation") + + // A plan created with old epoch must not be eligible + let stalePlan = CorrectionPlan( + trigger: .automaticBoundary, + boundarySequence: 10, + contextEpoch: current.epoch, + targetPID: current.frontmostPID, + editGeneration: 10, + correctionEpoch: initialEpoch, + deleteCount: 4, + replacementText: "test ", + originalText: "тест", + correctedText: "test", + boundaryText: " ", + originalLayout: .ukrainian, + targetLayout: .english + ) + check(!stalePlan.isEligible(using: store.snapshot()), "plan with stale epoch must fail isEligible") + + // 3. Exception and layout configuration updates invalidate in-flight correction before emission. + for updateLayoutConfiguration in [false, true] { + let addStore = CaptureStateStore(context: current, hotkeys: HotkeyConfiguration(hotkeyModifiers: 0)) + var addEmissionCount = 0 + let detectionStarted = DispatchSemaphore(value: 0) + let releaseDetection = DispatchSemaphore(value: 0) + let engine2 = InputEngine( + captureState: addStore, + initialContext: current, + preferences: InputPreferencesSnapshot(isEnabled: true, correctionMode: .automatic), + exactDetection: { request in + detectionStarted.signal() + _ = releaseDetection.wait(timeout: .now() + 5) + return DetectionResult( + sourceLayout: .english, + targetLayout: .ukrainian, + convertedWord: "тест", + originalWord: request.word, + shouldSwitchLayout: false + ) + }, + correctionEmission: { _ in + addEmissionCount += 1 + return true + } + ) + + // Type word "test " + engine2.enqueue(addStore.capture(timestamp: 1, kind: .character("t"), keyCode: 0, flagsRawValue: 0, isAutorepeat: false, sourcePID: 1, sourceUserData: 0)) + engine2.enqueue(addStore.capture(timestamp: 2, kind: .boundary(" "), keyCode: 49, flagsRawValue: 0, isAutorepeat: false, sourcePID: 1, sourceUserData: 0)) + + check(detectionStarted.wait(timeout: .now() + 1) == .success, "detection started") + if updateLayoutConfiguration { + engine2.updateDetectionConfiguration( + allowedLayouts: [.english], + ukrainianFromVariant: .standard, + ukrainianToVariant: .standard + ) + } else { + engine2.updatePersonalExceptions(PersonalExceptionsPolicy(words: ["newexception"])) + } + releaseDetection.signal() + + let drained3 = DispatchSemaphore(value: 0) + engine2.drainPipeline { drained3.signal() } + check(drained3.wait(timeout: .now() + 2) == .success, "pipeline drained after mid-flight configuration update") + check(addEmissionCount == 0, "in-flight correction must be invalidated by configuration update") + } +} + +private final class ExceptionPipelineHarness { + private final class Recorder { + private let lock = NSLock() + private var plans: [CorrectionPlan] = [] + private var selections: [(CorrectionTrigger, String)] = [] + + func record(_ plan: CorrectionPlan) { + lock.lock(); defer { lock.unlock() } + plans.append(plan) + } + + func record(_ trigger: CorrectionTrigger, _ selectedText: String) { + lock.lock(); defer { lock.unlock() } + selections.append((trigger, selectedText)) + } + + func snapshot() -> (plans: [CorrectionPlan], selections: [(CorrectionTrigger, String)]) { + lock.lock(); defer { lock.unlock() } + return (plans, selections) + } + } + + let store: CaptureStateStore + let engine: InputEngine + private let recorder = Recorder() + private let selectionEmitted = DispatchSemaphore(value: 0) + private var timestamp: UInt64 = 0 + + var plans: [CorrectionPlan] { recorder.snapshot().plans } + var selections: [(CorrectionTrigger, String)] { recorder.snapshot().selections } + + init( + mode: InputCorrectionMode, + focus: SecureFocusState = .notSecure, + selectedText: String? = nil, + detectionStarted: DispatchSemaphore? = nil, + releaseDetection: DispatchSemaphore? = nil + ) { + let current = context(focus: focus) + let store = CaptureStateStore(context: current, hotkeys: HotkeyConfiguration(hotkeyModifiers: 0)) + let recorder = self.recorder + let selectionEmitted = self.selectionEmitted + self.store = store + engine = InputEngine( + captureState: store, + initialContext: current, + preferences: InputPreferencesSnapshot(isEnabled: true, correctionMode: mode), + exactDetection: { request in + detectionStarted?.signal() + if let releaseDetection { _ = releaseDetection.wait(timeout: .now() + 5) } + return DetectionResult( + sourceLayout: .english, + targetLayout: .ukrainian, + convertedWord: "тест", + originalWord: request.word, + shouldSwitchLayout: false + ) + }, + correctionEmission: { plan in + recorder.record(plan) + return true + }, + selectedTextRequest: selectedText.map { text in + { _, _, completion in completion(text) } + }, + selectionEmission: { trigger, selected, _ in + recorder.record(trigger, selected) + selectionEmitted.signal() + } + ) + } + + func enqueue(_ kind: CapturedInput.Kind) { + timestamp += 1 + engine.enqueue(store.capture( + timestamp: timestamp, + kind: kind, + keyCode: 0, + flagsRawValue: 0, + isAutorepeat: false, + sourcePID: 1, + sourceUserData: 0 + )) + } + + func type(_ text: String) { + for character in text { enqueue(.character(String(character))) } + } + + /// Selected-text correction hops through the selection queue, which `drainPipeline` does not cover. + func waitForSelection() -> Bool { + selectionEmitted.wait(timeout: .now() + 2) == .success + } + + func drain() -> Bool { + let done = DispatchSemaphore(value: 0) + engine.drainPipeline { done.signal() } + return done.wait(timeout: .now() + 2) == .success + } + + func switchSource(to layout: Layout) { + let previous = store.snapshot().context + let next = store.replaceContext( + frontmostPID: previous.frontmostPID, + appAllowed: previous.appAllowed, + layout: layout, + inputSourceID: "com.test.\(layout.rawValue)", + secureFocus: previous.secureFocus + ) + engine.updateDetectionConfiguration( + allowedLayouts: [.english, .ukrainian], + activeSourceSupportedLayouts: [layout], + ukrainianFromVariant: .standard, + ukrainianToVariant: .standard + ) + engine.handleLayoutChange(from: previous.layout, to: layout, context: next, fromVariant: .standard, toVariant: .standard) + } +} + +run("configuration change keeps the next word") { + let current = context() + func flushes(_ commands: [InputStateCommand]) -> [String] { + commands.compactMap { command -> String? in + if case .flush(let word, _, _, _) = command { return word } + return nil + } + } + + var idle = automaticMachine(current) + idle.resetForConfigurationChange() + var commands: [InputStateCommand] = [] + for (index, character) in "next".enumerated() { + commands += idle.consume(input(sequence: UInt64(index + 1), kind: .character(String(character)), context: current)) + } + commands += idle.consume(input(sequence: 10, kind: .boundary(" "), context: current)) + check(flushes(commands) == ["next"], "with nothing in progress, the next word after a configuration change is judged") + + var midWord = automaticMachine(current) + _ = midWord.consume(input(sequence: 1, kind: .character("p"), context: current)) + _ = midWord.consume(input(sequence: 2, kind: .character("a"), context: current)) + midWord.resetForConfigurationChange() + commands = [] + for (index, character) in "rt".enumerated() { + commands += midWord.consume(input(sequence: UInt64(index + 3), kind: .character(String(character)), context: current)) + } + commands += midWord.consume(input(sequence: 5, kind: .boundary(" "), context: current)) + for (index, character) in "next".enumerated() { + commands += midWord.consume(input(sequence: UInt64(index + 6), kind: .character(String(character)), context: current)) + } + commands += midWord.consume(input(sequence: 10, kind: .boundary(" "), context: current)) + check(flushes(commands) == ["next"], "a word in progress is skipped to its boundary, never judged as a fragment") +} + +run("personal exceptions: explicit triggers bypass, automatic stays blocked") { + let protected = PersonalExceptionsPolicy(words: ["protected"]) + + let automatic = ExceptionPipelineHarness(mode: .automatic) + automatic.engine.updatePersonalExceptions(protected) + // No drain here: the published policy must precede input enqueued right after it. + automatic.type("protected") + automatic.enqueue(.boundary(" ")) + check(automatic.drain(), "automatic pipeline drains") + check(automatic.plans.isEmpty, "configuration published before new input blocks the automatic correction") + automatic.type("protected") + automatic.enqueue(.hotkey) + check(automatic.drain(), "hotkey pipeline drains") + check(automatic.plans.map(\.trigger) == [.hotkey], "hotkey correction of a protected word is applied") + check(automatic.plans.first?.originalText == "protected", "hotkey correction converts the protected word itself") + + let layoutSwitch = ExceptionPipelineHarness(mode: .layoutSwitch) + layoutSwitch.engine.updatePersonalExceptions(protected) + layoutSwitch.type("protected") + check(layoutSwitch.drain(), "layout-switch input drains before the source changes") + layoutSwitch.switchSource(to: .ukrainian) + check(layoutSwitch.drain(), "layout-switch pipeline drains") + check(layoutSwitch.plans.map(\.trigger) == [.layoutSwitch], "On Layout Switch correction of a protected word is applied") + check(layoutSwitch.plans.first?.originalText == "protected", "layout-switch correction converts the buffered protected word") + + let selection = ExceptionPipelineHarness(mode: .automatic, selectedText: "protected") + selection.engine.updatePersonalExceptions(protected) + selection.enqueue(.hotkey) + check(selection.waitForSelection(), "selection hotkey correction is emitted") + check(selection.selections.map(\.0) == [.hotkey], "selected-text hotkey correction is applied with hotkey provenance") + check(selection.selections.first?.1 == "protected", "selected protected text is converted") + + let switchSelection = ExceptionPipelineHarness(mode: .layoutSwitch, selectedText: "protected") + switchSelection.engine.updatePersonalExceptions(protected) + check(switchSelection.drain(), "selection layout-switch setup drains") + switchSelection.switchSource(to: .ukrainian) + check(switchSelection.waitForSelection(), "selection layout-switch correction is emitted") + check(switchSelection.selections.map(\.0) == [.layoutSwitch], "selected text converted on a layout switch carries layoutSwitch provenance") +} + +run("personal exceptions: security still wins for explicit triggers") { + let secure = ExceptionPipelineHarness(mode: .automatic, focus: .secure) + secure.engine.updatePersonalExceptions(PersonalExceptionsPolicy(words: ["protected"])) + secure.type("protected") + secure.enqueue(.hotkey) + secure.type("other") + secure.enqueue(.boundary(" ")) + check(secure.drain(), "secure pipeline drains") + check(secure.plans.isEmpty, "hotkey bypass of exceptions never bypasses secure-field protection") +} + +run("personal exceptions: empty-policy parity and removal in flight") { + let empty = ExceptionPipelineHarness(mode: .automatic) + empty.engine.updatePersonalExceptions(.empty) + let unrelated = ExceptionPipelineHarness(mode: .automatic) + unrelated.engine.updatePersonalExceptions(PersonalExceptionsPolicy(words: ["unrelated"])) + for harness in [empty, unrelated] { + harness.type("test") + harness.enqueue(.boundary(" ")) + check(harness.drain(), "parity pipeline drains") + } + check(empty.plans.map(\.replacementText) == ["тест "], "empty policy corrects as before") + check(unrelated.plans.map(\.replacementText) == empty.plans.map(\.replacementText), "an unrelated exception changes no correction") + + let started = DispatchSemaphore(value: 0) + let release = DispatchSemaphore(value: 0) + let removal = ExceptionPipelineHarness(mode: .automatic, detectionStarted: started, releaseDetection: release) + removal.engine.updatePersonalExceptions(PersonalExceptionsPolicy(words: ["other", "protected"])) + removal.type("test") + removal.enqueue(.boundary(" ")) + check(started.wait(timeout: .now() + 1) == .success, "detection started before removal") + removal.engine.updatePersonalExceptions(PersonalExceptionsPolicy(words: ["other"])) + release.signal() + check(removal.drain(), "removal pipeline drains") + check(removal.plans.isEmpty, "removing an exception invalidates the in-flight correction") +} + +run("personal exceptions: unchanged set is not republished") { + let current = context() + let store = CaptureStateStore(context: current, hotkeys: HotkeyConfiguration(hotkeyModifiers: 0)) + let engine = InputEngine( + captureState: store, + initialContext: current, + preferences: InputPreferencesSnapshot(isEnabled: true, correctionMode: .automatic), + exactDetection: { _ in nil }, + correctionEmission: { _ in true } + ) + let publisher = PersonalExceptionsPublisher { engine.updatePersonalExceptions($0) } + let start = store.snapshot().correctionEpoch + check(publisher.publishIfChanged(PersonalExceptionsPolicy(words: ["a"])), "first set is published") + check(!publisher.publishIfChanged(PersonalExceptionsPolicy(words: ["a"])), "unchanged set is not republished") + check(store.snapshot().correctionEpoch == start + 1, "an unrelated preference change does not invalidate corrections") + check(publisher.publishIfChanged(PersonalExceptionsPolicy(words: ["a", "b"])), "a changed set is published") + check(store.snapshot().correctionEpoch == start + 2, "a genuine change invalidates once") +} + +run("personal exceptions: U+02BC words reach detection whole") { + check(KeyboardMonitor.capturedKind(forText: "\u{02BC}") == .character("\u{02BC}"), "U+02BC is captured as part of a word") + check(KeyboardMonitor.capturedKind(forText: "’") == .character("’"), "U+2019 is captured as part of a word") + check(KeyboardMonitor.capturedKind(forText: "'") == .character("'"), "ASCII apostrophe is captured as part of a word") + check(KeyboardMonitor.capturedKind(forText: "!") == .boundary("!"), "control: '!' is a boundary") + + let current = context(layout: .ukrainian) + var machine = automaticMachine(current) + var commands: [InputStateCommand] = [] + for (index, character) in "сімʼя".enumerated() { + commands += machine.consume(input( + sequence: UInt64(index + 1), + kind: KeyboardMonitor.capturedKind(forText: String(character)), + context: current + )) + } + commands += machine.consume(input(sequence: 10, kind: .boundary(" "), context: current)) + let flushed = commands.compactMap { command -> String? in + if case .flush(let word, _, _, _) = command { return word } + return nil + } + check(flushed == ["сімʼя"], "the whole word including U+02BC is flushed once") + check(PersonalExceptionsPolicy(words: ["сім'я"]).matches(rawToken: flushed.first ?? ""), "the flushed word matches its exception") +} + private func runIntegrationSmoke() { guard Permissions.isAccessibilityGranted(), Permissions.isKeyboardListeningAvailable() else { print("SKIP: integration smoke requires Accessibility and Input Monitoring") @@ -1156,6 +1760,7 @@ private func runIntegrationSmoke() { let smokeContext = context(pid: getpid()) let plan = CorrectionPlan( + trigger: .automaticBoundary, boundarySequence: 1, contextEpoch: smokeContext.epoch, targetPID: getpid(), diff --git a/Sources/LayoutBenchmark/Baseline.swift b/Sources/LayoutBenchmark/Baseline.swift new file mode 100644 index 0000000..8710966 --- /dev/null +++ b/Sources/LayoutBenchmark/Baseline.swift @@ -0,0 +1,180 @@ +import Core +import Foundation + +/// Environment and inputs a run was measured with. Everything here is measured or read from +/// the corpus, never typed in by hand. +public struct RunMetadata: Codable, Equatable, Sendable { + public let sourceCommit: String + /// "clean", "dirty", or "unknown" when no git metadata is available. + public let sourceTreeState: String + public let swiftVersion: String + public let osVersion: String + public let osBuild: String + public let architecture: String + public let keyboardTypes: [UInt32] + public let dictionaryMode: String + public let profile: String + public let layer: String + public let corpusVersion: String + public let corpusManifestSha256: String + public let tablesVersion: String + public let detectorThresholds: [String: Int] + public let personalExceptions: Int + public let comparisonStatus: String +} + +public struct RunnerCheck: Codable, Equatable, Sendable { + public let passed: Int + public let failed: Int + public let failures: [String] + public let log: String +} + +public struct BaselineRecord: Codable, Equatable, Sendable { + public let schemaVersion: Int + public let baselineVersion: String + public let created: String + public let metadata: RunMetadata + public let runnerChecks: [String: RunnerCheck] + public let development: BenchmarkScoreReport + public let holdout: BenchmarkScoreReport + public let combined: BenchmarkScoreReport +} + +public enum BenchmarkEnvironment { + static func run(_ launchPath: String, _ arguments: [String]) -> String? { + let process = Process() + process.executableURL = URL(fileURLWithPath: launchPath) + process.arguments = arguments + let pipe = Pipe() + process.standardOutput = pipe + process.standardError = Pipe() + do { + try process.run() + } catch { + return nil + } + let data = pipe.fileHandleForReading.readDataToEndOfFile() + process.waitUntilExit() + guard process.terminationStatus == 0 else { return nil } + return String(data: data, encoding: .utf8)?.trimmingCharacters(in: .whitespacesAndNewlines) + } + + /// Commit and tree state of the sources that build the measured binary. + public static func sourceState(commitOverride: String?) -> (commit: String, state: String) { + guard let head = run("/usr/bin/git", ["rev-parse", "HEAD"]) else { + return (commitOverride ?? "unknown", "unknown") + } + let status = run("/usr/bin/git", ["status", "--porcelain", "--untracked-files=all", "--", "Sources", "Package.swift", "Benchmarks/layout-corpus", "Benchmarks/layout-tables"]) ?? "?" + return (commitOverride ?? head, status.isEmpty ? "clean" : "dirty") + } + + public static func swiftVersion() -> String { + run("/usr/bin/xcrun", ["swift", "--version"])?.components(separatedBy: "\n").first ?? "unknown" + } + + public static func architecture() -> String { + var sysinfo = utsname() + uname(&sysinfo) + return withUnsafePointer(to: &sysinfo.machine) { + $0.withMemoryRebound(to: CChar.self, capacity: 1) { String(cString: $0) } + } + } + + public static func detectorThresholds() -> [String: Int] { + let detector = LayoutDetector() + return [ + "detectionThreshold": detector.detectionThreshold, + "consecutiveThreshold": detector.consecutiveThreshold, + "lowConfidenceMaxLength": detector.lowConfidenceMaxLength, + "lowConfidenceConfirmations": detector.lowConfidenceConfirmations, + "shortWordSuppressionLength": detector.shortWordSuppressionLength, + "shortWordSuppressionMinValidContext": detector.shortWordSuppressionMinValidContext, + "shortWordSuppressionContextWindow": detector.shortWordSuppressionContextWindow + ] + } + + public static func metadata( + corpusDir: URL, + trials: [Trial], + profile: String, + layer: BenchmarkLayer, + commitOverride: String? + ) -> RunMetadata { + let manifestURL = corpusDir.appendingPathComponent("manifest.json") + let manifest = (try? Data(contentsOf: manifestURL)).flatMap { try? JSONDecoder().decode(Manifest.self, from: $0) } + let source = sourceState(commitOverride: commitOverride) + return RunMetadata( + sourceCommit: source.commit, + sourceTreeState: source.state, + swiftVersion: swiftVersion(), + osVersion: TableExporter.osVersionString(), + osBuild: TableExporter.osBuildVersion(), + architecture: architecture(), + keyboardTypes: Set(trials.map { $0.initial.keyboardType ?? 91 }).sorted(), + dictionaryMode: "text-fallback-test", + profile: profile, + layer: layer.rawValue, + corpusVersion: manifest?.corpusVersion ?? corpusDir.lastPathComponent, + corpusManifestSha256: (try? ManifestGenerator.sha256(for: manifestURL)) ?? "missing", + tablesVersion: manifest?.tablesVersion ?? "unknown", + detectorThresholds: detectorThresholds(), + personalExceptions: 0, + comparisonStatus: "not_run" + ) + } + + /// Parses a runner log ("Results: N passed, M failed" or "Input pipeline: N passed, M failed"). + public static func runnerCheck(logPath: String) -> RunnerCheck? { + guard let content = try? String(contentsOfFile: logPath, encoding: .utf8) else { return nil } + var passed: Int? + var failed: Int? + var failures: [String] = [] + let pattern = try? NSRegularExpression(pattern: "(\\d+) passed, (\\d+) failed") + for line in content.components(separatedBy: .newlines) { + let trimmed = line.trimmingCharacters(in: .whitespaces) + if trimmed.hasPrefix("FAIL") { + failures.append(trimmed) + } + let range = NSRange(trimmed.startIndex..., in: trimmed) + if let match = pattern?.firstMatch(in: trimmed, range: range), + let p = Range(match.range(at: 1), in: trimmed), + let f = Range(match.range(at: 2), in: trimmed) { + passed = Int(trimmed[p]) + failed = Int(trimmed[f]) + } + } + guard let passed, let failed else { return nil } + return RunnerCheck(passed: passed, failed: failed, failures: failures, log: logPath) + } +} + +/// Per-trial regression gate against a recorded baseline of the same corpus version. +public enum BaselineGate { + public struct Finding: Equatable, Sendable { + public let trialID: String + public let baseline: TrialOutcome + public let current: TrialOutcome? + } + + public static func regressions(current: BenchmarkScoreReport, baseline: BaselineRecord, split: String = "all") -> [Finding] { + let baselineOutcomes: [String: TrialOutcome] + switch split { + case "development": baselineOutcomes = baseline.development.trialOutcomes + case "holdout": baselineOutcomes = baseline.holdout.trialOutcomes + case "all": baselineOutcomes = baseline.development.trialOutcomes.merging(baseline.holdout.trialOutcomes) { first, _ in first } + default: preconditionFailure("Unknown baseline split: \(split)") + } + return Set(current.trialOutcomes.keys).union(baselineOutcomes.keys).sorted().compactMap { id in + guard let now = current.trialOutcomes[id] else { + return Finding(trialID: id, baseline: baselineOutcomes[id]!, current: nil) + } + guard let before = baselineOutcomes[id] else { + return now.isPassing ? nil : Finding(trialID: id, baseline: .unsupported, current: now) + } + let lostPass = before.isPassing && !now.isPassing + let lostCoverage = before != .unsupported && now == .unsupported + return lostPass || lostCoverage ? Finding(trialID: id, baseline: before, current: now) : nil + } + } +} diff --git a/Sources/LayoutBenchmark/Corpus.swift b/Sources/LayoutBenchmark/Corpus.swift new file mode 100644 index 0000000..c2ef03f --- /dev/null +++ b/Sources/LayoutBenchmark/Corpus.swift @@ -0,0 +1,290 @@ +import Foundation + +public struct StrokeOutput: Codable, Equatable, Sendable { + public let output: String? + public let rejection: String? + + public init(output: String?, rejection: String? = nil) { + self.output = output + self.rejection = rejection + } + + public var isValid: Bool { + return output != nil && rejection == nil + } +} + +public struct KeyCodeOutputs: Codable, Equatable, Sendable { + public let keyCode: UInt16 + public let unmodified: StrokeOutput + public let shift: StrokeOutput + public let caps: StrokeOutput + public let shiftCaps: StrokeOutput + public let option: StrokeOutput + public let shiftOption: StrokeOutput + public let capsOption: StrokeOutput + public let shiftCapsOption: StrokeOutput + + public init( + keyCode: UInt16, + unmodified: StrokeOutput, + shift: StrokeOutput, + caps: StrokeOutput, + shiftCaps: StrokeOutput, + option: StrokeOutput, + shiftOption: StrokeOutput, + capsOption: StrokeOutput, + shiftCapsOption: StrokeOutput + ) { + self.keyCode = keyCode + self.unmodified = unmodified + self.shift = shift + self.caps = caps + self.shiftCaps = shiftCaps + self.option = option + self.shiftOption = shiftOption + self.capsOption = capsOption + self.shiftCapsOption = shiftCapsOption + } + + public func output(for modifiers: [String]) -> StrokeOutput { + let hasShift = modifiers.contains("shift") + let hasCaps = modifiers.contains("caps") + let hasOption = modifiers.contains("option") + + switch (hasShift, hasCaps, hasOption) { + case (false, false, false): return unmodified + case (true, false, false): return shift + case (false, true, false): return caps + case (true, true, false): return shiftCaps + case (false, false, true): return option + case (true, false, true): return shiftOption + case (false, true, true): return capsOption + case (true, true, true): return shiftCapsOption + } + } +} + +public struct LayoutTable: Codable, Equatable, Sendable { + public let schemaVersion: Int + public let sourceID: String + public let keyboardType: UInt32 + public let osVersion: String + public let osBuild: String + public let keyOutputs: [String: KeyCodeOutputs] + + public init( + schemaVersion: Int = 1, + sourceID: String, + keyboardType: UInt32, + osVersion: String, + osBuild: String, + keyOutputs: [String: KeyCodeOutputs] + ) { + self.schemaVersion = schemaVersion + self.sourceID = sourceID + self.keyboardType = keyboardType + self.osVersion = osVersion + self.osBuild = osBuild + self.keyOutputs = keyOutputs + } + + public func lookup(keyCode: UInt16, modifiers: [String]) -> StrokeOutput? { + guard let entry = keyOutputs[String(keyCode)] else { return nil } + return entry.output(for: modifiers) + } + + public static func load(from url: URL) throws -> LayoutTable { + let data = try Data(contentsOf: url) + let decoder = JSONDecoder() + return try decoder.decode(LayoutTable.self, from: data) + } +} + +public struct Provenance: Codable, Equatable, Sendable { + public let kind: String + public let generator: String + public let seed: Int + public let review: String + + public init(kind: String = "synthetic", generator: String = "manual-v1", seed: Int = 0, review: String = "pending-human") { + self.kind = kind + self.generator = generator + self.seed = seed + self.review = review + } +} + +public struct InitialState: Codable, Equatable, Sendable { + public let sourceID: String + public let language: String + public let text: String + public let caretUTF16: Int + public let preferredCyrillic: String? + public let keyboardType: UInt32? + + public init( + sourceID: String, + language: String, + text: String = "", + caretUTF16: Int = 0, + preferredCyrillic: String? = nil, + keyboardType: UInt32? = nil + ) { + self.sourceID = sourceID + self.language = language + self.text = text + self.caretUTF16 = caretUTF16 + self.preferredCyrillic = preferredCyrillic + self.keyboardType = keyboardType + } +} + +public struct TrialEvent: Codable, Equatable, Sendable { + public let type: String + public let keyCode: UInt16 + public let modifiers: [String] + public let rendered: String + + public init(type: String, keyCode: UInt16, modifiers: [String] = [], rendered: String) { + self.type = type + self.keyCode = keyCode + self.modifiers = modifiers + self.rendered = rendered + } +} + +public struct Checkpoint: Codable, Equatable, Sendable { + public let afterEvent: Int + public let text: String + public let language: String + + public init(afterEvent: Int, text: String, language: String) { + self.afterEvent = afterEvent + self.text = text + self.language = language + } +} + +public struct ExpectedOutcome: Codable, Equatable, Sendable { + public let text: String + public let language: String + public let sourceID: String + public let allowedAutomaticActions: [String] + public let checkpoints: [Checkpoint] + + public init( + text: String, + language: String, + sourceID: String, + allowedAutomaticActions: [String] = [], + checkpoints: [Checkpoint] = [] + ) { + self.text = text + self.language = language + self.sourceID = sourceID + self.allowedAutomaticActions = allowedAutomaticActions + self.checkpoints = checkpoints + } +} + +public struct Trial: Codable, Equatable, Sendable { + public let schemaVersion: Int + public let id: String + public let groupID: String + public let split: String + public let languages: [String] + public let category: String + public let label: String + public let provenance: Provenance + public let initial: InitialState + public let events: [TrialEvent] + public let expected: ExpectedOutcome + + public init( + schemaVersion: Int = 1, + id: String, + groupID: String, + split: String, + languages: [String], + category: String, + label: String, + provenance: Provenance = Provenance(), + initial: InitialState, + events: [TrialEvent], + expected: ExpectedOutcome + ) { + self.schemaVersion = schemaVersion + self.id = id + self.groupID = groupID + self.split = split + self.languages = languages + self.category = category + self.label = label + self.provenance = provenance + self.initial = initial + self.events = events + self.expected = expected + } +} + +public struct FileChecksum: Codable, Equatable, Sendable { + public let sha256: String + public let trialsCount: Int? + + public init(sha256: String, trialsCount: Int? = nil) { + self.sha256 = sha256 + self.trialsCount = trialsCount + } +} + +public struct SplitSummary: Codable, Equatable, Sendable { + public let repair: Int + public let preserve: Int + public let abstain: Int + public let total: Int + + public init(repair: Int, preserve: Int, abstain: Int, total: Int) { + self.repair = repair + self.preserve = preserve + self.abstain = abstain + self.total = total + } +} + +public struct Manifest: Codable, Equatable, Sendable { + public let schemaVersion: Int + public let corpusVersion: String + public let tablesVersion: String + public let created: String + public let osVersion: String + public let osBuild: String + public let keyboardType: UInt32 + public let sourceCommit: String + public let files: [String: FileChecksum] + public let splits: [String: SplitSummary] + + public init( + schemaVersion: Int = 1, + corpusVersion: String = "v1", + tablesVersion: String = "v1", + created: String, + osVersion: String, + osBuild: String, + keyboardType: UInt32, + sourceCommit: String, + files: [String: FileChecksum], + splits: [String: SplitSummary] + ) { + self.schemaVersion = schemaVersion + self.corpusVersion = corpusVersion + self.tablesVersion = tablesVersion + self.created = created + self.osVersion = osVersion + self.osBuild = osBuild + self.keyboardType = keyboardType + self.sourceCommit = sourceCommit + self.files = files + self.splits = splits + } +} diff --git a/Sources/LayoutBenchmark/CorpusValidator.swift b/Sources/LayoutBenchmark/CorpusValidator.swift new file mode 100644 index 0000000..d1814dc --- /dev/null +++ b/Sources/LayoutBenchmark/CorpusValidator.swift @@ -0,0 +1,275 @@ +import Foundation + +public struct ValidationResult: Sendable { + public let passed: Bool + public let trialsCount: Int + public let splitsCount: Int + public let errors: [String] + /// Problems a frozen version may carry but a new version must not (`validate --strict`). + public let warnings: [String] + + public init(passed: Bool, trialsCount: Int, splitsCount: Int, errors: [String], warnings: [String] = []) { + self.passed = passed + self.trialsCount = trialsCount + self.splitsCount = splitsCount + self.errors = errors + self.warnings = warnings + } +} + +public enum CorpusValidator { + public static let supportedEventTypes: Set = ["key", "boundary", "delete"] + public static let reviewStates: Set = ["pending-human", "reviewed"] + + public static func validate(corpusDirectory: URL, tablesDirectory: URL) throws -> ValidationResult { + var errors: [String] = [] + var warnings: [String] = [] + var loadedTables: [String: LayoutTable] = [:] + + func getTable(sourceID: String, keyboardType: UInt32) -> LayoutTable? { + let key = "\(sourceID)@\(keyboardType)" + if let existing = loadedTables[key] { return existing } + // Exact keyboard type only: another type's table is not evidence for this one. + guard let table = try? LayoutTable.load(from: tablesDirectory.appendingPathComponent("\(key).json")) else { + return nil + } + loadedTables[key] = table + return table + } + + let devURL = corpusDirectory.appendingPathComponent("development.jsonl") + let holdoutURL = corpusDirectory.appendingPathComponent("holdout.jsonl") + + guard FileManager.default.fileExists(atPath: devURL.path) else { + return ValidationResult(passed: false, trialsCount: 0, splitsCount: 0, errors: ["Missing development.jsonl at \(devURL.path)"]) + } + guard FileManager.default.fileExists(atPath: holdoutURL.path) else { + return ValidationResult(passed: false, trialsCount: 0, splitsCount: 0, errors: ["Missing holdout.jsonl at \(holdoutURL.path)"]) + } + + errors += verifyManifest(corpusDirectory: corpusDirectory, tablesDirectory: tablesDirectory) + + let devTrials = try loadJSONL(from: devURL) + let holdoutTrials = try loadJSONL(from: holdoutURL) + + var seenIDs: Set = [] + var devGroups: Set = [] + var holdoutGroups: Set = [] + // Identical input (source, initial text, keys) must carry one label everywhere. + var labelsByInput: [String: [(id: String, split: String, label: String)]] = [:] + var splitsByBaseText: [String: [(id: String, split: String)]] = [:] + + func validateTrial(_ trial: Trial, expectedSplit: String, file: String, lineNum: Int) { + let prefix = "[\(file):\(lineNum) id=\(trial.id)]" + + if trial.schemaVersion != 1 { + errors.append("\(prefix) Unknown schemaVersion \(trial.schemaVersion)") + } + + if seenIDs.contains(trial.id) { + errors.append("\(prefix) Duplicate trial ID: \(trial.id)") + } + seenIDs.insert(trial.id) + + if trial.split != expectedSplit { + errors.append("\(prefix) Trial split '\(trial.split)' does not match expected file split '\(expectedSplit)'") + } + + if !reviewStates.contains(trial.provenance.review) { + errors.append("\(prefix) Provenance review must be one of \(reviewStates.sorted()), got '\(trial.provenance.review)'") + } + + let validLabels = ["repair", "preserve", "abstain"] + if !validLabels.contains(trial.label) { + errors.append("\(prefix) Invalid label: \(trial.label)") + } + + let initialText = trial.initial.text + let caret = trial.initial.caretUTF16 + let characterBoundaries = Set(initialText.indices.map { $0.utf16Offset(in: initialText) }).union([initialText.utf16.count]) + if caret < 0 || caret > initialText.utf16.count { + errors.append("\(prefix) caretUTF16 \(caret) outside 0...\(initialText.utf16.count)") + } else if !characterBoundaries.contains(caret) { + errors.append("\(prefix) caretUTF16 \(caret) is not on a character boundary") + } else if caret != initialText.utf16.count { + errors.append("\(prefix) caretUTF16 \(caret) must be at end of initial text (\(initialText.utf16.count))") + } + + var editor = VirtualEditor(text: initialText) + for event in trial.events { + switch event.type { + case "key", "boundary": editor.type(event.rendered) + case "delete": editor.deleteBackward() + default: errors.append("\(prefix) Unsupported event type '\(event.type)'") + } + } + let typedText = editor.text + + switch trial.label { + case "repair": + if trial.expected.allowedAutomaticActions.isEmpty { + errors.append("\(prefix) Repair label must have non-empty allowedAutomaticActions") + } + if trial.expected.text == typedText && trial.expected.sourceID == trial.initial.sourceID { + errors.append("\(prefix) Repair label must specify a text change or source change compared to typed text") + } + case "preserve", "abstain": + if !trial.expected.allowedAutomaticActions.isEmpty { + errors.append("\(prefix) Label '\(trial.label)' must have empty allowedAutomaticActions, got \(trial.expected.allowedAutomaticActions)") + } + if trial.expected.text != typedText { + errors.append("\(prefix) Label '\(trial.label)' expected text '\(trial.expected.text)' does not match typed text '\(typedText)'") + } + if trial.expected.sourceID != trial.initial.sourceID { + errors.append("\(prefix) Label '\(trial.label)' expected sourceID '\(trial.expected.sourceID)' does not match initial sourceID '\(trial.initial.sourceID)'") + } + default: + break + } + + for cp in trial.expected.checkpoints { + if cp.afterEvent > trial.events.count || cp.afterEvent < 0 { + errors.append("\(prefix) Checkpoint afterEvent \(cp.afterEvent) out of range 0...\(trial.events.count)") + } + } + + let inputKey = ([trial.initial.sourceID, initialText] + trial.events.map { "\($0.type):\($0.keyCode):\($0.modifiers.sorted().joined(separator: "+"))" }) + .joined(separator: "\u{1F}") + labelsByInput[inputKey, default: []].append((trial.id, trial.split, trial.label)) + let baseText = trial.expected.text.trimmingCharacters(in: .whitespacesAndNewlines).lowercased() + splitsByBaseText[baseText, default: []].append((trial.id, trial.split)) + + let kbdType = trial.initial.keyboardType ?? 91 + guard let table = getTable(sourceID: trial.initial.sourceID, keyboardType: kbdType) else { + errors.append("\(prefix) Missing layout table for \(trial.initial.sourceID)@\(kbdType)") + return + } + + for (idx, event) in trial.events.enumerated() where event.type != "delete" { + if event.keyCode >= 128 { + errors.append("\(prefix) Event \(idx) keyCode \(event.keyCode) >= 128") + continue + } + guard let strokeOutput = table.lookup(keyCode: event.keyCode, modifiers: event.modifiers) else { + errors.append("\(prefix) Event \(idx) key \(event.keyCode) modifiers \(event.modifiers) missing in table") + continue + } + if let expectedOutput = strokeOutput.output { + if event.rendered != expectedOutput { + errors.append("\(prefix) Event \(idx) rendered '\(event.rendered)' does not match table output '\(expectedOutput)' for key \(event.keyCode) modifiers \(event.modifiers)") + } + } else { + errors.append("\(prefix) Event \(idx) key \(event.keyCode) modifiers \(event.modifiers) is rejected by table: \(strokeOutput.rejection ?? "unknown") but event rendered '\(event.rendered)'") + } + } + } + + for (idx, trial) in devTrials.enumerated() { + devGroups.insert(trial.groupID) + validateTrial(trial, expectedSplit: "development", file: "development.jsonl", lineNum: idx + 1) + } + + for (idx, trial) in holdoutTrials.enumerated() { + holdoutGroups.insert(trial.groupID) + validateTrial(trial, expectedSplit: "holdout", file: "holdout.jsonl", lineNum: idx + 1) + } + + let groupOverlap = devGroups.intersection(holdoutGroups) + if !groupOverlap.isEmpty { + errors.append("Leaking groups between development and holdout: \(groupOverlap.sorted())") + } + + for entries in labelsByInput.values where entries.count > 1 { + let ids = entries.map(\.id).joined(separator: ", ") + if Set(entries.map(\.label)).count > 1 { + warnings.append("Contradictory labels for identical input: \(entries.map { "\($0.id)=\($0.label)" }.joined(separator: ", "))") + } + if Set(entries.map(\.split)).count > 1 { + warnings.append("Identical typed input in both splits: \(ids)") + } + } + for (text, entries) in splitsByBaseText where Set(entries.map(\.split)).count > 1 { + warnings.append("Base text '\(text)' in both splits: \(entries.map(\.id).joined(separator: ", "))") + } + + let totalTrials = devTrials.count + holdoutTrials.count + return ValidationResult( + passed: errors.isEmpty, + trialsCount: totalTrials, + splitsCount: 2, + errors: errors, + warnings: warnings.sorted() + ) + } + + /// Resolve manifest version prefixes against the actual supplied input directories, + /// not an inferred repository root. Every consumed table must be listed and hashed. + static func verifyManifest(corpusDirectory: URL, tablesDirectory: URL) -> [String] { + let manifestURL = corpusDirectory.appendingPathComponent("manifest.json") + guard let data = try? Data(contentsOf: manifestURL) else { + return ["Missing manifest.json at \(manifestURL.path)"] + } + guard let manifest = try? JSONDecoder().decode(Manifest.self, from: data) else { + return ["Unreadable manifest.json at \(manifestURL.path)"] + } + let corpusPrefix = "layout-corpus/\(manifest.corpusVersion)/" + let tablesPrefix = "layout-tables/\(manifest.tablesVersion)/" + var errors: [String] = [] + for name in ["development.jsonl", "holdout.jsonl"] where manifest.files[corpusPrefix + name] == nil { + errors.append("Manifest does not list corpus file \(name)") + } + let tableFiles = (try? FileManager.default.contentsOfDirectory(at: tablesDirectory, includingPropertiesForKeys: nil)) ?? [] + for file in tableFiles where file.pathExtension == "json" && manifest.files[tablesPrefix + file.lastPathComponent] == nil { + errors.append("Manifest does not list table file \(file.lastPathComponent)") + } + for (path, checksum) in manifest.files.sorted(by: { $0.key < $1.key }) { + let directory: URL + let name: String + if path.hasPrefix(corpusPrefix) { + directory = corpusDirectory + name = String(path.dropFirst(corpusPrefix.count)) + } else if path.hasPrefix(tablesPrefix) { + directory = tablesDirectory + name = String(path.dropFirst(tablesPrefix.count)) + } else { + errors.append("Unexpected manifest path \(path)") + continue + } + guard !name.isEmpty, !name.contains("/"), name != ".." else { + errors.append("Invalid manifest path \(path)") + continue + } + guard let actual = try? ManifestGenerator.sha256(for: directory.appendingPathComponent(name)) else { + errors.append("Manifest lists missing file \(path)") + continue + } + if actual != checksum.sha256 { + errors.append("Manifest hash mismatch for \(path): recorded \(checksum.sha256.prefix(12)), actual \(actual.prefix(12))") + } + } + return errors + } + + /// Checks the exact inputs consumed by run and record-baseline, without promoting + /// frozen-corpus quality warnings to errors. + static func requireVerifiedInputs(corpusDirectory: URL, tablesDirectory: URL) throws { + let errors = verifyManifest(corpusDirectory: corpusDirectory, tablesDirectory: tablesDirectory) + guard errors.isEmpty else { + throw NSError(domain: "LayoutBenchmark", code: 1, userInfo: [NSLocalizedDescriptionKey: errors.joined(separator: "\n")]) + } + } + + private static func loadJSONL(from url: URL) throws -> [Trial] { + let content = try String(contentsOf: url, encoding: .utf8) + let decoder = JSONDecoder() + var trials: [Trial] = [] + for line in content.components(separatedBy: .newlines) { + let trimmed = line.trimmingCharacters(in: .whitespacesAndNewlines) + if trimmed.isEmpty { continue } + guard let data = trimmed.data(using: .utf8) else { continue } + let trial = try decoder.decode(Trial.self, from: data) + trials.append(trial) + } + return trials + } +} diff --git a/Sources/LayoutBenchmark/ManifestGenerator.swift b/Sources/LayoutBenchmark/ManifestGenerator.swift new file mode 100644 index 0000000..6625c22 --- /dev/null +++ b/Sources/LayoutBenchmark/ManifestGenerator.swift @@ -0,0 +1,97 @@ +import Foundation +import CryptoKit + +public enum ManifestGenerator { + public static func sha256(for fileURL: URL) throws -> String { + let data = try Data(contentsOf: fileURL) + return SHA256.hash(data: data).map { String(format: "%02x", $0) }.joined() + } + + /// Writes one corpus version byte-deterministically: sorted JSON keys, fixed dates and the + /// definition order, so regenerating a version reproduces its manifest hashes. + public static func generateSeedCorpusAndManifest( + corpusDir: URL, + tablesDir: URL, + sourceCommit: String, + definitions: SeedCorpusVersion + ) throws { + let fileManager = FileManager.default + try fileManager.createDirectory(at: corpusDir, withIntermediateDirectories: true) + + let abcURL = tablesDir.appendingPathComponent("com.apple.keylayout.ABC@91.json") + let ukURL = tablesDir.appendingPathComponent("com.apple.keylayout.Ukrainian@91.json") + let abcTable = try LayoutTable.load(from: abcURL) + let ukTable = try LayoutTable.load(from: ukURL) + + func trials(_ items: [SeedItemDefinition], split: String, prefix: String) throws -> [Trial] { + try items.enumerated().map { idx, item in + try SeedCorpusGenerator.generateTrial( + id: item.id ?? String(format: "%@-%@-%03d", prefix, item.label, idx + 1), + item: item, + split: split, + generator: definitions.generator, + abcTable: abcTable, + ukTable: ukTable + ) + } + } + let devTrials = try trials(definitions.developmentItems, split: "development", prefix: "dev") + let holdoutTrials = try trials(definitions.holdoutItems, split: "holdout", prefix: "holdout") + + let encoder = JSONEncoder() + encoder.outputFormatting = [.sortedKeys, .withoutEscapingSlashes] + func writeJSONL(_ list: [Trial], to url: URL) throws { + let lines = try list.map { String(decoding: try encoder.encode($0), as: UTF8.self) } + try Data((lines.joined(separator: "\n") + "\n").utf8).write(to: url) + } + + let devURL = corpusDir.appendingPathComponent("development.jsonl") + let holdoutURL = corpusDir.appendingPathComponent("holdout.jsonl") + try writeJSONL(devTrials, to: devURL) + try writeJSONL(holdoutTrials, to: holdoutURL) + + func summary(_ list: [Trial]) -> SplitSummary { + SplitSummary( + repair: list.filter { $0.label == "repair" }.count, + preserve: list.filter { $0.label == "preserve" }.count, + abstain: list.filter { $0.label == "abstain" }.count, + total: list.count + ) + } + let summaries = ["development": summary(devTrials), "holdout": summary(holdoutTrials)] + + let readmeURL = corpusDir.appendingPathComponent("README.md") + try Data((definitions.readme(sourceCommit, summaries) + "\n").utf8).write(to: readmeURL) + + let corpusPrefix = "layout-corpus/\(definitions.version)" + var filesMap: [String: FileChecksum] = [:] + filesMap["\(corpusPrefix)/development.jsonl"] = FileChecksum(sha256: try sha256(for: devURL), trialsCount: devTrials.count) + filesMap["\(corpusPrefix)/holdout.jsonl"] = FileChecksum(sha256: try sha256(for: holdoutURL), trialsCount: holdoutTrials.count) + filesMap["\(corpusPrefix)/README.md"] = FileChecksum(sha256: try sha256(for: readmeURL), trialsCount: nil) + + let tablesVersion = tablesDir.lastPathComponent + let tableFiles = try fileManager.contentsOfDirectory(at: tablesDir, includingPropertiesForKeys: nil) + .filter { $0.pathExtension == "json" } + .sorted { $0.lastPathComponent < $1.lastPathComponent } + for tURL in tableFiles { + filesMap["layout-tables/\(tablesVersion)/\(tURL.lastPathComponent)"] = FileChecksum(sha256: try sha256(for: tURL), trialsCount: nil) + } + + let manifest = Manifest( + schemaVersion: 1, + corpusVersion: definitions.version, + tablesVersion: tablesVersion, + created: definitions.created, + osVersion: abcTable.osVersion, + osBuild: abcTable.osBuild, + keyboardType: abcTable.keyboardType, + sourceCommit: sourceCommit, + files: filesMap, + splits: summaries + ) + + let manifestEncoder = JSONEncoder() + manifestEncoder.outputFormatting = [.prettyPrinted, .sortedKeys] + try manifestEncoder.encode(manifest).write(to: corpusDir.appendingPathComponent("manifest.json")) + } +} diff --git a/Sources/LayoutBenchmark/Replay.swift b/Sources/LayoutBenchmark/Replay.swift new file mode 100644 index 0000000..94db694 --- /dev/null +++ b/Sources/LayoutBenchmark/Replay.swift @@ -0,0 +1,194 @@ +import Foundation +import Core + +/// Corpus language codes ("en", "uk", "ru") for detector layouts. +public func languageCode(for layout: Layout) -> String { + switch layout { + case .english: return "en" + case .ukrainian: return "uk" + case .russian: return "ru" + } +} + +public func layout(forLanguageCode code: String) -> Layout? { + switch code { + case "en": return .english + case "uk": return .ukrainian + case "ru": return .russian + default: return nil + } +} + +/// What the target application shows during replay. The caret is always at the end; trials +/// that start elsewhere are reported unsupported rather than approximated. +public struct VirtualEditor: Equatable, Sendable { + public struct ReplacementOutcome: Equatable, Sendable { + /// The screen ended with the detector's original span plus boundary. + public let suffixMatched: Bool + /// More deletions were requested than there were characters. + public let underflow: Bool + } + + public private(set) var text: String + + public init(text: String = "") { + self.text = text + } + + public mutating func type(_ value: String) { + text.append(value) + } + + public mutating func deleteBackward() { + if !text.isEmpty { + text.removeLast() + } + } + + /// Mirrors `TextCorrector`: `deleteCount` backspaces, then the replacement, whatever the + /// screen shows. A mismatch is reported, never hidden, so a damaging edit stays visible. + public mutating func replaceSuffix(deleteCount: Int, with replacement: String, expectedSuffix: String) -> ReplacementOutcome { + let matched = text.hasSuffix(expectedSuffix) + let available = text.count + text.removeLast(min(deleteCount, available)) + text.append(replacement) + return ReplacementOutcome(suffixMatched: matched, underflow: deleteCount > available) + } +} + +/// Outcome of rendering one physical key through a source's frozen table. +public enum RenderOutcome: Equatable, Sendable { + case rendered(String) + case unavailable(status: String, reason: String) +} + +/// Renders physical keys after an automatic switch through the new source's own table. +public struct KeyRenderer: Sendable { + public let tables: [String: LayoutTable] + + public init(tables: [String: LayoutTable]) { + self.tables = tables + } + + public func render(_ event: TrialEvent, sourceID: String, keyboardType: UInt32, initialSourceID: String) -> RenderOutcome { + // `rendered` is the validated oracle for the initial source. + if sourceID == initialSourceID { + return .rendered(event.rendered) + } + guard let table = tables["\(sourceID)@\(keyboardType)"] else { + return .unavailable(status: "error", reason: "missing_layout_table") + } + guard let stroke = table.lookup(keyCode: event.keyCode, modifiers: event.modifiers) else { + return .unavailable(status: "error", reason: "missing_key") + } + guard let output = stroke.output else { + return .unavailable(status: "unsupported", reason: "rejected_stroke_\(stroke.rejection ?? "unknown")") + } + if event.type == "boundary" && output != event.rendered { + return .unavailable(status: "unsupported", reason: "boundary_changed_after_switch") + } + return .rendered(output) + } +} + +/// Exposes a frozen table through the same protocol the app uses for live TIS sources, so the +/// app's own `InputSourceDiscoveryEngine` classifies it. Tables do not record TIS languages; +/// classification falls back to the source ID and key probes, as for a source without them. +struct TableInputSourceProvider: InputSourcePropertyProviding { + let table: LayoutTable + + var id: String { table.sourceID } + var localizedName: String { table.sourceID.components(separatedBy: ".").last ?? table.sourceID } + var inputSourceType: String? { "TISTypeKeyboardLayout" } + var languages: [String]? { nil } + + func translatedCharacter(keyCode: UInt16, modifierKeyState: UInt32) -> Character? { + let modifiers: [String] + switch modifierKeyState { + case 0x00: modifiers = [] + case 0x02: modifiers = ["shift"] + case 0x04: modifiers = ["caps"] + case 0x06: modifiers = ["shift", "caps"] + case 0x08: modifiers = ["option"] + case 0x0A: modifiers = ["shift", "option"] + case 0x0C: modifiers = ["caps", "option"] + case 0x0E: modifiers = ["shift", "caps", "option"] + default: return nil + } + guard let output = table.lookup(keyCode: keyCode, modifiers: modifiers)?.output, + output.count == 1 else { return nil } + return output.first + } +} + +/// The installed sources for one trial, taken only from the trial and the frozen tables. +public struct TrialSourceEnvironment: Sendable { + public static let defaultSourceIDs: [Layout: String] = [ + .english: "com.apple.keylayout.ABC", + .ukrainian: "com.apple.keylayout.Ukrainian" + ] + + public let keyboardType: UInt32 + public let descriptors: [String: DiscoveredInputSourceDescriptor] + public let preferredSourceIDs: [Layout: String] + public let allowedLayouts: Set + public let preferredCyrillicLayout: Layout? + + public static func make( + trial: Trial, + tables: [String: LayoutTable], + allowedLayouts: Set + ) -> Result { + let keyboardType = trial.initial.keyboardType ?? 91 + var preferred: [Layout: String] = [:] + for target in allowedLayouts { + let code = languageCode(for: target) + if trial.initial.language == code { + preferred[target] = trial.initial.sourceID + } else if trial.expected.language == code { + preferred[target] = trial.expected.sourceID + } else { + preferred[target] = defaultSourceIDs[target] + } + } + + var descriptors: [String: DiscoveredInputSourceDescriptor] = [:] + for sourceID in Set(preferred.values).union([trial.initial.sourceID]) { + guard let table = tables["\(sourceID)@\(keyboardType)"] else { + return .failure(AdapterFailure(status: "error", reason: "missing_layout_table")) + } + guard let descriptor = InputSourceDiscoveryEngine.classify(provider: TableInputSourceProvider(table: table)) else { + return .failure(AdapterFailure(status: "unsupported", reason: "unclassified_source")) + } + descriptors[sourceID] = descriptor + } + + return .success(TrialSourceEnvironment( + keyboardType: keyboardType, + descriptors: descriptors, + preferredSourceIDs: preferred, + allowedLayouts: allowedLayouts, + preferredCyrillicLayout: trial.initial.preferredCyrillic.flatMap(layout(forLanguageCode:)) + )) + } + + /// Mirrors `AppDelegate.updateDetectionConfiguration` for the given active source. + public func detectionConfiguration(currentSourceID: String, currentLayout: Layout) -> DetectionConfiguration { + let preferredVariant = preferredSourceIDs[.ukrainian].flatMap { descriptors[$0]?.ukrainianVariant } ?? .standard + let currentVariant = currentLayout == .ukrainian + ? descriptors[currentSourceID]?.ukrainianVariant ?? preferredVariant + : preferredVariant + return DetectionConfiguration( + allowedLayouts: allowedLayouts, + preferredCyrillicLayout: currentLayout.isCyrillic ? currentLayout : preferredCyrillicLayout, + activeSourceSupportedLayouts: descriptors[currentSourceID]?.supportedLayouts ?? [currentLayout], + ukrainianFromVariant: currentVariant, + ukrainianToVariant: preferredVariant + ) + } +} + +public struct AdapterFailure: Error, Equatable, Sendable { + public let status: String + public let reason: String +} diff --git a/Sources/LayoutBenchmark/Scoring.swift b/Sources/LayoutBenchmark/Scoring.swift new file mode 100644 index 0000000..11fc0bd --- /dev/null +++ b/Sources/LayoutBenchmark/Scoring.swift @@ -0,0 +1,350 @@ +import Foundation + +public struct WilsonScoreInterval: Codable, Equatable, Sendable { + public let lower: Double + public let upper: Double + + public init(lower: Double, upper: Double) { + self.lower = lower + self.upper = upper + } + + public static func compute(successes: Int, total: Int, z: Double = 1.95996) -> WilsonScoreInterval? { + guard total > 0, successes >= 0, successes <= total else { return nil } + let n = Double(total) + let k = Double(successes) + let z2 = z * z + let p = k / n + let denominator = 1.0 + z2 / n + let center = (p + z2 / (2.0 * n)) / denominator + let spread = (z * sqrt(p * (1.0 - p) / n + z2 / (4.0 * n * n))) / denominator + let lower = max(0.0, center - spread) + let upper = min(1.0, center + spread) + return WilsonScoreInterval(lower: lower, upper: upper) + } + + public var formatted: String { + let l = String(format: "%.1f%%", lower * 100) + let u = String(format: "%.1f%%", upper * 100) + return "[\(l), \(u)]" + } +} + +/// Per-trial outcome. Unsupported trials leave every quality denominator; errors and missing +/// observations stay in them as failures, so they can never count as correct preservation. +public enum TrialOutcome: String, Codable, Equatable, Sendable { + case truePositive = "TP" + case fnNoAction = "FN_no_action" + case fnWrongRepair = "FN_wrong_repair" + case fnWrongSource = "FN_wrong_source" + case fnUnsafeMutation = "FN_unsafe_mutation" + case fnTransientDamage = "FN_transient_damage" + case fnError = "FN_error" + case trueNegative = "TN" + case textDamageFP = "FP_text_damage" + case layoutOnlyFP = "FP_layout_only" + case negativeError = "NEG_error" + case unsupported = "unsupported" + + public var isPassing: Bool { + self == .truePositive || self == .trueNegative + } +} + +public struct ScoredMetric: Codable, Equatable, Sendable { + public let count: Int + public let total: Int + public let rate: Double? + public let interval: WilsonScoreInterval? + + public init(count: Int, total: Int, z: Double = 1.95996) { + self.count = count + self.total = total + if total > 0 { + self.rate = Double(count) / Double(total) + self.interval = WilsonScoreInterval.compute(successes: count, total: total, z: z) + } else { + self.rate = nil + self.interval = nil + } + } +} + +public struct OutcomeTally: Codable, Equatable, Sendable { + public var total = 0 + public var nPositive = 0 + public var nNegative = 0 + public var tp = 0 + public var fn = 0 + public var tn = 0 + public var textFP = 0 + public var layoutFP = 0 + public var errors = 0 + public var unsupported = 0 + + public var recall: Double? { nPositive > 0 ? Double(tp) / Double(nPositive) : nil } + public var textDamageFPRate: Double? { nNegative > 0 ? Double(textFP) / Double(nNegative) : nil } + + mutating func add(_ outcome: TrialOutcome, positive: Bool) { + total += 1 + if outcome == .unsupported { + unsupported += 1 + return + } + if positive { nPositive += 1 } else { nNegative += 1 } + switch outcome { + case .truePositive: tp += 1 + case .fnError, .negativeError: errors += 1; if positive { fn += 1 } + case .fnNoAction, .fnWrongRepair, .fnWrongSource, .fnUnsafeMutation, .fnTransientDamage: fn += 1 + case .trueNegative: tn += 1 + case .textDamageFP: textFP += 1 + case .layoutOnlyFP: layoutFP += 1 + case .unsupported: break + } + } +} + +public struct BenchmarkScoreReport: Codable, Equatable, Sendable { + public let schemaVersion: Int + /// Trials requested, including unsupported ones. + public let totalTrials: Int + public let groupCount: Int + /// No error or missing observation; a quality gate requires this. + public let complete: Bool + public let completedTrials: Int + public let unsupportedTrials: Int + public let errorTrials: Int + public let missingTrials: Int + public let coverage: ScoredMetric + + public let nPositive: Int + public let nNegative: Int + public let nPreserve: Int + public let nAbstain: Int + + public let truePositives: Int + public let fnNoAction: Int + public let fnWrongRepair: Int + public let fnWrongSource: Int + public let fnUnsafeMutation: Int + public let fnTransientDamage: Int + public let fnError: Int + public let falseNegatives: Int + public let trueNegatives: Int + public let textDamageFPs: Int + public let layoutOnlyFPs: Int + public let totalFPs: Int + /// Negative trials with any unauthorized source change, with or without text damage. + public let unauthorizedSourceChanges: Int + public let repairAttempts: Int + + public let recall: ScoredMetric + public let precision: ScoredMetric + public let textDamageFPRate: ScoredMetric + public let layoutOnlyFPRate: ScoredMetric + public let totalFPRate: ScoredMetric + public let unauthorizedSourceChangeRate: ScoredMetric + + public let directionBreakdown: [String: OutcomeTally] + public let categoryBreakdown: [String: OutcomeTally] + /// Diagnostic reason codes across all results (mapping rejections, cancellations, mismatches). + public let reasonCodeCounts: [String: Int] + public let trialOutcomes: [String: TrialOutcome] +} + +public enum Scorer { + public static func classify(trial: Trial, result: TrialResult?) -> TrialOutcome { + let positive = trial.label == "repair" + guard let result else { return positive ? .fnError : .negativeError } + switch result.status { + case "ok": break + case "unsupported": return .unsupported + default: return positive ? .fnError : .negativeError + } + + let observed = Dictionary(result.checkpoints.map { ($0.afterEvent, $0) }, uniquingKeysWith: { _, last in last }) + let checkpointTextMatches = trial.expected.checkpoints.allSatisfy { expected in + guard let seen = observed[expected.afterEvent] else { return false } + return seen.text == expected.text + } + let checkpointLanguageMatches = trial.expected.checkpoints.allSatisfy { expected in + guard let seen = observed[expected.afterEvent] else { return false } + return seen.language == expected.language + } + let checkpointsMatch = checkpointTextMatches && checkpointLanguageMatches + let prefixIntact = result.finalText.hasPrefix(trial.initial.text) + let textMatches = result.finalText == trial.expected.text + let sourceMatches = result.finalSourceID == trial.expected.sourceID + let replaced = result.actions.contains { $0.type == "replaceSuffix" } + let switched = result.actions.contains { $0.type == "switchSource" } || !sourceMatches + + if positive { + let allowed = Set(trial.expected.allowedAutomaticActions) + let actionsAllowed = result.actions.allSatisfy { allowed.contains($0.type) } + if textMatches && sourceMatches && checkpointsMatch && actionsAllowed && prefixIntact { + return .truePositive + } + if result.actions.isEmpty { return .fnNoAction } + if !actionsAllowed || !prefixIntact { return .fnUnsafeMutation } + if !textMatches { return .fnWrongRepair } + if !sourceMatches { return .fnWrongSource } + return .fnTransientDamage + } + + // Any replacement or checkpoint text mutation counts as text damage on a negative trial. + if replaced || !textMatches || !checkpointTextMatches || !prefixIntact { + return .textDamageFP + } + return (switched || !checkpointLanguageMatches) ? .layoutOnlyFP : .trueNegative + } + + public static func score(trials: [Trial], results: [TrialResult]) -> BenchmarkScoreReport { + var resultsMap: [String: TrialResult] = [:] + for r in results { + resultsMap[r.trialID] = r + } + + var all = OutcomeTally() + var nPreserve = 0 + var nAbstain = 0 + var completed = 0 + var missing = 0 + var unauthorizedSourceChanges = 0 + var attempts = 0 + var fnCounts: [TrialOutcome: Int] = [:] + var directions: [String: OutcomeTally] = [:] + var categories: [String: OutcomeTally] = [:] + var reasonCounts: [String: Int] = [:] + var outcomes: [String: TrialOutcome] = [:] + + for trial in trials { + let result = resultsMap[trial.id] + if result == nil { missing += 1 } + if result?.status == "ok" { completed += 1 } + for code in result?.reasonCodes ?? [] { + reasonCounts[code, default: 0] += 1 + } + + let positive = trial.label == "repair" + let outcome = classify(trial: trial, result: result) + outcomes[trial.id] = outcome + all.add(outcome, positive: positive) + fnCounts[outcome, default: 0] += 1 + + let direction = trial.initial.language == "en" ? "EN -> UK" : "UK -> EN" + directions[direction, default: OutcomeTally()].add(outcome, positive: positive) + categories[trial.category, default: OutcomeTally()].add(outcome, positive: positive) + + guard outcome != .unsupported else { continue } + if !positive { + if trial.label == "preserve" { nPreserve += 1 } else { nAbstain += 1 } + if let result, result.status == "ok", + result.actions.contains(where: { $0.type == "switchSource" }) || result.finalSourceID != trial.expected.sourceID { + unauthorizedSourceChanges += 1 + } + } + if positive, let result, result.status == "ok", !result.actions.isEmpty { + attempts += 1 + } + } + + let totalFPs = all.textFP + all.layoutFP + attempts += totalFPs + + return BenchmarkScoreReport( + schemaVersion: 2, + totalTrials: trials.count, + groupCount: Set(trials.map(\.groupID)).count, + complete: all.errors == 0 && missing == 0, + completedTrials: completed, + unsupportedTrials: all.unsupported, + errorTrials: all.errors - missing, + missingTrials: missing, + coverage: ScoredMetric(count: completed, total: trials.count), + nPositive: all.nPositive, + nNegative: all.nNegative, + nPreserve: nPreserve, + nAbstain: nAbstain, + truePositives: all.tp, + fnNoAction: fnCounts[.fnNoAction, default: 0], + fnWrongRepair: fnCounts[.fnWrongRepair, default: 0], + fnWrongSource: fnCounts[.fnWrongSource, default: 0], + fnUnsafeMutation: fnCounts[.fnUnsafeMutation, default: 0], + fnTransientDamage: fnCounts[.fnTransientDamage, default: 0], + fnError: fnCounts[.fnError, default: 0], + falseNegatives: all.fn, + trueNegatives: all.tn, + textDamageFPs: all.textFP, + layoutOnlyFPs: all.layoutFP, + totalFPs: totalFPs, + unauthorizedSourceChanges: unauthorizedSourceChanges, + repairAttempts: attempts, + recall: ScoredMetric(count: all.tp, total: all.nPositive), + precision: ScoredMetric(count: all.tp, total: attempts), + textDamageFPRate: ScoredMetric(count: all.textFP, total: all.nNegative), + layoutOnlyFPRate: ScoredMetric(count: all.layoutFP, total: all.nNegative), + totalFPRate: ScoredMetric(count: totalFPs, total: all.nNegative), + unauthorizedSourceChangeRate: ScoredMetric(count: unauthorizedSourceChanges, total: all.nNegative), + directionBreakdown: directions, + categoryBreakdown: categories, + reasonCodeCounts: reasonCounts, + trialOutcomes: outcomes + ) + } + + static func percent(_ value: Double?, digits: Int = 1) -> String { + value.map { String(format: "%.\(digits)f%%", $0 * 100) } ?? "N/A" + } + + static func metric(_ m: ScoredMetric, digits: Int = 1) -> String { + "\(m.count) / \(m.total) | \(percent(m.rate, digits: digits)) | \(m.interval?.formatted ?? "N/A")" + } + + public static func renderMarkdown(report: BenchmarkScoreReport, split: String, adapter: String) -> String { + var lines: [String] = [] + lines.append("## Layout Reliability Benchmark Report") + lines.append("") + lines.append("- **Adapter**: `\(adapter)`") + lines.append("- **Split**: `\(split)`") + lines.append("- **Status**: \(report.complete ? "complete" : "INCOMPLETE — errors or missing observations; no quality gate may pass")") + lines.append("- **Trials**: \(report.totalTrials) in \(report.groupCount) groups (\(report.nPositive) positive, \(report.nNegative) negative [\(report.nPreserve) preserve, \(report.nAbstain) abstain])") + lines.append("- **Coverage**: \(report.completedTrials) completed, \(report.unsupportedTrials) unsupported, \(report.errorTrials) errors, \(report.missingTrials) missing") + lines.append("") + lines.append("### Primary Metrics") + lines.append("") + lines.append("| Metric | Count / Denominator | Rate | Wilson 95% CI |") + lines.append("| --- | --- | --- | --- |") + lines.append("| **Recall** (TP / N_positive) | \(metric(report.recall)) |") + lines.append("| **Precision** (TP / trials with automatic attempts) | \(metric(report.precision)) |") + lines.append("| **Text-Damage FP** (damage / N_negative) | \(metric(report.textDamageFPRate, digits: 2)) |") + lines.append("| **Layout-Only FP** (switch without damage / N_negative) | \(metric(report.layoutOnlyFPRate, digits: 2)) |") + lines.append("| **Union Intervention FP** | \(metric(report.totalFPRate, digits: 2)) |") + lines.append("| **Any Unauthorized Source Change** | \(metric(report.unauthorizedSourceChangeRate, digits: 2)) |") + lines.append("") + lines.append("### Outcomes") + lines.append("") + lines.append("- **TP**: \(report.truePositives); **TN**: \(report.trueNegatives)") + lines.append("- **FN**: \(report.falseNegatives) (no action \(report.fnNoAction), wrong repair \(report.fnWrongRepair), wrong source \(report.fnWrongSource), unsafe mutation \(report.fnUnsafeMutation), transient damage or too late \(report.fnTransientDamage), error \(report.fnError))") + lines.append("- **FP**: text damage \(report.textDamageFPs), layout only \(report.layoutOnlyFPs)") + lines.append("") + lines.append("### Direction Breakdown") + lines.append("") + lines.append("| Direction | Trials | TP | FN | TN | Text FP | Layout FP | Errors | Unsupported | Recall | Text-Damage FP |") + lines.append("| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |") + for dir in report.directionBreakdown.keys.sorted() { + guard let d = report.directionBreakdown[dir] else { continue } + lines.append("| \(dir) | \(d.total) | \(d.tp) | \(d.fn) | \(d.tn) | \(d.textFP) | \(d.layoutFP) | \(d.errors) | \(d.unsupported) | \(percent(d.recall)) | \(percent(d.textDamageFPRate, digits: 2)) |") + } + if !report.reasonCodeCounts.isEmpty { + lines.append("") + lines.append("### Reason Codes") + lines.append("") + lines.append("| Code | Trials |") + lines.append("| --- | --- |") + for code in report.reasonCodeCounts.keys.sorted() { + lines.append("| `\(code)` | \(report.reasonCodeCounts[code] ?? 0) |") + } + } + return lines.joined(separator: "\n") + } +} diff --git a/Sources/LayoutBenchmark/SeedCorpusDefinitions.swift b/Sources/LayoutBenchmark/SeedCorpusDefinitions.swift new file mode 100644 index 0000000..0e56460 --- /dev/null +++ b/Sources/LayoutBenchmark/SeedCorpusDefinitions.swift @@ -0,0 +1,376 @@ +import Foundation + +public enum SeedCorpusDefinitions { + public static let developmentItems: [SeedItemDefinition] = [ + // 20 Repairs (10 EN->UK, 10 UK->EN) + SeedItemDefinition(groupID: "dev-grp-001", category: "single-word", label: "repair", sourceLayout: "en", targetLayout: "uk", intendedText: "привіт ", isRepair: true), + SeedItemDefinition(groupID: "dev-grp-002", category: "single-word", label: "repair", sourceLayout: "en", targetLayout: "uk", intendedText: "робота ", isRepair: true), + SeedItemDefinition(groupID: "dev-grp-003", category: "single-word", label: "repair", sourceLayout: "en", targetLayout: "uk", intendedText: "людина ", isRepair: true), + SeedItemDefinition(groupID: "dev-grp-004", category: "single-word", label: "repair", sourceLayout: "en", targetLayout: "uk", intendedText: "дякую ", isRepair: true), + SeedItemDefinition(groupID: "dev-grp-005", category: "capitalized", label: "repair", sourceLayout: "en", targetLayout: "uk", intendedText: "Київ ", isRepair: true), + SeedItemDefinition(groupID: "dev-grp-006", category: "back-to-back", label: "repair", sourceLayout: "en", targetLayout: "uk", intendedText: "добрий день ", isRepair: true), + SeedItemDefinition(groupID: "dev-grp-007", category: "single-word", label: "repair", sourceLayout: "en", targetLayout: "uk", intendedText: "дивимось ", isRepair: true), + SeedItemDefinition(groupID: "dev-grp-008", category: "single-word", label: "repair", sourceLayout: "en", targetLayout: "uk", intendedText: "чудово ", isRepair: true), + SeedItemDefinition(groupID: "dev-grp-009", category: "single-word", label: "repair", sourceLayout: "en", targetLayout: "uk", intendedText: "правда ", isRepair: true), + SeedItemDefinition(groupID: "dev-grp-010", category: "question", label: "repair", sourceLayout: "en", targetLayout: "uk", intendedText: "як справи ", isRepair: true), + + SeedItemDefinition(groupID: "dev-grp-011", category: "single-word", label: "repair", sourceLayout: "uk", targetLayout: "en", intendedText: "hello ", isRepair: true), + SeedItemDefinition(groupID: "dev-grp-012", category: "single-word", label: "repair", sourceLayout: "uk", targetLayout: "en", intendedText: "world ", isRepair: true), + SeedItemDefinition(groupID: "dev-grp-013", category: "single-word", label: "repair", sourceLayout: "uk", targetLayout: "en", intendedText: "system ", isRepair: true), + SeedItemDefinition(groupID: "dev-grp-014", category: "single-word", label: "repair", sourceLayout: "uk", targetLayout: "en", intendedText: "window ", isRepair: true), + SeedItemDefinition(groupID: "dev-grp-015", category: "capitalized", label: "repair", sourceLayout: "uk", targetLayout: "en", intendedText: "London ", isRepair: true), + SeedItemDefinition(groupID: "dev-grp-016", category: "back-to-back", label: "repair", sourceLayout: "uk", targetLayout: "en", intendedText: "good morning ", isRepair: true), + SeedItemDefinition(groupID: "dev-grp-017", category: "single-word", label: "repair", sourceLayout: "uk", targetLayout: "en", intendedText: "expected ", isRepair: true), + SeedItemDefinition(groupID: "dev-grp-018", category: "single-word", label: "repair", sourceLayout: "uk", targetLayout: "en", intendedText: "thanks ", isRepair: true), + SeedItemDefinition(groupID: "dev-grp-019", category: "single-word", label: "repair", sourceLayout: "uk", targetLayout: "en", intendedText: "project ", isRepair: true), + SeedItemDefinition(groupID: "dev-grp-020", category: "question", label: "repair", sourceLayout: "uk", targetLayout: "en", intendedText: "how are you ", isRepair: true), + + // 60 Preserves (30 English, 30 Ukrainian) + SeedItemDefinition(groupID: "dev-grp-021", category: "natural-word", label: "preserve", sourceLayout: "en", targetLayout: "en", intendedText: "apple ", isRepair: false), + SeedItemDefinition(groupID: "dev-grp-022", category: "natural-word", label: "preserve", sourceLayout: "en", targetLayout: "en", intendedText: "swift ", isRepair: false), + SeedItemDefinition(groupID: "dev-grp-023", category: "natural-word", label: "preserve", sourceLayout: "en", targetLayout: "en", intendedText: "developer ", isRepair: false), + SeedItemDefinition(groupID: "dev-grp-024", category: "natural-word", label: "preserve", sourceLayout: "en", targetLayout: "en", intendedText: "terminal ", isRepair: false), + SeedItemDefinition(groupID: "dev-grp-025", category: "natural-word", label: "preserve", sourceLayout: "en", targetLayout: "en", intendedText: "release ", isRepair: false), + SeedItemDefinition(groupID: "dev-grp-026", category: "natural-word", label: "preserve", sourceLayout: "en", targetLayout: "en", intendedText: "keyboard ", isRepair: false), + SeedItemDefinition(groupID: "dev-grp-027", category: "short-word", label: "preserve", sourceLayout: "en", targetLayout: "en", intendedText: "the ", isRepair: false), + SeedItemDefinition(groupID: "dev-grp-028", category: "short-word", label: "preserve", sourceLayout: "en", targetLayout: "en", intendedText: "and ", isRepair: false), + SeedItemDefinition(groupID: "dev-grp-029", category: "short-word", label: "preserve", sourceLayout: "en", targetLayout: "en", intendedText: "for ", isRepair: false), + SeedItemDefinition(groupID: "dev-grp-030", category: "short-word", label: "preserve", sourceLayout: "en", targetLayout: "en", intendedText: "not ", isRepair: false), + SeedItemDefinition(groupID: "dev-grp-031", category: "short-word", label: "preserve", sourceLayout: "en", targetLayout: "en", intendedText: "but ", isRepair: false), + SeedItemDefinition(groupID: "dev-grp-032", category: "short-word", label: "preserve", sourceLayout: "en", targetLayout: "en", intendedText: "yes ", isRepair: false), + SeedItemDefinition(groupID: "dev-grp-033", category: "proper-noun", label: "preserve", sourceLayout: "en", targetLayout: "en", intendedText: "GitHub ", isRepair: false), + SeedItemDefinition(groupID: "dev-grp-034", category: "proper-noun", label: "preserve", sourceLayout: "en", targetLayout: "en", intendedText: "Unix ", isRepair: false), + SeedItemDefinition(groupID: "dev-grp-035", category: "proper-noun", label: "preserve", sourceLayout: "en", targetLayout: "en", intendedText: "Xcode ", isRepair: false), + SeedItemDefinition(groupID: "dev-grp-036", category: "proper-noun", label: "preserve", sourceLayout: "en", targetLayout: "en", intendedText: "macOS ", isRepair: false), + SeedItemDefinition(groupID: "dev-grp-037", category: "code-token", label: "preserve", sourceLayout: "en", targetLayout: "en", intendedText: "camelCase ", isRepair: false), + SeedItemDefinition(groupID: "dev-grp-038", category: "code-token", label: "preserve", sourceLayout: "en", targetLayout: "en", intendedText: "snake_case ", isRepair: false), + SeedItemDefinition(groupID: "dev-grp-039", category: "code-token", label: "preserve", sourceLayout: "en", targetLayout: "en", intendedText: "kUCKeyActionDown ", isRepair: false), + SeedItemDefinition(groupID: "dev-grp-040", category: "code-token", label: "preserve", sourceLayout: "en", targetLayout: "en", intendedText: "init() ", isRepair: false), + SeedItemDefinition(groupID: "dev-grp-041", category: "path", label: "preserve", sourceLayout: "en", targetLayout: "en", intendedText: "/usr/local/bin ", isRepair: false), + SeedItemDefinition(groupID: "dev-grp-042", category: "filename", label: "preserve", sourceLayout: "en", targetLayout: "en", intendedText: "Package.swift ", isRepair: false), + SeedItemDefinition(groupID: "dev-grp-043", category: "url", label: "preserve", sourceLayout: "en", targetLayout: "en", intendedText: "https://apple.com ", isRepair: false), + SeedItemDefinition(groupID: "dev-grp-044", category: "email", label: "preserve", sourceLayout: "en", targetLayout: "en", intendedText: "dev@example.org ", isRepair: false), + SeedItemDefinition(groupID: "dev-grp-045", category: "acronym", label: "preserve", sourceLayout: "en", targetLayout: "en", intendedText: "HTTP ", isRepair: false), + SeedItemDefinition(groupID: "dev-grp-046", category: "acronym", label: "preserve", sourceLayout: "en", targetLayout: "en", intendedText: "JSON ", isRepair: false), + SeedItemDefinition(groupID: "dev-grp-047", category: "acronym", label: "preserve", sourceLayout: "en", targetLayout: "en", intendedText: "UUID ", isRepair: false), + SeedItemDefinition(groupID: "dev-grp-048", category: "contraction", label: "preserve", sourceLayout: "en", targetLayout: "en", intendedText: "don't ", isRepair: false), + SeedItemDefinition(groupID: "dev-grp-049", category: "contraction", label: "preserve", sourceLayout: "en", targetLayout: "en", intendedText: "it's ", isRepair: false), + SeedItemDefinition(groupID: "dev-grp-050", category: "punctuation", label: "preserve", sourceLayout: "en", targetLayout: "en", intendedText: "result = true; ", isRepair: false), + + SeedItemDefinition(groupID: "dev-grp-051", category: "natural-word", label: "preserve", sourceLayout: "uk", targetLayout: "uk", intendedText: "яблуко ", isRepair: false), + SeedItemDefinition(groupID: "dev-grp-052", category: "natural-word", label: "preserve", sourceLayout: "uk", targetLayout: "uk", intendedText: "ринок ", isRepair: false), + SeedItemDefinition(groupID: "dev-grp-053", category: "natural-word", label: "preserve", sourceLayout: "uk", targetLayout: "uk", intendedText: "розробник ", isRepair: false), + SeedItemDefinition(groupID: "dev-grp-054", category: "natural-word", label: "preserve", sourceLayout: "uk", targetLayout: "uk", intendedText: "термінал ", isRepair: false), + SeedItemDefinition(groupID: "dev-grp-055", category: "natural-word", label: "preserve", sourceLayout: "uk", targetLayout: "uk", intendedText: "реліз ", isRepair: false), + SeedItemDefinition(groupID: "dev-grp-056", category: "natural-word", label: "preserve", sourceLayout: "uk", targetLayout: "uk", intendedText: "система ", isRepair: false), + SeedItemDefinition(groupID: "dev-grp-057", category: "short-word", label: "preserve", sourceLayout: "uk", targetLayout: "uk", intendedText: "це ", isRepair: false), + SeedItemDefinition(groupID: "dev-grp-058", category: "short-word", label: "preserve", sourceLayout: "uk", targetLayout: "uk", intendedText: "і ", isRepair: false), + SeedItemDefinition(groupID: "dev-grp-059", category: "short-word", label: "preserve", sourceLayout: "uk", targetLayout: "uk", intendedText: "на ", isRepair: false), + SeedItemDefinition(groupID: "dev-grp-060", category: "short-word", label: "preserve", sourceLayout: "uk", targetLayout: "uk", intendedText: "до ", isRepair: false), + SeedItemDefinition(groupID: "dev-grp-061", category: "short-word", label: "preserve", sourceLayout: "uk", targetLayout: "uk", intendedText: "та ", isRepair: false), + SeedItemDefinition(groupID: "dev-grp-062", category: "short-word", label: "preserve", sourceLayout: "uk", targetLayout: "uk", intendedText: "ми ", isRepair: false), + SeedItemDefinition(groupID: "dev-grp-063", category: "proper-noun", label: "preserve", sourceLayout: "uk", targetLayout: "uk", intendedText: "Львів ", isRepair: false), + SeedItemDefinition(groupID: "dev-grp-064", category: "proper-noun", label: "preserve", sourceLayout: "uk", targetLayout: "uk", intendedText: "Тарас ", isRepair: false), + SeedItemDefinition(groupID: "dev-grp-065", category: "proper-noun", label: "preserve", sourceLayout: "uk", targetLayout: "uk", intendedText: "Шевченко ", isRepair: false), + SeedItemDefinition(groupID: "dev-grp-066", category: "proper-noun", label: "preserve", sourceLayout: "uk", targetLayout: "uk", intendedText: "Дніпро ", isRepair: false), + SeedItemDefinition(groupID: "dev-grp-067", category: "apostrophe", label: "preserve", sourceLayout: "uk", targetLayout: "uk", intendedText: "сім'я ", isRepair: false), + SeedItemDefinition(groupID: "dev-grp-068", category: "apostrophe", label: "preserve", sourceLayout: "uk", targetLayout: "uk", intendedText: "м'ясо ", isRepair: false), + SeedItemDefinition(groupID: "dev-grp-069", category: "apostrophe", label: "preserve", sourceLayout: "uk", targetLayout: "uk", intendedText: "в'язати ", isRepair: false), + SeedItemDefinition(groupID: "dev-grp-070", category: "apostrophe", label: "preserve", sourceLayout: "uk", targetLayout: "uk", intendedText: "комп'ютер ", isRepair: false), + SeedItemDefinition(groupID: "dev-grp-071", category: "acronym", label: "preserve", sourceLayout: "uk", targetLayout: "uk", intendedText: "НАТО ", isRepair: false), + SeedItemDefinition(groupID: "dev-grp-072", category: "acronym", label: "preserve", sourceLayout: "uk", targetLayout: "uk", intendedText: "ООН ", isRepair: false), + SeedItemDefinition(groupID: "dev-grp-073", category: "acronym", label: "preserve", sourceLayout: "uk", targetLayout: "uk", intendedText: "США ", isRepair: false), + SeedItemDefinition(groupID: "dev-grp-074", category: "acronym", label: "preserve", sourceLayout: "uk", targetLayout: "uk", intendedText: "НБУ ", isRepair: false), + SeedItemDefinition(groupID: "dev-grp-075", category: "punctuation", label: "preserve", sourceLayout: "uk", targetLayout: "uk", intendedText: "добре! ", isRepair: false), + SeedItemDefinition(groupID: "dev-grp-076", category: "punctuation", label: "preserve", sourceLayout: "uk", targetLayout: "uk", intendedText: "(так) ", isRepair: false), + SeedItemDefinition(groupID: "dev-grp-077", category: "punctuation", label: "preserve", sourceLayout: "uk", targetLayout: "uk", intendedText: "слово: ", isRepair: false), + SeedItemDefinition(groupID: "dev-grp-078", category: "symbol", label: "preserve", sourceLayout: "uk", targetLayout: "uk", intendedText: "100% ", isRepair: false), + SeedItemDefinition(groupID: "dev-grp-079", category: "phrase", label: "preserve", sourceLayout: "uk", targetLayout: "uk", intendedText: "добрий ранок ", isRepair: false), + SeedItemDefinition(groupID: "dev-grp-080", category: "phrase", label: "preserve", sourceLayout: "uk", targetLayout: "uk", intendedText: "гарного дня ", isRepair: false), + + // 20 Abstains (10 English, 10 Ukrainian) + SeedItemDefinition(groupID: "dev-grp-081", category: "ambiguous-short", label: "abstain", sourceLayout: "en", targetLayout: "en", intendedText: "to ", isRepair: false), + SeedItemDefinition(groupID: "dev-grp-082", category: "ambiguous-short", label: "abstain", sourceLayout: "en", targetLayout: "en", intendedText: "me ", isRepair: false), + SeedItemDefinition(groupID: "dev-grp-083", category: "ambiguous-short", label: "abstain", sourceLayout: "en", targetLayout: "en", intendedText: "do ", isRepair: false), + SeedItemDefinition(groupID: "dev-grp-084", category: "ambiguous-short", label: "abstain", sourceLayout: "en", targetLayout: "en", intendedText: "no ", isRepair: false), + SeedItemDefinition(groupID: "dev-grp-085", category: "ambiguous-short", label: "abstain", sourceLayout: "en", targetLayout: "en", intendedText: "or ", isRepair: false), + SeedItemDefinition(groupID: "dev-grp-086", category: "gibberish", label: "abstain", sourceLayout: "en", targetLayout: "en", intendedText: "asdfghjk ", isRepair: false), + SeedItemDefinition(groupID: "dev-grp-087", category: "gibberish", label: "abstain", sourceLayout: "en", targetLayout: "en", intendedText: "qwertyuiop ", isRepair: false), + SeedItemDefinition(groupID: "dev-grp-088", category: "isolated-char", label: "abstain", sourceLayout: "en", targetLayout: "en", intendedText: "a ", isRepair: false), + SeedItemDefinition(groupID: "dev-grp-089", category: "isolated-char", label: "abstain", sourceLayout: "en", targetLayout: "en", intendedText: "x ", isRepair: false), + SeedItemDefinition(groupID: "dev-grp-090", category: "isolated-symbol", label: "abstain", sourceLayout: "en", targetLayout: "en", intendedText: ": ", isRepair: false), + + SeedItemDefinition(groupID: "dev-grp-091", category: "ambiguous-short", label: "abstain", sourceLayout: "uk", targetLayout: "uk", intendedText: "то ", isRepair: false), + SeedItemDefinition(groupID: "dev-grp-092", category: "ambiguous-short", label: "abstain", sourceLayout: "uk", targetLayout: "uk", intendedText: "не ", isRepair: false), + SeedItemDefinition(groupID: "dev-grp-093", category: "ambiguous-short", label: "abstain", sourceLayout: "uk", targetLayout: "uk", intendedText: "чи ", isRepair: false), + SeedItemDefinition(groupID: "dev-grp-094", category: "ambiguous-short", label: "abstain", sourceLayout: "uk", targetLayout: "uk", intendedText: "за ", isRepair: false), + SeedItemDefinition(groupID: "dev-grp-095", category: "ambiguous-short", label: "abstain", sourceLayout: "uk", targetLayout: "uk", intendedText: "як ", isRepair: false), + SeedItemDefinition(groupID: "dev-grp-096", category: "gibberish", label: "abstain", sourceLayout: "uk", targetLayout: "uk", intendedText: "йцукенгш ", isRepair: false), + SeedItemDefinition(groupID: "dev-grp-097", category: "gibberish", label: "abstain", sourceLayout: "uk", targetLayout: "uk", intendedText: "фівапрол ", isRepair: false), + SeedItemDefinition(groupID: "dev-grp-098", category: "isolated-char", label: "abstain", sourceLayout: "uk", targetLayout: "uk", intendedText: "я ", isRepair: false), + SeedItemDefinition(groupID: "dev-grp-099", category: "isolated-symbol", label: "abstain", sourceLayout: "uk", targetLayout: "uk", intendedText: "; ", isRepair: false), + SeedItemDefinition(groupID: "dev-grp-100", category: "symbol", label: "abstain", sourceLayout: "uk", targetLayout: "uk", intendedText: "--- ", isRepair: false) + ] + + public static let holdoutItems: [SeedItemDefinition] = [ + // 20 Repairs (10 EN->UK, 10 UK->EN) + SeedItemDefinition(groupID: "holdout-grp-001", category: "single-word", label: "repair", sourceLayout: "en", targetLayout: "uk", intendedText: "мова ", isRepair: true), + SeedItemDefinition(groupID: "holdout-grp-002", category: "capitalized", label: "repair", sourceLayout: "en", targetLayout: "uk", intendedText: "Україна ", isRepair: true), + SeedItemDefinition(groupID: "holdout-grp-003", category: "single-word", label: "repair", sourceLayout: "en", targetLayout: "uk", intendedText: "дякуємо ", isRepair: true), + SeedItemDefinition(groupID: "holdout-grp-004", category: "back-to-back", label: "repair", sourceLayout: "en", targetLayout: "uk", intendedText: "новий день ", isRepair: true), + SeedItemDefinition(groupID: "holdout-grp-005", category: "single-word", label: "repair", sourceLayout: "en", targetLayout: "uk", intendedText: "користувач ", isRepair: true), + SeedItemDefinition(groupID: "holdout-grp-006", category: "single-word", label: "repair", sourceLayout: "en", targetLayout: "uk", intendedText: "документ ", isRepair: true), + SeedItemDefinition(groupID: "holdout-grp-007", category: "single-word", label: "repair", sourceLayout: "en", targetLayout: "uk", intendedText: "можливо ", isRepair: true), + SeedItemDefinition(groupID: "holdout-grp-008", category: "single-word", label: "repair", sourceLayout: "en", targetLayout: "uk", intendedText: "бачимо ", isRepair: true), + SeedItemDefinition(groupID: "holdout-grp-009", category: "single-word", label: "repair", sourceLayout: "en", targetLayout: "uk", intendedText: "вечір ", isRepair: true), + SeedItemDefinition(groupID: "holdout-grp-010", category: "question", label: "repair", sourceLayout: "en", targetLayout: "uk", intendedText: "де ти ", isRepair: true), + + SeedItemDefinition(groupID: "holdout-grp-011", category: "single-word", label: "repair", sourceLayout: "uk", targetLayout: "en", intendedText: "apple ", isRepair: true), + SeedItemDefinition(groupID: "holdout-grp-012", category: "capitalized", label: "repair", sourceLayout: "uk", targetLayout: "en", intendedText: "Canada ", isRepair: true), + SeedItemDefinition(groupID: "holdout-grp-013", category: "single-word", label: "repair", sourceLayout: "uk", targetLayout: "en", intendedText: "welcome ", isRepair: true), + SeedItemDefinition(groupID: "holdout-grp-014", category: "back-to-back", label: "repair", sourceLayout: "uk", targetLayout: "en", intendedText: "new file ", isRepair: true), + SeedItemDefinition(groupID: "holdout-grp-015", category: "single-word", label: "repair", sourceLayout: "uk", targetLayout: "en", intendedText: "browser ", isRepair: true), + SeedItemDefinition(groupID: "holdout-grp-016", category: "single-word", label: "repair", sourceLayout: "uk", targetLayout: "en", intendedText: "document ", isRepair: true), + SeedItemDefinition(groupID: "holdout-grp-017", category: "single-word", label: "repair", sourceLayout: "uk", targetLayout: "en", intendedText: "perhaps ", isRepair: true), + SeedItemDefinition(groupID: "holdout-grp-018", category: "single-word", label: "repair", sourceLayout: "uk", targetLayout: "en", intendedText: "looking ", isRepair: true), + SeedItemDefinition(groupID: "holdout-grp-019", category: "single-word", label: "repair", sourceLayout: "uk", targetLayout: "en", intendedText: "evening ", isRepair: true), + SeedItemDefinition(groupID: "holdout-grp-020", category: "question", label: "repair", sourceLayout: "uk", targetLayout: "en", intendedText: "where are you ", isRepair: true), + + // 60 Preserves (30 English, 30 Ukrainian) + SeedItemDefinition(groupID: "holdout-grp-021", category: "natural-word", label: "preserve", sourceLayout: "en", targetLayout: "en", intendedText: "orange ", isRepair: false), + SeedItemDefinition(groupID: "holdout-grp-022", category: "natural-word", label: "preserve", sourceLayout: "en", targetLayout: "en", intendedText: "python ", isRepair: false), + SeedItemDefinition(groupID: "holdout-grp-023", category: "natural-word", label: "preserve", sourceLayout: "en", targetLayout: "en", intendedText: "architect ", isRepair: false), + SeedItemDefinition(groupID: "holdout-grp-024", category: "natural-word", label: "preserve", sourceLayout: "en", targetLayout: "en", intendedText: "console ", isRepair: false), + SeedItemDefinition(groupID: "holdout-grp-025", category: "natural-word", label: "preserve", sourceLayout: "en", targetLayout: "en", intendedText: "update ", isRepair: false), + SeedItemDefinition(groupID: "holdout-grp-026", category: "natural-word", label: "preserve", sourceLayout: "en", targetLayout: "en", intendedText: "display ", isRepair: false), + SeedItemDefinition(groupID: "holdout-grp-027", category: "short-word", label: "preserve", sourceLayout: "en", targetLayout: "en", intendedText: "an ", isRepair: false), + SeedItemDefinition(groupID: "holdout-grp-028", category: "short-word", label: "preserve", sourceLayout: "en", targetLayout: "en", intendedText: "in ", isRepair: false), + SeedItemDefinition(groupID: "holdout-grp-029", category: "short-word", label: "preserve", sourceLayout: "en", targetLayout: "en", intendedText: "at ", isRepair: false), + SeedItemDefinition(groupID: "holdout-grp-030", category: "short-word", label: "preserve", sourceLayout: "en", targetLayout: "en", intendedText: "so ", isRepair: false), + SeedItemDefinition(groupID: "holdout-grp-031", category: "short-word", label: "preserve", sourceLayout: "en", targetLayout: "en", intendedText: "if ", isRepair: false), + SeedItemDefinition(groupID: "holdout-grp-032", category: "short-word", label: "preserve", sourceLayout: "en", targetLayout: "en", intendedText: "up ", isRepair: false), + SeedItemDefinition(groupID: "holdout-grp-033", category: "proper-noun", label: "preserve", sourceLayout: "en", targetLayout: "en", intendedText: "GitLab ", isRepair: false), + SeedItemDefinition(groupID: "holdout-grp-034", category: "proper-noun", label: "preserve", sourceLayout: "en", targetLayout: "en", intendedText: "Linux ", isRepair: false), + SeedItemDefinition(groupID: "holdout-grp-035", category: "proper-noun", label: "preserve", sourceLayout: "en", targetLayout: "en", intendedText: "VisualCode ", isRepair: false), + SeedItemDefinition(groupID: "holdout-grp-036", category: "proper-noun", label: "preserve", sourceLayout: "en", targetLayout: "en", intendedText: "iOS ", isRepair: false), + SeedItemDefinition(groupID: "holdout-grp-037", category: "code-token", label: "preserve", sourceLayout: "en", targetLayout: "en", intendedText: "pascalCase ", isRepair: false), + SeedItemDefinition(groupID: "holdout-grp-038", category: "code-token", label: "preserve", sourceLayout: "en", targetLayout: "en", intendedText: "kebab_case ", isRepair: false), + SeedItemDefinition(groupID: "holdout-grp-039", category: "code-token", label: "preserve", sourceLayout: "en", targetLayout: "en", intendedText: "CFRelease ", isRepair: false), + SeedItemDefinition(groupID: "holdout-grp-040", category: "code-token", label: "preserve", sourceLayout: "en", targetLayout: "en", intendedText: "deinit() ", isRepair: false), + SeedItemDefinition(groupID: "holdout-grp-041", category: "path", label: "preserve", sourceLayout: "en", targetLayout: "en", intendedText: "/etc/hosts ", isRepair: false), + SeedItemDefinition(groupID: "holdout-grp-042", category: "filename", label: "preserve", sourceLayout: "en", targetLayout: "en", intendedText: "README.md ", isRepair: false), + SeedItemDefinition(groupID: "holdout-grp-043", category: "url", label: "preserve", sourceLayout: "en", targetLayout: "en", intendedText: "https://swift.org ", isRepair: false), + SeedItemDefinition(groupID: "holdout-grp-044", category: "email", label: "preserve", sourceLayout: "en", targetLayout: "en", intendedText: "info@company.com ", isRepair: false), + SeedItemDefinition(groupID: "holdout-grp-045", category: "acronym", label: "preserve", sourceLayout: "en", targetLayout: "en", intendedText: "REST ", isRepair: false), + SeedItemDefinition(groupID: "holdout-grp-046", category: "acronym", label: "preserve", sourceLayout: "en", targetLayout: "en", intendedText: "HTML ", isRepair: false), + SeedItemDefinition(groupID: "holdout-grp-047", category: "acronym", label: "preserve", sourceLayout: "en", targetLayout: "en", intendedText: "RAM ", isRepair: false), + SeedItemDefinition(groupID: "holdout-grp-048", category: "contraction", label: "preserve", sourceLayout: "en", targetLayout: "en", intendedText: "won't ", isRepair: false), + SeedItemDefinition(groupID: "holdout-grp-049", category: "contraction", label: "preserve", sourceLayout: "en", targetLayout: "en", intendedText: "that's ", isRepair: false), + SeedItemDefinition(groupID: "holdout-grp-050", category: "punctuation", label: "preserve", sourceLayout: "en", targetLayout: "en", intendedText: "status == 0; ", isRepair: false), + + SeedItemDefinition(groupID: "holdout-grp-051", category: "natural-word", label: "preserve", sourceLayout: "uk", targetLayout: "uk", intendedText: "апельсин ", isRepair: false), + SeedItemDefinition(groupID: "holdout-grp-052", category: "natural-word", label: "preserve", sourceLayout: "uk", targetLayout: "uk", intendedText: "мова ", isRepair: false), + SeedItemDefinition(groupID: "holdout-grp-053", category: "natural-word", label: "preserve", sourceLayout: "uk", targetLayout: "uk", intendedText: "архітектор ", isRepair: false), + SeedItemDefinition(groupID: "holdout-grp-054", category: "natural-word", label: "preserve", sourceLayout: "uk", targetLayout: "uk", intendedText: "консоль ", isRepair: false), + SeedItemDefinition(groupID: "holdout-grp-055", category: "natural-word", label: "preserve", sourceLayout: "uk", targetLayout: "uk", intendedText: "оновлення ", isRepair: false), + SeedItemDefinition(groupID: "holdout-grp-056", category: "natural-word", label: "preserve", sourceLayout: "uk", targetLayout: "uk", intendedText: "екран ", isRepair: false), + SeedItemDefinition(groupID: "holdout-grp-057", category: "short-word", label: "preserve", sourceLayout: "uk", targetLayout: "uk", intendedText: "бо ", isRepair: false), + SeedItemDefinition(groupID: "holdout-grp-058", category: "short-word", label: "preserve", sourceLayout: "uk", targetLayout: "uk", intendedText: "чи ", isRepair: false), + SeedItemDefinition(groupID: "holdout-grp-059", category: "short-word", label: "preserve", sourceLayout: "uk", targetLayout: "uk", intendedText: "у ", isRepair: false), + SeedItemDefinition(groupID: "holdout-grp-060", category: "short-word", label: "preserve", sourceLayout: "uk", targetLayout: "uk", intendedText: "в ", isRepair: false), + SeedItemDefinition(groupID: "holdout-grp-061", category: "short-word", label: "preserve", sourceLayout: "uk", targetLayout: "uk", intendedText: "от ", isRepair: false), + SeedItemDefinition(groupID: "holdout-grp-062", category: "short-word", label: "preserve", sourceLayout: "uk", targetLayout: "uk", intendedText: "ну ", isRepair: false), + SeedItemDefinition(groupID: "holdout-grp-063", category: "proper-noun", label: "preserve", sourceLayout: "uk", targetLayout: "uk", intendedText: "Одеса ", isRepair: false), + SeedItemDefinition(groupID: "holdout-grp-064", category: "proper-noun", label: "preserve", sourceLayout: "uk", targetLayout: "uk", intendedText: "Франко ", isRepair: false), + SeedItemDefinition(groupID: "holdout-grp-065", category: "proper-noun", label: "preserve", sourceLayout: "uk", targetLayout: "uk", intendedText: "Леся ", isRepair: false), + SeedItemDefinition(groupID: "holdout-grp-066", category: "proper-noun", label: "preserve", sourceLayout: "uk", targetLayout: "uk", intendedText: "Харків ", isRepair: false), + SeedItemDefinition(groupID: "holdout-grp-067", category: "apostrophe", label: "preserve", sourceLayout: "uk", targetLayout: "uk", intendedText: "м'який ", isRepair: false), + SeedItemDefinition(groupID: "holdout-grp-068", category: "apostrophe", label: "preserve", sourceLayout: "uk", targetLayout: "uk", intendedText: "п'ять ", isRepair: false), + SeedItemDefinition(groupID: "holdout-grp-069", category: "apostrophe", label: "preserve", sourceLayout: "uk", targetLayout: "uk", intendedText: "солов'їний ", isRepair: false), + SeedItemDefinition(groupID: "holdout-grp-070", category: "apostrophe", label: "preserve", sourceLayout: "uk", targetLayout: "uk", intendedText: "під'їзд ", isRepair: false), + SeedItemDefinition(groupID: "holdout-grp-071", category: "acronym", label: "preserve", sourceLayout: "uk", targetLayout: "uk", intendedText: "МВФ ", isRepair: false), + SeedItemDefinition(groupID: "holdout-grp-072", category: "acronym", label: "preserve", sourceLayout: "uk", targetLayout: "uk", intendedText: "ЮНЕСКО ", isRepair: false), + SeedItemDefinition(groupID: "holdout-grp-073", category: "acronym", label: "preserve", sourceLayout: "uk", targetLayout: "uk", intendedText: "ЄС ", isRepair: false), + SeedItemDefinition(groupID: "holdout-grp-074", category: "acronym", label: "preserve", sourceLayout: "uk", targetLayout: "uk", intendedText: "ЗСУ ", isRepair: false), + SeedItemDefinition(groupID: "holdout-grp-075", category: "punctuation", label: "preserve", sourceLayout: "uk", targetLayout: "uk", intendedText: "чудово! ", isRepair: false), + SeedItemDefinition(groupID: "holdout-grp-076", category: "punctuation", label: "preserve", sourceLayout: "uk", targetLayout: "uk", intendedText: "(ні) ", isRepair: false), + SeedItemDefinition(groupID: "holdout-grp-077", category: "punctuation", label: "preserve", sourceLayout: "uk", targetLayout: "uk", intendedText: "висновок: ", isRepair: false), + SeedItemDefinition(groupID: "holdout-grp-078", category: "symbol", label: "preserve", sourceLayout: "uk", targetLayout: "uk", intendedText: "50% ", isRepair: false), + SeedItemDefinition(groupID: "holdout-grp-079", category: "phrase", label: "preserve", sourceLayout: "uk", targetLayout: "uk", intendedText: "добрий вечір ", isRepair: false), + SeedItemDefinition(groupID: "holdout-grp-080", category: "phrase", label: "preserve", sourceLayout: "uk", targetLayout: "uk", intendedText: "на все добре ", isRepair: false), + + // 20 Abstains (10 English, 10 Ukrainian) + SeedItemDefinition(groupID: "holdout-grp-081", category: "ambiguous-short", label: "abstain", sourceLayout: "en", targetLayout: "en", intendedText: "am ", isRepair: false), + SeedItemDefinition(groupID: "holdout-grp-082", category: "ambiguous-short", label: "abstain", sourceLayout: "en", targetLayout: "en", intendedText: "is ", isRepair: false), + SeedItemDefinition(groupID: "holdout-grp-083", category: "ambiguous-short", label: "abstain", sourceLayout: "en", targetLayout: "en", intendedText: "us ", isRepair: false), + SeedItemDefinition(groupID: "holdout-grp-084", category: "ambiguous-short", label: "abstain", sourceLayout: "en", targetLayout: "en", intendedText: "be ", isRepair: false), + SeedItemDefinition(groupID: "holdout-grp-085", category: "ambiguous-short", label: "abstain", sourceLayout: "en", targetLayout: "en", intendedText: "he ", isRepair: false), + SeedItemDefinition(groupID: "holdout-grp-086", category: "gibberish", label: "abstain", sourceLayout: "en", targetLayout: "en", intendedText: "zxcvbnm ", isRepair: false), + SeedItemDefinition(groupID: "holdout-grp-087", category: "gibberish", label: "abstain", sourceLayout: "en", targetLayout: "en", intendedText: "lkjhgfdsa ", isRepair: false), + SeedItemDefinition(groupID: "holdout-grp-088", category: "isolated-char", label: "abstain", sourceLayout: "en", targetLayout: "en", intendedText: "b ", isRepair: false), + SeedItemDefinition(groupID: "holdout-grp-089", category: "isolated-char", label: "abstain", sourceLayout: "en", targetLayout: "en", intendedText: "y ", isRepair: false), + SeedItemDefinition(groupID: "holdout-grp-090", category: "isolated-symbol", label: "abstain", sourceLayout: "en", targetLayout: "en", intendedText: ", ", isRepair: false), + + SeedItemDefinition(groupID: "holdout-grp-091", category: "ambiguous-short", label: "abstain", sourceLayout: "uk", targetLayout: "uk", intendedText: "би ", isRepair: false), + SeedItemDefinition(groupID: "holdout-grp-092", category: "ambiguous-short", label: "abstain", sourceLayout: "uk", targetLayout: "uk", intendedText: "же ", isRepair: false), + SeedItemDefinition(groupID: "holdout-grp-093", category: "ambiguous-short", label: "abstain", sourceLayout: "uk", targetLayout: "uk", intendedText: "от ", isRepair: false), + SeedItemDefinition(groupID: "holdout-grp-094", category: "ambiguous-short", label: "abstain", sourceLayout: "uk", targetLayout: "uk", intendedText: "ну ", isRepair: false), + SeedItemDefinition(groupID: "holdout-grp-095", category: "ambiguous-short", label: "abstain", sourceLayout: "uk", targetLayout: "uk", intendedText: "що ", isRepair: false), + SeedItemDefinition(groupID: "holdout-grp-096", category: "gibberish", label: "abstain", sourceLayout: "uk", targetLayout: "uk", intendedText: "ячсмитьб ", isRepair: false), + SeedItemDefinition(groupID: "holdout-grp-097", category: "gibberish", label: "abstain", sourceLayout: "uk", targetLayout: "uk", intendedText: "полдзячс ", isRepair: false), + SeedItemDefinition(groupID: "holdout-grp-098", category: "isolated-char", label: "abstain", sourceLayout: "uk", targetLayout: "uk", intendedText: "і ", isRepair: false), + SeedItemDefinition(groupID: "holdout-grp-099", category: "isolated-symbol", label: "abstain", sourceLayout: "uk", targetLayout: "uk", intendedText: ", ", isRepair: false), + SeedItemDefinition(groupID: "holdout-grp-100", category: "symbol", label: "abstain", sourceLayout: "uk", targetLayout: "uk", intendedText: "=== ", isRepair: false) + ] +} + +/// One frozen corpus version: its trials and the README written next to them. +public struct SeedCorpusVersion { + public let version: String + public let created: String + public let generator: String + public let developmentItems: [SeedItemDefinition] + public let holdoutItems: [SeedItemDefinition] + public let readme: (_ sourceCommit: String, _ summaries: [String: SplitSummary]) -> String +} + +extension SeedCorpusDefinitions { + public static func version(_ name: String) -> SeedCorpusVersion? { + switch name { + case "v1": return v1 + case "v2": return v2 + default: return nil + } + } + + static let v1 = SeedCorpusVersion( + version: "v1", + created: "2026-10-19", + generator: "manual-v1", + developmentItems: developmentItems, + holdoutItems: holdoutItems, + readme: { sourceCommit, _ in + """ + # Layout Benchmark Corpus v1 + + Version: 1.0 (Frozen Seed Baseline) + Specification: `docs/plans/03-comparative-false-positive-benchmark.md` + Source commit: `\(sourceCommit)` + + ## Overview + + This directory contains the frozen 200-trial seed corpus for the SwitchFix comparative layout reliability benchmark: + - `development.jsonl`: 100 trials used during development and regression testing + - `holdout.jsonl`: 100 trials reserved for generalization evaluation + - `manifest.json`: Cryptographic integrity hashes and split distributions + + ## Split Structure (Per Split) + + Each split contains exactly 100 trials: + - **20 Repair** trials: Intentional layout errors requiring automatic repair (10 English → Ukrainian, 10 Ukrainian → English) + - **60 Preserve** trials: Intentional correct text requiring preservation with no modifications (30 typed under ABC, 30 typed under Ukrainian) + - **20 Abstain** trials: Ambiguous or unsupported inputs requiring no automatic action (10 typed under ABC, 10 typed under Ukrainian) + + Base group IDs (`groupID`) are completely disjoint across the development and holdout splits. + + ## Trial Schema & Provenance + + Every row in the JSONL files is an independent JSON object: + - `schemaVersion`: 1 + - `id`: Unique identifier across all splits + - `groupID`: Base entity / sentence group (disjoint across splits) + - `split`: `"development"` or `"holdout"` + - `languages`: Array of relevant language codes (e.g. `["en", "uk"]`) + - `category`: Strata label (e.g. `single-word`, `capitalized`, `code-token`, `acronym`, `ambiguous-short`, etc.) + - `label`: Target behavior (`repair`, `preserve`, or `abstain`) + - `provenance`: Synthetic generation metadata with `"review": "pending-human"` + - `initial`: Starting editor state (sourceID, language, text, caretUTF16, preferredCyrillic, keyboardType) + - `events`: Array of physical key and boundary events (keyCode, modifiers, rendered) + - `expected`: Target outcome (text, language, sourceID, allowedAutomaticActions, checkpoints) + + ## Deterministic Rendering + + Events are rendered using the frozen layout tables in `Benchmarks/layout-tables/v1/`. The `rendered` field in each event is verified against the table output for the given keyCode and modifiers. + """ + } + ) + + /// v1 trials whose typed input or base text leaked across splits or carried contradictory + /// labels, keyed by their v1 position, with the replacement that keeps the stratum. + static let v2HoldoutReplacements: [Int: SeedItemDefinition] = [ + 11: SeedItemDefinition(groupID: "holdout-grp-011-v2", category: "single-word", label: "repair", sourceLayout: "uk", targetLayout: "en", intendedText: "garden ", isRepair: true, id: "holdout-repair-011-v2"), + 58: SeedItemDefinition(groupID: "holdout-grp-058-v2", category: "short-word", label: "preserve", sourceLayout: "uk", targetLayout: "uk", intendedText: "ще ", isRepair: false, id: "holdout-preserve-058-v2"), + 93: SeedItemDefinition(groupID: "holdout-grp-093-v2", category: "ambiguous-short", label: "abstain", sourceLayout: "uk", targetLayout: "uk", intendedText: "аж ", isRepair: false, id: "holdout-abstain-093-v2"), + 94: SeedItemDefinition(groupID: "holdout-grp-094-v2", category: "ambiguous-short", label: "abstain", sourceLayout: "uk", targetLayout: "uk", intendedText: "хоч ", isRepair: false, id: "holdout-abstain-094-v2"), + 98: SeedItemDefinition(groupID: "holdout-grp-098-v2", category: "isolated-char", label: "abstain", sourceLayout: "uk", targetLayout: "uk", intendedText: "є ", isRepair: false, id: "holdout-abstain-098-v2") + ] + + /// Intentional tokens that are also real words in the other layout (Apple "Ukrainian" is + /// the legacy variant: s=и, b=і). Labels come from the intended text, not a dictionary. + static let v2DevelopmentCollisions: [SeedItemDefinition] = [ + SeedItemDefinition(groupID: "dev-grp-101", category: "cross-layout-collision", label: "preserve", sourceLayout: "en", targetLayout: "en", intendedText: "vs ", isRepair: false), + SeedItemDefinition(groupID: "dev-grp-102", category: "cross-layout-collision", label: "preserve", sourceLayout: "en", targetLayout: "en", intendedText: "yt ", isRepair: false), + SeedItemDefinition(groupID: "dev-grp-103", category: "cross-layout-collision", label: "preserve", sourceLayout: "en", targetLayout: "en", intendedText: "nfr ", isRepair: false), + SeedItemDefinition(groupID: "dev-grp-104", category: "cross-layout-collision", label: "preserve", sourceLayout: "en", targetLayout: "en", intendedText: "uhf ", isRepair: false), + SeedItemDefinition(groupID: "dev-grp-105", category: "cross-layout-collision", label: "preserve", sourceLayout: "en", targetLayout: "en", intendedText: "hbr ", isRepair: false), + SeedItemDefinition(groupID: "dev-grp-106", category: "cross-layout-collision", label: "preserve", sourceLayout: "uk", targetLayout: "uk", intendedText: "руку ", isRepair: false), + SeedItemDefinition(groupID: "dev-grp-106", category: "cross-layout-collision", label: "preserve", sourceLayout: "uk", targetLayout: "uk", intendedText: "руки ", isRepair: false), + SeedItemDefinition(groupID: "dev-grp-106", category: "cross-layout-collision", label: "preserve", sourceLayout: "uk", targetLayout: "uk", intendedText: "рук ", isRepair: false), + SeedItemDefinition(groupID: "dev-grp-107", category: "cross-layout-collision", label: "preserve", sourceLayout: "uk", targetLayout: "uk", intendedText: "кум ", isRepair: false), + SeedItemDefinition(groupID: "dev-grp-108", category: "cross-layout-collision", label: "preserve", sourceLayout: "uk", targetLayout: "uk", intendedText: "нут ", isRepair: false) + ] + + static let v2HoldoutCollisions: [SeedItemDefinition] = [ + SeedItemDefinition(groupID: "holdout-grp-101", category: "cross-layout-collision", label: "preserve", sourceLayout: "en", targetLayout: "en", intendedText: "lev ", isRepair: false), + SeedItemDefinition(groupID: "holdout-grp-102", category: "cross-layout-collision", label: "preserve", sourceLayout: "en", targetLayout: "en", intendedText: "ns ", isRepair: false), + SeedItemDefinition(groupID: "holdout-grp-103", category: "cross-layout-collision", label: "preserve", sourceLayout: "en", targetLayout: "en", intendedText: "gap ", isRepair: false), + SeedItemDefinition(groupID: "holdout-grp-104", category: "cross-layout-collision", label: "preserve", sourceLayout: "en", targetLayout: "en", intendedText: "ufc ", isRepair: false), + SeedItemDefinition(groupID: "holdout-grp-105", category: "cross-layout-collision", label: "preserve", sourceLayout: "en", targetLayout: "en", intendedText: "dsl ", isRepair: false), + SeedItemDefinition(groupID: "holdout-grp-101", category: "cross-layout-collision", label: "preserve", sourceLayout: "uk", targetLayout: "uk", intendedText: "дум ", isRepair: false), + SeedItemDefinition(groupID: "holdout-grp-106", category: "cross-layout-collision", label: "preserve", sourceLayout: "uk", targetLayout: "uk", intendedText: "дути ", isRepair: false), + SeedItemDefinition(groupID: "holdout-grp-106", category: "cross-layout-collision", label: "preserve", sourceLayout: "uk", targetLayout: "uk", intendedText: "дув ", isRepair: false), + SeedItemDefinition(groupID: "holdout-grp-107", category: "cross-layout-collision", label: "preserve", sourceLayout: "uk", targetLayout: "uk", intendedText: "пуд ", isRepair: false), + SeedItemDefinition(groupID: "holdout-grp-108", category: "cross-layout-collision", label: "preserve", sourceLayout: "uk", targetLayout: "uk", intendedText: "дну ", isRepair: false) + ] + + static let v2 = SeedCorpusVersion( + version: "v2", + created: "2026-10-19", + generator: "manual-v2", + developmentItems: developmentItems + v2DevelopmentCollisions, + holdoutItems: holdoutItems.enumerated().map { v2HoldoutReplacements[$0.offset + 1] ?? $0.element } + v2HoldoutCollisions, + readme: { sourceCommit, summaries in + let dev = summaries["development"] + let holdout = summaries["holdout"] + return """ + # Layout Benchmark Corpus v2 + + Specification: `docs/plans/03-comparative-false-positive-benchmark.md` + Source commit: `\(sourceCommit)` + Generator: `LayoutBenchmark generate-seed-corpus --version v2` (byte-deterministic; regenerating reproduces the manifest hashes) + + v2 supersedes v1 as the regression corpus. v1 stays frozen and unchanged for reproducing its baselines. + + ## Changes from v1 + + 1. **Leaks and contradictory labels removed.** In v1 the identical typed inputs `і` and `чи` appeared in both splits with opposite labels (preserve vs abstain), `apple` was a development preserve and a holdout reversed-mapping repair, and holdout labeled `от` and `ну` both preserve and abstain. Five holdout trials are replaced, keeping their stratum, under new IDs: `holdout-repair-011-v2` (`garden`), `holdout-preserve-058-v2` (`ще`), `holdout-abstain-093-v2` (`аж`), `holdout-abstain-094-v2` (`хоч`), `holdout-abstain-098-v2` (`є`). + 2. **Hard negatives added.** v1 negatives were all typed on the correct layout and never collided with a word in the other language, so a zero false-positive rate said little. Category `cross-layout-collision` adds 10 `preserve` trials per split, 5 typed on ABC and 5 on Ukrainian, each an intended token whose rendering on the other layout is a real word (for example `vs` → `ми`, `руку` → `here`). Inflections of one lemma and reversed mappings share a group. + 3. **Validation is stricter.** `validate` reports identical typed input with different labels anywhere, and typed input or base text shared across splits; `--strict` fails on any of them. v1 validates with 9 such warnings, v2 with none. Validation also checks manifest hashes, caret offsets and exact keyboard-type tables. + + ## Split Structure + + - Development: \(dev?.total ?? 0) trials (\(dev?.repair ?? 0) repair, \(dev?.preserve ?? 0) preserve, \(dev?.abstain ?? 0) abstain) + - Holdout: \(holdout?.total ?? 0) trials (\(holdout?.repair ?? 0) repair, \(holdout?.preserve ?? 0) preserve, \(holdout?.abstain ?? 0) abstain) + + Every label is divided equally between trials typed on ABC and on Ukrainian. All trials are synthetic, rendered through the frozen `Benchmarks/layout-tables/v1` tables, and carry `"review": "pending-human"`; human review results become a new corpus version. + + ## Known limits + + - The `abstain` stratum is short words, gibberish and symbols typed on the correct layout; it does not yet contain genuinely ambiguous wrong-layout input. + - Hard negatives are few (20 in total) and correlated within groups; treat their rate as a regression signal, not a false-positive claim. + + The trial schema is unchanged from v1 (see `Benchmarks/layout-corpus/v1/README.md`). + """ + } + ) +} diff --git a/Sources/LayoutBenchmark/SeedCorpusGenerator.swift b/Sources/LayoutBenchmark/SeedCorpusGenerator.swift new file mode 100644 index 0000000..8e5d437 --- /dev/null +++ b/Sources/LayoutBenchmark/SeedCorpusGenerator.swift @@ -0,0 +1,124 @@ +import Foundation + +public struct SeedItemDefinition { + public let groupID: String + public let category: String + public let label: String + public let sourceLayout: String // "en" (ABC) or "uk" (Ukrainian) + public let targetLayout: String // "en" (ABC) or "uk" (Ukrainian) + public let intendedText: String + public let isRepair: Bool + /// Explicit trial ID; by default the ID comes from the split, label and position. + public var id: String? = nil +} + +public enum SeedCorpusGenerator { + public static func generateTrial( + id: String, + item: SeedItemDefinition, + split: String, + generator: String = "manual-v1", + abcTable: LayoutTable, + ukTable: LayoutTable + ) throws -> Trial { + let initialSourceID = item.sourceLayout == "en" ? "com.apple.keylayout.ABC" : "com.apple.keylayout.Ukrainian" + let initialLanguage = item.sourceLayout == "en" ? "en" : "uk" + + // Layout used by physical fingers to press keys: + // If repair, fingers typed assuming target layout, but active layout was sourceLayout. + // If preserve or abstain, fingers typed assuming source layout, and active was sourceLayout. + let physicalLayout = item.isRepair ? item.targetLayout : item.sourceLayout + let physicalTable = physicalLayout == "en" ? abcTable : ukTable + let activeTable = item.sourceLayout == "en" ? abcTable : ukTable + + var events: [TrialEvent] = [] + var typedText = "" + + // For each character in intendedText (which is what fingers intended to type) + for char in item.intendedText { + guard let (keyCode, modifiers) = findKey(for: char, in: physicalTable) else { + throw NSError(domain: "SeedCorpusGenerator", code: 1, userInfo: [NSLocalizedDescriptionKey: "No key found for character '\(char)' in \(physicalLayout) table"]) + } + guard let stroke = activeTable.lookup(keyCode: keyCode, modifiers: modifiers), + let rendered = stroke.output else { + throw NSError(domain: "SeedCorpusGenerator", code: 2, userInfo: [NSLocalizedDescriptionKey: "Active table \(item.sourceLayout) rejected stroke for key \(keyCode) mod \(modifiers)"]) + } + let isBoundary = (char == " " || char == "\n" || char == "\t") + let event = TrialEvent( + type: isBoundary ? "boundary" : "key", + keyCode: keyCode, + modifiers: modifiers, + rendered: rendered + ) + events.append(event) + typedText.append(rendered) + } + + let expectedText: String + let expectedSourceID: String + let expectedLanguage: String + let allowedActions: [String] + + if item.isRepair { + expectedText = item.intendedText + expectedSourceID = item.targetLayout == "en" ? "com.apple.keylayout.ABC" : "com.apple.keylayout.Ukrainian" + expectedLanguage = item.targetLayout == "en" ? "en" : "uk" + allowedActions = ["replaceSuffix", "switchSource"] + } else { + expectedText = typedText + expectedSourceID = initialSourceID + expectedLanguage = initialLanguage + allowedActions = [] + } + + let checkpoints = [ + Checkpoint(afterEvent: events.count, text: expectedText, language: expectedLanguage) + ] + + let expected = ExpectedOutcome( + text: expectedText, + language: expectedLanguage, + sourceID: expectedSourceID, + allowedAutomaticActions: allowedActions, + checkpoints: checkpoints + ) + + return Trial( + schemaVersion: 1, + id: id, + groupID: item.groupID, + split: split, + languages: ["en", "uk"], + category: item.category, + label: item.label, + provenance: Provenance(kind: "synthetic", generator: generator, seed: 0, review: "pending-human"), + initial: InitialState( + sourceID: initialSourceID, + language: initialLanguage, + text: "", + caretUTF16: 0, + preferredCyrillic: "uk", + keyboardType: 91 + ), + events: events, + expected: expected + ) + } + + private static func findKey(for char: Character, in table: LayoutTable) -> (UInt16, [String])? { + let s = String(char) + var candidates: [(UInt16, [String], Int)] = [] + + for (kStr, outputs) in table.keyOutputs { + guard let kc = UInt16(kStr), kc < 65 else { continue } + if outputs.unmodified.output == s { + candidates.append((kc, [], 0)) + } else if outputs.shift.output == s { + candidates.append((kc, ["shift"], 1)) + } + } + + candidates.sort { $0.2 < $1.2 } + return candidates.first.map { ($0.0, $0.1) } + } +} diff --git a/Sources/LayoutBenchmark/SelfChecks.swift b/Sources/LayoutBenchmark/SelfChecks.swift new file mode 100644 index 0000000..dc467dd --- /dev/null +++ b/Sources/LayoutBenchmark/SelfChecks.swift @@ -0,0 +1,616 @@ +import Foundation +import Core +import Dictionary +import Utils + +/// Checks of the benchmark's own machinery. Each check drives the real scorer, validator, +/// editor, renderer or adapter; none re-implements the logic it is checking. +public enum SelfChecks { + private static var passedCount = 0 + private static var failedCount = 0 + + private static let abc = "com.apple.keylayout.ABC" + private static let ukrainian = "com.apple.keylayout.Ukrainian" + private static let tablesDir = URL(fileURLWithPath: "Benchmarks/layout-tables/v1") + + private static func check(_ condition: Bool, _ message: String) { + if condition { + passedCount += 1 + } else { + failedCount += 1 + print("FAIL: \(message)") + } + } + + public static func runAll() -> Bool { + passedCount = 0 + failedCount = 0 + + print("--- SelfChecks: Wilson interval calculation ---") + testWilsonInterval() + + print("--- SelfChecks: Precision null on zero attempts ---") + testPrecisionNullOnZeroAttempts() + + print("--- SelfChecks: Control adapters (preserve, replace, wrong, error, missing, unsupported, good) ---") + testControlAdapters() + + print("--- SelfChecks: Checkpoint damage, layout-only and unsafe actions are scored ---") + testCheckpointAndActionScoring() + + print("--- SelfChecks: Baseline gate ---") + testBaselineGate() + + print("--- SelfChecks: Validation catches schema, duplicate, leaking and label errors ---") + testValidationCatchesErrors() + testActualInputChecksums() + + print("--- SelfChecks: UTF-16 caret versus grapheme handling ---") + testCaretAndGraphemes() + + print("--- SelfChecks: Virtual editor suffix mismatch ---") + testSuffixMismatch() + + print("--- SelfChecks: Source switch rendering ---") + testSourceSwitchRendering() + + guard let tables = try? loadTables(from: tablesDir) else { + check(false, "Frozen layout tables must load from \(tablesDir.path)") + return finish() + } + let adapter = SwitchFixAdapter(tables: tables) + check(adapter.prewarm(), "Adapter dictionaries must be ready") + + print("--- SelfChecks: Source environment comes from the trial and the app's discovery ---") + testSourceEnvironment(tables: tables) + + print("--- SelfChecks: Replay of delete, switch feedback and unsupported trials ---") + testAdapterReplay(adapter: adapter, tables: tables) + + print("--- SelfChecks: Replay agrees with InputEngine using its real detector ---") + testEngineCrossCheck(adapter: adapter, tables: tables) + + return finish() + } + + private static func finish() -> Bool { + print("\nSelf-check results: \(passedCount) passed, \(failedCount) failed") + return failedCount == 0 + } + + // MARK: - Fixtures + + private static func trial( + _ id: String, + label: String, + events: [TrialEvent], + expected text: String, + expectedSource: String? = nil, + language: String = "en", + initialText: String = "", + caret: Int? = nil, + checkpoints: [Checkpoint] = [] + ) -> Trial { + let source = language == "en" ? abc : ukrainian + let expectedSourceID = expectedSource ?? source + return Trial( + id: id, + groupID: "g-\(id)", + split: "development", + languages: ["en", "uk"], + category: "test", + label: label, + initial: InitialState(sourceID: source, language: language, text: initialText, caretUTF16: caret ?? initialText.utf16.count, preferredCyrillic: "uk", keyboardType: 91), + events: events, + expected: ExpectedOutcome( + text: text, + language: expectedSourceID == ukrainian ? "uk" : "en", + sourceID: expectedSourceID, + allowedAutomaticActions: label == "repair" ? ["replaceSuffix", "switchSource"] : [], + checkpoints: checkpoints + ) + ) + } + + private static func generated(_ text: String, label: String, from: String, to: String, id: String) -> Trial? { + guard let abcTable = try? LayoutTable.load(from: tablesDir.appendingPathComponent("\(abc)@91.json")), + let ukTable = try? LayoutTable.load(from: tablesDir.appendingPathComponent("\(ukrainian)@91.json")) else { + return nil + } + let item = SeedItemDefinition(groupID: "g-\(id)", category: "self-check", label: label, sourceLayout: from, targetLayout: to, intendedText: text, isRepair: label == "repair") + return try? SeedCorpusGenerator.generateTrial(id: id, item: item, split: "development", abcTable: abcTable, ukTable: ukTable) + } + + private static let replace = EmittedAction(type: "replaceSuffix", eventIndex: 1, fromText: "g", toText: "п") + private static let switchToUkrainian = EmittedAction(type: "switchSource", eventIndex: 1, targetSource: ukrainian, targetLayout: "uk") + + private static var positive: Trial { + trial("pos1", label: "repair", events: [TrialEvent(type: "key", keyCode: 5, rendered: "g"), TrialEvent(type: "boundary", keyCode: 49, rendered: " ")], expected: "п ", expectedSource: ukrainian) + } + + private static var negative: Trial { + trial("neg1", label: "preserve", events: [TrialEvent(type: "key", keyCode: 0, rendered: "a"), TrialEvent(type: "boundary", keyCode: 49, rendered: " ")], expected: "a ") + } + + // MARK: - Scoring + + private static func testWilsonInterval() { + let wZero = WilsonScoreInterval.compute(successes: 0, total: 80)! + check(wZero.lower == 0.0, "Zero successes lower bound must be 0.0") + check(wZero.upper > 0.045 && wZero.upper < 0.047, "Wilson upper bound for 0/80 must be ~4.6% (was \(wZero.upper))") + + let wFull = WilsonScoreInterval.compute(successes: 80, total: 80)! + check(wFull.upper == 1.0, "Full successes upper bound must be 1.0") + check(wFull.lower > 0.95 && wFull.lower < 0.96, "Wilson lower bound for 80/80 must be ~95.4%") + + let w18 = WilsonScoreInterval.compute(successes: 18, total: 20)! + check(w18.lower > 0.69 && w18.lower < 0.71, "Wilson lower bound for 18/20 must be ~69.9%") + check(w18.upper > 0.96 && w18.upper < 0.98, "Wilson upper bound for 18/20 must be ~97.2%") + + check(WilsonScoreInterval.compute(successes: 0, total: 0) == nil, "0 total must return nil") + } + + private static func testPrecisionNullOnZeroAttempts() { + let report = Scorer.score(trials: [negative], results: [TrialResult(trialID: "neg1", finalText: "a ", finalSourceID: abc)]) + check(report.precision.rate == nil, "Precision rate must be nil when no repair is attempted") + check(report.precision.interval == nil, "Precision interval must be nil when no repair is attempted") + check(report.textDamageFPRate.rate == 0.0, "Text damage FP rate must be 0.0") + check(report.complete, "A run with every observation present is complete") + } + + private static func testControlAdapters() { + let trials = [positive, negative] + + let preserve = Scorer.score(trials: trials, results: [ + TrialResult(trialID: "pos1", finalText: "g ", finalSourceID: abc), + TrialResult(trialID: "neg1", finalText: "a ", finalSourceID: abc) + ]) + check(preserve.recall.rate == 0.0, "Always-preserve control must have 0% recall") + check(preserve.textDamageFPRate.rate == 0.0, "Always-preserve control must have 0% FP") + check(preserve.precision.rate == nil, "Always-preserve control must have undefined precision") + + let aggressive = Scorer.score(trials: trials, results: [ + TrialResult(trialID: "pos1", finalText: "п ", finalSourceID: ukrainian, actions: [replace, switchToUkrainian]), + TrialResult(trialID: "neg1", finalText: "ф ", finalSourceID: ukrainian, actions: [replace, switchToUkrainian]) + ]) + check(aggressive.recall.rate == 1.0, "Always-replace control recall must be 100%") + check(aggressive.textDamageFPRate.rate == 1.0, "Always-replace control text-damage FP must be 100%") + check(aggressive.precision.rate == 0.5, "Always-replace precision counts the negative intervention") + + let wrong = Scorer.score(trials: trials, results: [ + TrialResult(trialID: "pos1", finalText: "wrong ", finalSourceID: ukrainian, actions: [replace]), + TrialResult(trialID: "neg1", finalText: "a ", finalSourceID: abc) + ]) + check(wrong.recall.rate == 0.0, "Wrong repair control must have 0% recall") + check(wrong.fnWrongRepair == 1, "Wrong repair control must record fnWrongRepair") + check(wrong.precision.rate == 0.0, "A wrong repair is an attempt without success") + + let errors = Scorer.score(trials: trials, results: [ + TrialResult(trialID: "pos1", status: "error", finalText: "", finalSourceID: "", errorMessage: "boom"), + TrialResult(trialID: "neg1", status: "error", finalText: "", finalSourceID: "", errorMessage: "boom") + ]) + check(!errors.complete, "An error control must leave the run incomplete") + check(errors.errorTrials == 2 && errors.fnError == 1, "Errors are counted, the positive one as a failed repair") + check(errors.trueNegatives == 0 && errors.textDamageFPs == 0, "An erroring negative is neither preserved nor damaged") + check(errors.nNegative == 1 && errors.nPositive == 1, "Errors stay in the denominators") + check(errors.recall.rate == 0.0 && errors.precision.rate == nil, "Errors earn no recall or precision") + + let missing = Scorer.score(trials: trials, results: []) + check(!missing.complete && missing.missingTrials == 2, "Missing observations leave the run incomplete") + check(missing.trueNegatives == 0 && missing.nNegative == 1, "A missing negative cannot count as preserved") + check(missing.trialOutcomes["neg1"] == .negativeError, "A missing negative is recorded as an error outcome") + + let unsupported = Scorer.score(trials: trials, results: [ + TrialResult(trialID: "pos1", status: "unsupported", finalText: "", finalSourceID: ""), + TrialResult(trialID: "neg1", status: "unsupported", finalText: "", finalSourceID: "") + ]) + check(unsupported.unsupportedTrials == 2 && unsupported.nPositive == 0 && unsupported.nNegative == 0, "Unsupported trials leave every denominator") + check(unsupported.recall.rate == nil && unsupported.coverage.rate == 0.0, "Unsupported trials report coverage, not quality") + + let good = Scorer.score(trials: trials, results: [ + TrialResult(trialID: "pos1", finalText: "п ", finalSourceID: ukrainian, actions: [replace, switchToUkrainian]), + TrialResult(trialID: "neg1", finalText: "a ", finalSourceID: abc) + ]) + check(good.recall.rate == 1.0, "Known good control must have 100% recall") + check(good.textDamageFPRate.rate == 0.0, "Known good control must have 0% text damage FP") + check(good.layoutOnlyFPRate.rate == 0.0, "Known good control must have 0% layout only FP") + check(good.precision.rate == 1.0, "Known good control must have 100% precision") + check(good.complete && good.coverage.rate == 1.0, "Known good control is complete with full coverage") + } + + private static func testCheckpointAndActionScoring() { + let checkpointed = trial( + "pos-cp", + label: "repair", + events: [TrialEvent(type: "key", keyCode: 5, rendered: "g"), TrialEvent(type: "boundary", keyCode: 49, rendered: " ")], + expected: "п ", + expectedSource: ukrainian, + checkpoints: [Checkpoint(afterEvent: 2, text: "п ", language: "uk")] + ) + let late = TrialResult( + trialID: "pos-cp", + finalText: "п ", + finalSourceID: ukrainian, + actions: [replace, switchToUkrainian], + checkpoints: [ObservedCheckpoint(afterEvent: 2, text: "g ", language: "en", sourceID: abc)] + ) + check(Scorer.classify(trial: checkpointed, result: late) == .fnTransientDamage, "A repair that misses its checkpoint is not a true positive") + + let guarded = trial( + "neg-cp", + label: "preserve", + events: [TrialEvent(type: "key", keyCode: 0, rendered: "a"), TrialEvent(type: "boundary", keyCode: 49, rendered: " ")], + expected: "a ", + checkpoints: [Checkpoint(afterEvent: 1, text: "a", language: "en")] + ) + let restored = TrialResult( + trialID: "neg-cp", + finalText: "a ", + finalSourceID: abc, + checkpoints: [ObservedCheckpoint(afterEvent: 1, text: "ф", language: "en", sourceID: abc)] + ) + check(Scorer.classify(trial: guarded, result: restored) == .textDamageFP, "Damage seen at a checkpoint counts even when the final text is restored") + + let languageMismatch = TrialResult( + trialID: "neg-cp", + finalText: "a ", + finalSourceID: abc, + checkpoints: [ObservedCheckpoint(afterEvent: 1, text: "a", language: "uk", sourceID: ukrainian)] + ) + check(Scorer.classify(trial: guarded, result: languageMismatch) == .layoutOnlyFP, "Language-only mismatch at checkpoint is a layout-only false positive, not text damage") + + let switchOnly = TrialResult(trialID: "neg1", finalText: "a ", finalSourceID: ukrainian, actions: [switchToUkrainian]) + check(Scorer.classify(trial: negative, result: switchOnly) == .layoutOnlyFP, "A switch without a text edit is a layout-only false positive") + + let undone = TrialResult(trialID: "neg1", finalText: "a ", finalSourceID: abc, actions: [replace]) + check(Scorer.classify(trial: negative, result: undone) == .textDamageFP, "Any replacement on a negative counts as damage") + + let foreign = TrialResult(trialID: "pos1", finalText: "п ", finalSourceID: ukrainian, actions: [replace, EmittedAction(type: "pasteSelection", eventIndex: 1)]) + check(Scorer.classify(trial: positive, result: foreign) == .fnUnsafeMutation, "An action outside allowedAutomaticActions is unsafe") + + let prefixed = trial("pos-prefix", label: "repair", events: positive.events, expected: "keep п ", expectedSource: ukrainian, initialText: "keep ") + let clobbered = TrialResult(trialID: "pos-prefix", finalText: "kee п ", finalSourceID: ukrainian, actions: [replace]) + check(Scorer.classify(trial: prefixed, result: clobbered) == .fnUnsafeMutation, "Damage to the protected initial text is unsafe") + } + + private static func testBaselineGate() { + let trials = [positive, negative] + let good = Scorer.score(trials: trials, results: [ + TrialResult(trialID: "pos1", finalText: "п ", finalSourceID: ukrainian, actions: [replace, switchToUkrainian]), + TrialResult(trialID: "neg1", finalText: "a ", finalSourceID: abc) + ]) + let metadata = BenchmarkEnvironment.metadata(corpusDir: URL(fileURLWithPath: "/nonexistent"), trials: trials, profile: "boundary-layout-only", layer: .replay, commitOverride: "self-check") + let baseline = BaselineRecord(schemaVersion: 2, baselineVersion: "self-check", created: "", metadata: metadata, runnerChecks: [:], development: good, holdout: good, combined: good) + check(BaselineGate.regressions(current: good, baseline: baseline).isEmpty, "An unchanged run passes the gate") + + let empty = Scorer.score(trials: [], results: []) + check(BaselineGate.regressions(current: empty, baseline: baseline).map(\.trialID) == ["neg1", "pos1"], "Removed trial IDs fail the inventory gate") + let splitBaseline = BaselineRecord(schemaVersion: 2, baselineVersion: "split", created: "", metadata: metadata, runnerChecks: [:], development: good, holdout: empty, combined: good) + check(BaselineGate.regressions(current: empty, baseline: splitBaseline, split: "holdout").isEmpty, "Holdout gate must not require development IDs") + check(BaselineGate.regressions(current: empty, baseline: splitBaseline, split: "development").count == 2, "Development gate requires its own IDs") + check(BaselineGate.regressions(current: good, baseline: splitBaseline, split: "development").isEmpty, "Development gate ignores holdout inventory") + let unsupported = Scorer.score(trials: trials, results: trials.map { + TrialResult(trialID: $0.id, status: "unsupported", finalText: "", finalSourceID: "") + }) + let unsupportedBaseline = BaselineRecord(schemaVersion: 2, baselineVersion: "unsupported", created: "", metadata: metadata, runnerChecks: [:], development: unsupported, holdout: empty, combined: unsupported) + check(BaselineGate.regressions(current: empty, baseline: unsupportedBaseline, split: "development").count == 2, "Removed unsupported IDs also fail inventory") + + let regressed = Scorer.score(trials: trials, results: [ + TrialResult(trialID: "pos1", finalText: "g ", finalSourceID: abc), + TrialResult(trialID: "neg1", finalText: "ф ", finalSourceID: abc, actions: [replace]) + ]) + let findings = BaselineGate.regressions(current: regressed, baseline: baseline) + check(findings.map(\.trialID) == ["neg1", "pos1"], "A lost repair and a new false positive both fail the gate") + + let lostCoverage = Scorer.score(trials: trials, results: [ + TrialResult(trialID: "pos1", status: "unsupported", finalText: "", finalSourceID: ""), + TrialResult(trialID: "neg1", finalText: "a ", finalSourceID: abc) + ]) + check(BaselineGate.regressions(current: lostCoverage, baseline: baseline).map(\.trialID) == ["pos1"], "Silent coverage loss fails the gate") + } + + // MARK: - Validation + + private static func writeCorpus(_ dev: [Trial], _ holdout: [Trial], to dir: URL) { + let encoder = JSONEncoder() + func lines(_ trials: [Trial]) -> String { + trials.map { String(decoding: try! encoder.encode($0), as: UTF8.self) + "\n" }.joined() + } + try! lines(dev).write(to: dir.appendingPathComponent("development.jsonl"), atomically: true, encoding: .utf8) + try! lines(holdout).write(to: dir.appendingPathComponent("holdout.jsonl"), atomically: true, encoding: .utf8) + } + + private static func withSplit(_ t: Trial, _ split: String, id: String? = nil, group: String? = nil, label: String? = nil, review: String = "pending-human") -> Trial { + Trial( + id: id ?? t.id, + groupID: group ?? t.groupID, + split: split, + languages: t.languages, + category: t.category, + label: label ?? t.label, + provenance: Provenance(review: review), + initial: t.initial, + events: t.events, + expected: t.expected + ) + } + + private static func testValidationCatchesErrors() { + let tempDir = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("benchmark-test-\(UUID().uuidString)") + try? FileManager.default.createDirectory(at: tempDir, withIntermediateDirectories: true) + defer { try? FileManager.default.removeItem(at: tempDir) } + func validate() -> ValidationResult { try! CorpusValidator.validate(corpusDirectory: tempDir, tablesDirectory: tablesDir) } + + let a = trial("a", label: "preserve", events: [TrialEvent(type: "key", keyCode: 0, rendered: "a")], expected: "a") + let s = trial("s", label: "preserve", events: [TrialEvent(type: "key", keyCode: 1, rendered: "s")], expected: "s") + + writeCorpus([a, withSplit(s, "development", id: "a")], [], to: tempDir) + let duplicate = validate() + check(!duplicate.passed && duplicate.errors.contains { $0.contains("Duplicate trial ID") }, "Validation must fail on duplicate trial IDs") + check(duplicate.errors.contains { $0.contains("Missing manifest.json") }, "Validation must require a manifest") + + writeCorpus([withSplit(a, "development", group: "leak")], [withSplit(s, "holdout", group: "leak")], to: tempDir) + check(validate().errors.contains { $0.contains("Leaking groups") }, "Validation must fail on leaking groups between splits") + + let badRender = trial("bad", label: "preserve", events: [TrialEvent(type: "key", keyCode: 0, rendered: "z")], expected: "z") + writeCorpus([badRender], [], to: tempDir) + check(validate().errors.contains { $0.contains("does not match table output") }, "Validation must fail when rendered text does not match the table") + + writeCorpus([a, withSplit(a, "development", id: "a2", label: "abstain")], [], to: tempDir) + check(validate().warnings.contains { $0.contains("Contradictory labels") }, "Identical input with different labels must be reported (and fails --strict)") + + writeCorpus([a], [withSplit(a, "holdout", id: "a-holdout", group: "other")], to: tempDir) + let crossSplit = validate() + check(crossSplit.warnings.contains { $0.contains("Identical typed input in both splits") }, "Identical input in both splits must warn") + check(crossSplit.warnings.contains { $0.contains("in both splits") && $0.contains("Base text") }, "Shared base text across splits must warn") + + writeCorpus([withSplit(a, "development", review: "reviewed")], [], to: tempDir) + check(!validate().errors.contains { $0.contains("Provenance review") }, "Reviewed trials must be accepted") + writeCorpus([withSplit(a, "development", review: "approved-by-bot")], [], to: tempDir) + check(validate().errors.contains { $0.contains("Provenance review") }, "Unknown review states must fail") + + let otherType = Trial(id: "kt", groupID: "kt", split: "development", languages: ["en"], category: "test", label: "preserve", + initial: InitialState(sourceID: abc, language: "en", keyboardType: 40), + events: a.events, expected: a.expected) + writeCorpus([otherType], [], to: tempDir) + check(validate().errors.contains { $0.contains("Missing layout table for \(abc)@40") }, "A keyboard type without its own table must fail, not borrow another") + + check(CorpusValidator.verifyManifest(corpusDirectory: URL(fileURLWithPath: "Benchmarks/layout-corpus/v1"), tablesDirectory: tablesDir).isEmpty, "The frozen v1 manifest hashes must match its files") + } + + private static func testActualInputChecksums() { + let fm = FileManager.default + let temp = fm.temporaryDirectory.appendingPathComponent(UUID().uuidString) + defer { try? fm.removeItem(at: temp) } + do { + try fm.createDirectory(at: temp, withIntermediateDirectories: true) + let corpus = temp.appendingPathComponent("relocated-corpus") + let tables = temp.appendingPathComponent("relocated-tables") + try fm.copyItem(at: URL(fileURLWithPath: "Benchmarks/layout-corpus/v2"), to: corpus) + try fm.copyItem(at: tablesDir, to: tables) + check(CorpusValidator.verifyManifest(corpusDirectory: corpus, tablesDirectory: tables).isEmpty, "Relocated identical inputs verify without inferred roots") + let table = tables.appendingPathComponent("com.apple.keylayout.ABC@91.json") + var bytes = try Data(contentsOf: table) + bytes.append(Data("\n".utf8)) + try bytes.write(to: table) + let validation = try CorpusValidator.validate(corpusDirectory: corpus, tablesDirectory: tables) + check(validation.errors.contains { $0.contains("hash mismatch") && $0.contains("ABC@91") }, "Validator hashes the actual supplied tables") + do { + try CorpusValidator.requireVerifiedInputs(corpusDirectory: corpus, tablesDirectory: tables) + check(false, "Run/record input preflight must reject modified tables") + } catch { check(true, "Run/record input preflight rejects modified tables") } + try fm.copyItem(at: table, to: tables.appendingPathComponent("extra.json")) + check(CorpusValidator.verifyManifest(corpusDirectory: corpus, tablesDirectory: tables).contains { $0.contains("does not list table") }, "Unmanifested replay tables fail preflight") + try fm.removeItem(at: table) + check(CorpusValidator.verifyManifest(corpusDirectory: corpus, tablesDirectory: tables).contains { $0.contains("missing file") }, "Missing manifest tables fail preflight") + try Data("changed".utf8).write(to: corpus.appendingPathComponent("development.jsonl")) + check(CorpusValidator.verifyManifest(corpusDirectory: corpus, tablesDirectory: tables).contains { $0.contains("hash mismatch") && $0.contains("development.jsonl") }, "Modified corpus bytes fail preflight") + } catch { check(false, "Checksum regression fixture: \(error)") } + } + + private static func testCaretAndGraphemes() { + let tempDir = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("benchmark-caret-\(UUID().uuidString)") + try? FileManager.default.createDirectory(at: tempDir, withIntermediateDirectories: true) + defer { try? FileManager.default.removeItem(at: tempDir) } + + let text = "привіт 👋" + let midSurrogate = trial("mid", label: "preserve", events: [], expected: text, initialText: text, caret: text.utf16.count - 1) + writeCorpus([midSurrogate], [], to: tempDir) + let result = try! CorpusValidator.validate(corpusDirectory: tempDir, tablesDirectory: tablesDir) + check(result.errors.contains { $0.contains("not on a character boundary") }, "A caret inside a surrogate pair must fail validation") + + let midChar = trial("midchar", label: "preserve", events: [], expected: text, initialText: text, caret: 0) + writeCorpus([midChar], [], to: tempDir) + let midCharResult = try! CorpusValidator.validate(corpusDirectory: tempDir, tablesDirectory: tablesDir) + check(midCharResult.errors.contains { $0.contains("must be at end of initial text") }, "A non-terminal caret must fail validation") + + let atEnd = trial("end", label: "preserve", events: [], expected: text, initialText: text) + writeCorpus([atEnd], [], to: tempDir) + check(!(try! CorpusValidator.validate(corpusDirectory: tempDir, tablesDirectory: tablesDir)).errors.contains { $0.contains("caretUTF16") }, "A caret at the end is valid") + + var editor = VirtualEditor(text: "a👋") + editor.deleteBackward() + check(editor.text == "a", "One backspace deletes the whole emoji (two UTF-16 units, one grapheme)") + var combining = VirtualEditor(text: "е\u{0301}") + combining.deleteBackward() + check(combining.text.isEmpty, "One backspace deletes a letter with its combining mark") + } + + private static func testSuffixMismatch() { + var matched = VirtualEditor(text: "say test ") + let ok = matched.replaceSuffix(deleteCount: 5, with: "тест ", expectedSuffix: "test ") + check(ok.suffixMatched && !ok.underflow && matched.text == "say тест ", "A matching suffix is replaced exactly") + + var mismatched = VirtualEditor(text: "other word ") + let outcome = mismatched.replaceSuffix(deleteCount: 5, with: "тест ", expectedSuffix: "test ") + check(!outcome.suffixMatched, "A suffix mismatch is reported") + check(mismatched.text == "other тест ", "The editor deletes by count like TextCorrector, so the damage stays visible") + + var short = VirtualEditor(text: "ab") + check(short.replaceSuffix(deleteCount: 5, with: "x", expectedSuffix: "test ").underflow, "Deleting past the start is reported") + } + + private static func testSourceSwitchRendering() { + guard let tables = try? loadTables(from: tablesDir) else { + check(false, "Frozen layout tables must load") + return + } + let renderer = KeyRenderer(tables: tables) + let keyA = TrialEvent(type: "key", keyCode: 0, rendered: "a") + check(renderer.render(keyA, sourceID: abc, keyboardType: 91, initialSourceID: abc) == .rendered("a"), "The initial source uses the validated rendered text") + check(renderer.render(keyA, sourceID: ukrainian, keyboardType: 91, initialSourceID: abc) == .rendered("ф"), "After a switch the key renders through the new source's table") + check(renderer.render(keyA, sourceID: ukrainian, keyboardType: 40, initialSourceID: abc) == .unavailable(status: "error", reason: "missing_layout_table"), "A missing table is an error, never the original text") + let optionE = TrialEvent(type: "key", keyCode: 14, modifiers: ["option"], rendered: "e") + check(renderer.render(optionE, sourceID: abc, keyboardType: 91, initialSourceID: ukrainian) == .unavailable(status: "unsupported", reason: "rejected_stroke_deadKey"), "A dead key after a switch is unsupported") + } + + // MARK: - Adapter + + private static func testSourceEnvironment(tables: [String: LayoutTable]) { + guard let repair = generated("привіт ", label: "repair", from: "en", to: "uk", id: "env") else { + check(false, "Generator must build a repair trial") + return + } + guard case .success(let environment) = TrialSourceEnvironment.make(trial: repair, tables: tables, allowedLayouts: SwitchFixAdapter.profileLayouts) else { + check(false, "Environment must build from the frozen tables") + return + } + check(environment.descriptors[ukrainian]?.ukrainianVariant == .legacy, "Apple 'Ukrainian' (s=и, b=і) is classified legacy, as the app does") + check(environment.descriptors[abc]?.supportedLayouts == [.english], "ABC supports English only") + let config = environment.detectionConfiguration(currentSourceID: abc, currentLayout: .english) + check(config.ukrainianToVariant == .legacy, "Conversions target the variant of the trial's Ukrainian source") + check(config.activeSourceSupportedLayouts == [.english], "Active source layouts come from discovery") + + let pc = Trial(id: "pc", groupID: "pc", split: "development", languages: ["en", "uk"], category: "test", label: "repair", + initial: repair.initial, events: repair.events, + expected: ExpectedOutcome(text: "привіт ", language: "uk", sourceID: "com.apple.keylayout.Ukrainian-PC", allowedAutomaticActions: ["replaceSuffix", "switchSource"])) + if case .success(let pcEnvironment) = TrialSourceEnvironment.make(trial: pc, tables: tables, allowedLayouts: SwitchFixAdapter.profileLayouts) { + check(pcEnvironment.detectionConfiguration(currentSourceID: abc, currentLayout: .english).ukrainianToVariant == .standard, "Ukrainian-PC is classified standard") + } else { + check(false, "Ukrainian-PC environment must build") + } + } + + private static func testAdapterReplay(adapter: SwitchFixAdapter, tables: [String: LayoutTable]) { + if let clean = generated("привіт ", label: "repair", from: "en", to: "uk", id: "typo") { + // Type a stray key and delete it before the last letter. + var events = clean.events + events.insert(contentsOf: [TrialEvent(type: "key", keyCode: 7, rendered: "x"), TrialEvent(type: "delete", keyCode: 51, rendered: "")], at: events.count - 2) + let typo = Trial(id: "typo", groupID: "typo", split: "development", languages: clean.languages, category: "test", label: "repair", + initial: clean.initial, events: events, expected: ExpectedOutcome(text: clean.expected.text, language: "uk", sourceID: ukrainian, allowedAutomaticActions: clean.expected.allowedAutomaticActions)) + let result = adapter.executeTrial(trial: typo) + check(result.finalText == "привіт ", "A delete inside a word is replayed on screen and in the buffer (got '\(result.finalText)')") + check(Scorer.classify(trial: typo, result: result) == .truePositive, "The corrected word after a delete is a true positive") + } else { + check(false, "Generator must build the delete fixture") + } + + if let twoWords = generated("добрий день ", label: "repair", from: "en", to: "uk", id: "two-words") { + let result = adapter.executeTrial(trial: twoWords) + check(result.finalText == "добрий день ", "After the first word switches the source, later keys render as Ukrainian (got '\(result.finalText)')") + check(result.finalSourceID == ukrainian, "The replay ends on the switched source") + check(Scorer.classify(trial: twoWords, result: result) == .truePositive, "A two-word repair on Apple 'Ukrainian' is a true positive") + let decision = adapter.executeTrial(trial: twoWords, layer: .decision) + let edits = { (r: TrialResult) in r.actions.filter { $0.type == "replaceSuffix" }.count } + check(edits(result) == 1, "With switch feedback only the first word needs a repair") + check(edits(decision) == 2, "Without feedback the second word is still typed on ABC and needs its own repair") + + for event in [TrialEvent(type: "key", keyCode: 36, rendered: "\r"), + TrialEvent(type: "unknown", keyCode: 0, rendered: "")] { + let failing = Trial(id: "post-intervention", groupID: "post-intervention", split: "development", + languages: twoWords.languages, category: "test", label: "repair", + initial: twoWords.initial, events: twoWords.events + [event], expected: twoWords.expected) + let failed = adapter.executeTrial(trial: failing) + check(!failed.actions.isEmpty && failed.status == "error", "Unsupported input after an intervention remains an error") + check(Scorer.classify(trial: failing, result: failed) == .fnError, "A post-intervention error remains in quality denominators") + } + } else { + check(false, "Generator must build the two-word trial") + } + + let caretInside = trial("caret", label: "preserve", events: negative.events, expected: "x a ", initialText: "x ", caret: 0) + let caretResult = adapter.executeTrial(trial: caretInside) + check(caretResult.status == "unsupported" && caretResult.reasonCodes.contains("caret_not_at_end"), "A caret that is not at the end is unsupported, not approximated") + + let typeFortyTrial = Trial(id: "kt40", groupID: "kt40", split: "development", languages: ["en", "uk"], category: "test", label: "preserve", + initial: InitialState(sourceID: abc, language: "en", keyboardType: 40), events: negative.events, expected: negative.expected) + let typeForty = adapter.executeTrial(trial: typeFortyTrial) + check(typeForty.status == "error" && typeForty.reasonCodes.contains("missing_layout_table"), "A trial without its keyboard type's tables is an error") + } + + /// Feeds the same trials through `InputEngine` with its real detector and an injected + /// emission, and checks the engine's planned edit leaves the same text and switch. + private static func testEngineCrossCheck(adapter: SwitchFixAdapter, tables: [String: LayoutTable]) { + let cases: [(String, String, String, String)] = [ + ("привіт ", "repair", "en", "uk"), + ("робота ", "repair", "en", "uk"), + ("hello ", "repair", "uk", "en"), + ("apple ", "preserve", "en", "en"), + ("яблуко ", "preserve", "uk", "uk"), + ("vs ", "preserve", "en", "en") + ] + for (index, (text, label, from, to)) in cases.enumerated() { + guard let trial = generated(text, label: label, from: from, to: to, id: "xcheck-\(index)"), + case .success(let environment) = TrialSourceEnvironment.make(trial: trial, tables: tables, allowedLayouts: SwitchFixAdapter.profileLayouts), + let initialLayout = layout(forLanguageCode: trial.initial.language) else { + check(false, "Cross-check fixture \(index) must build") + continue + } + let replay = adapter.executeTrial(trial: trial) + + let context = InputContextSnapshot(epoch: 1, frontmostPID: 100, appAllowed: true, layout: initialLayout, inputSourceID: trial.initial.sourceID, secureFocus: .notSecure) + let store = CaptureStateStore(context: context, hotkeys: HotkeyConfiguration(hotkeyModifiers: 0)) + let detector = LayoutDetector() + detector.preferredSourceIDProvider = { environment.preferredSourceIDs[$0] } + let lock = NSLock() + var plans: [CorrectionPlan] = [] + let engine = InputEngine( + captureState: store, + initialContext: context, + preferences: InputPreferencesSnapshot(isEnabled: true, correctionMode: .automatic), + detector: detector, + correctionEmission: { plan in + lock.lock(); plans.append(plan); lock.unlock() + return true + } + ) + let config = environment.detectionConfiguration(currentSourceID: trial.initial.sourceID, currentLayout: initialLayout) + engine.updateDetectionConfiguration( + allowedLayouts: config.allowedLayouts, + preferredCyrillicLayout: config.preferredCyrillicLayout, + activeSourceSupportedLayouts: config.activeSourceSupportedLayouts, + ukrainianFromVariant: config.ukrainianFromVariant, + ukrainianToVariant: config.ukrainianToVariant + ) + + var screen = VirtualEditor(text: trial.initial.text) + for (timestamp, event) in trial.events.enumerated() { + screen.type(event.rendered) + let kind: CapturedInput.Kind = event.type == "boundary" ? .boundary(event.rendered) : KeyboardMonitor.capturedKind(forText: event.rendered) + engine.enqueue(store.capture( + timestamp: UInt64(timestamp + 1), + kind: kind, + keyCode: event.keyCode, + flagsRawValue: event.modifiers.contains("shift") ? 0x020000 : 0, + isAutorepeat: false, + sourcePID: 100, + sourceUserData: 0 + )) + } + let drained = DispatchSemaphore(value: 0) + engine.drainPipeline { drained.signal() } + check(drained.wait(timeout: .now() + 5) == .success, "Engine pipeline drains for '\(text)'") + + lock.lock(); let emitted = plans; lock.unlock() + for plan in emitted { + _ = screen.replaceSuffix(deleteCount: plan.deleteCount, with: plan.replacementText, expectedSuffix: plan.originalText + plan.boundaryText) + } + let engineTarget = emitted.last?.targetLayout.map(languageCode(for:)) + let replayTarget = replay.actions.last(where: { $0.type == "switchSource" })?.targetLayout + check(screen.text == replay.finalText, "Replay and engine leave the same text for '\(text)' (engine '\(screen.text)', replay '\(replay.finalText)')") + check(engineTarget == replayTarget, "Replay and engine request the same switch for '\(text)'") + let engineEdits = emitted.filter { $0.deleteCount > 0 || !$0.replacementText.isEmpty }.count + check(engineEdits == replay.actions.filter { $0.type == "replaceSuffix" }.count, "Replay and engine apply the same number of text edits for '\(text)'") + } + } +} diff --git a/Sources/LayoutBenchmark/SwitchFixAdapter.swift b/Sources/LayoutBenchmark/SwitchFixAdapter.swift new file mode 100644 index 0000000..f427a90 --- /dev/null +++ b/Sources/LayoutBenchmark/SwitchFixAdapter.swift @@ -0,0 +1,327 @@ +import Foundation +import Core +import Dictionary +import Utils + +public struct EmittedAction: Codable, Equatable, Sendable { + public let type: String + public let eventIndex: Int + public let fromText: String? + public let toText: String? + public let targetSource: String? + public let targetLayout: String? + + public init( + type: String, + eventIndex: Int, + fromText: String? = nil, + toText: String? = nil, + targetSource: String? = nil, + targetLayout: String? = nil + ) { + self.type = type + self.eventIndex = eventIndex + self.fromText = fromText + self.toText = toText + self.targetSource = targetSource + self.targetLayout = targetLayout + } +} + +public struct ObservedCheckpoint: Codable, Equatable, Sendable { + public let afterEvent: Int + public let text: String + public let language: String + public let sourceID: String + + public init(afterEvent: Int, text: String, language: String, sourceID: String) { + self.afterEvent = afterEvent + self.text = text + self.language = language + self.sourceID = sourceID + } +} + +public struct TrialResult: Codable, Equatable, Sendable { + public let protocolVersion: Int + public let adapter: String + public let adapterVersion: String + public let trialID: String + public let status: String + public let finalText: String + public let finalSourceID: String + public let actions: [EmittedAction] + public let checkpoints: [ObservedCheckpoint] + public let reasonCodes: [String] + public let durationMs: Double + public let errorMessage: String? + + public init( + protocolVersion: Int = 1, + adapter: String = "switchfix", + adapterVersion: String = "1.0", + trialID: String, + status: String = "ok", + finalText: String, + finalSourceID: String, + actions: [EmittedAction] = [], + checkpoints: [ObservedCheckpoint] = [], + reasonCodes: [String] = [], + durationMs: Double = 0, + errorMessage: String? = nil + ) { + self.protocolVersion = protocolVersion + self.adapter = adapter + self.adapterVersion = adapterVersion + self.trialID = trialID + self.status = status + self.finalText = finalText + self.finalSourceID = finalSourceID + self.actions = actions + self.checkpoints = checkpoints + self.reasonCodes = reasonCodes + self.durationMs = durationMs + self.errorMessage = errorMessage + } +} + +public enum BenchmarkLayer: String, Sendable { + case decision + case replay + case app +} + +public final class SwitchFixAdapter: @unchecked Sendable { + /// Profile `boundary-layout-only`: English and Ukrainian only. + public static let profileLayouts: Set = [.english, .ukrainian] + + private let tables: [String: LayoutTable] + private let renderer: KeyRenderer + private var isReady = false + + public init(tables: [String: LayoutTable]) { + self.tables = tables + self.renderer = KeyRenderer(tables: tables) + } + + public func prewarm() -> Bool { + DictionaryLoader.shared.enableTextFallbackForTesting() + let enLoaded = DictionaryLoader.shared.prewarm(language: .english) + let ukLoaded = DictionaryLoader.shared.prewarm(language: .ukrainian) + isReady = enLoaded && ukLoaded + return isReady + } + + /// Replays a trial on the real `InputStateMachine` and `LayoutDetector`. + /// + /// `replay` feeds automatic source switches back into rendering and context; `decision` + /// records the same proposals without feedback, so later keys keep the initial source. + public func executeTrial(trial: Trial, layer: BenchmarkLayer = .replay) -> TrialResult { + func unavailable(_ failure: AdapterFailure, text: String = "", sourceID: String = "", actions: [EmittedAction] = [], checkpoints: [ObservedCheckpoint] = [], reasons: Set = []) -> TrialResult { + TrialResult( + trialID: trial.id, + status: failure.status, + finalText: text, + finalSourceID: sourceID, + actions: actions, + checkpoints: checkpoints, + reasonCodes: reasons.union([failure.reason]).sorted(), + errorMessage: failure.reason + ) + } + + guard isReady else { + return unavailable(AdapterFailure(status: "error", reason: "dictionaries_not_ready")) + } + guard let initialLayout = layout(forLanguageCode: trial.initial.language), + SwitchFixAdapter.profileLayouts.contains(initialLayout) else { + return unavailable(AdapterFailure(status: "unsupported", reason: "language_outside_profile")) + } + guard trial.initial.caretUTF16 == trial.initial.text.utf16.count else { + return unavailable(AdapterFailure(status: "unsupported", reason: "caret_not_at_end")) + } + + let environment: TrialSourceEnvironment + switch TrialSourceEnvironment.make(trial: trial, tables: tables, allowedLayouts: SwitchFixAdapter.profileLayouts) { + case .success(let value): environment = value + case .failure(let failure): return unavailable(failure) + } + + let startedAt = DispatchTime.now().uptimeNanoseconds + var activeLayout = initialLayout + var activeSourceID = trial.initial.sourceID + var editor = VirtualEditor(text: trial.initial.text) + + let detector = LayoutDetector() + detector.preferredSourceIDProvider = { environment.preferredSourceIDs[$0] } + var config = environment.detectionConfiguration(currentSourceID: activeSourceID, currentLayout: activeLayout) + + var contextSnapshot = InputContextSnapshot( + epoch: 1, + frontmostPID: 100, + appAllowed: true, + layout: activeLayout, + inputSourceID: activeSourceID, + secureFocus: .notSecure + ) + var stateMachine = InputStateMachine( + context: contextSnapshot, + preferences: InputPreferencesSnapshot(isEnabled: true, correctionMode: .automatic) + ) + + var actions: [EmittedAction] = [] + var observedCheckpoints = [ObservedCheckpoint(afterEvent: 0, text: editor.text, language: languageCode(for: activeLayout), sourceID: activeSourceID)] + var reasons: Set = [] + var sequence: UInt64 = 0 + + for (idx, event) in trial.events.enumerated() { + sequence += 1 + + let kind: CapturedInput.Kind + switch event.type { + case "key", "boundary": + let rendered: String + switch renderer.render(event, sourceID: activeSourceID, keyboardType: environment.keyboardType, initialSourceID: trial.initial.sourceID) { + case .rendered(let value): + rendered = value + case .unavailable(let status, let reason): + let effectiveStatus = actions.isEmpty ? status : "error" + let effectiveReason = actions.isEmpty ? reason : "post_intervention_\(reason)" + return unavailable(AdapterFailure(status: effectiveStatus, reason: effectiveReason), text: editor.text, sourceID: activeSourceID, actions: actions, checkpoints: observedCheckpoints, reasons: reasons) + } + // The keystroke reaches the application before SwitchFix sees the boundary. + editor.type(rendered) + kind = event.type == "boundary" ? .boundary(rendered) : KeyboardMonitor.capturedKind(forText: rendered) + case "delete": + editor.deleteBackward() + kind = .delete + default: + let effectiveStatus = actions.isEmpty ? "unsupported" : "error" + let effectiveReason = actions.isEmpty ? "event_type_\(event.type)" : "post_intervention_event_type_\(event.type)" + return unavailable(AdapterFailure(status: effectiveStatus, reason: effectiveReason), text: editor.text, sourceID: activeSourceID, actions: actions, checkpoints: observedCheckpoints, reasons: reasons) + } + + var flags: UInt64 = 0 + if event.modifiers.contains("shift") { flags |= 0x020000 } + if event.modifiers.contains("caps") { flags |= 0x010000 } + if event.modifiers.contains("option") { flags |= 0x080000 } + + let input = CapturedInput( + sequence: sequence, + timestamp: sequence, + kind: kind, + keyCode: event.keyCode, + flagsRawValue: flags, + isAutorepeat: false, + sourcePID: 100, + sourceUserData: 0, + context: contextSnapshot, + editGeneration: 1 + ) + + for command in stateMachine.consume(input) { + switch command { + case .append, .deleteLast, .untrackedWordHeld: + break + case .invalidate, .nativeUndo: + detector.reset() + case .requestManualCorrection, .requestRevert: + reasons.insert("manual_command_ignored") + case .flush(let word, let boundary, let seq, let ctx): + let request = assembleDetectionRequest( + trigger: .automaticBoundary, + word: word, + boundary: boundary, + sequence: seq, + editGeneration: 1, + correctionEpoch: 0, + context: ctx, + run: stateMachine.run + ) + guard let detection = evaluateDetection(detector: detector, request: request, configuration: config) else { + continue + } + + let edit: CorrectionEdit + switch planCorrectionEdit(result: detection, boundary: boundary) { + case .success(let value): + edit = value + case .failure(let rejection): + reasons.insert("cancelled_\(rejection.rawValue)") + continue + } + + if edit.deleteCount > 0 || !edit.replacementText.isEmpty { + let outcome = editor.replaceSuffix( + deleteCount: edit.deleteCount, + with: edit.replacementText, + expectedSuffix: detection.originalWord + boundary + ) + if !outcome.suffixMatched { reasons.insert("suffix_mismatch") } + if outcome.underflow { reasons.insert("delete_underflow") } + actions.append(EmittedAction( + type: "replaceSuffix", + eventIndex: idx, + fromText: detection.originalWord, + toText: detection.convertedWord + )) + } + + guard let target = edit.switchTarget else { continue } + guard let targetSource = environment.preferredSourceIDs[target] else { + reasons.insert("no_source_for_target") + continue + } + actions.append(EmittedAction( + type: "switchSource", + eventIndex: idx, + targetSource: targetSource, + targetLayout: languageCode(for: target) + )) + guard layer == .replay else { continue } + + // Mirrors the app after its own switch: new context (focus assumed resolved + // before the next key), detector reset, configuration recomputed. + activeSourceID = targetSource + activeLayout = target + contextSnapshot = InputContextSnapshot( + epoch: contextSnapshot.epoch + 1, + frontmostPID: 100, + appAllowed: true, + layout: activeLayout, + inputSourceID: activeSourceID, + secureFocus: .notSecure + ) + _ = stateMachine.updateContext(contextSnapshot) + detector.reset() + config = environment.detectionConfiguration(currentSourceID: activeSourceID, currentLayout: activeLayout) + } + } + + observedCheckpoints.append(ObservedCheckpoint( + afterEvent: idx + 1, + text: editor.text, + language: languageCode(for: activeLayout), + sourceID: activeSourceID + )) + } + + let finalSourceID: String + if layer == .replay { + finalSourceID = activeSourceID + } else { + finalSourceID = actions.last(where: { $0.type == "switchSource" })?.targetSource ?? activeSourceID + } + + return TrialResult( + trialID: trial.id, + status: "ok", + finalText: editor.text, + finalSourceID: finalSourceID, + actions: actions, + checkpoints: observedCheckpoints, + reasonCodes: reasons.sorted(), + durationMs: Double(DispatchTime.now().uptimeNanoseconds &- startedAt) / 1_000_000.0 + ) + } +} diff --git a/Sources/LayoutBenchmark/TableExporter.swift b/Sources/LayoutBenchmark/TableExporter.swift new file mode 100644 index 0000000..eff3d30 --- /dev/null +++ b/Sources/LayoutBenchmark/TableExporter.swift @@ -0,0 +1,204 @@ +import Foundation +import Carbon + +public enum TableExporter { + public static func osBuildVersion() -> String { + var size = 0 + sysctlbyname("kern.osversion", nil, &size, nil, 0) + guard size > 0 else { return "unknown" } + var buffer = [CChar](repeating: 0, count: size) + sysctlbyname("kern.osversion", &buffer, &size, nil, 0) + return String(cString: buffer) + } + + public static func osVersionString() -> String { + let v = ProcessInfo.processInfo.operatingSystemVersion + return "\(v.majorVersion).\(v.minorVersion).\(v.patchVersion)" + } + + public static func translateKey( + layoutData: Data, + keyCode: UInt16, + modifierKeyState: UInt32, + keyboardType: UInt32 + ) -> StrokeOutput { + return layoutData.withUnsafeBytes { ptr -> StrokeOutput in + guard let base = ptr.baseAddress?.assumingMemoryBound(to: UCKeyboardLayout.self) else { + return StrokeOutput(output: nil, rejection: "nullLayoutPointer") + } + + var deadKeyState: UInt32 = 0 + var chars = [UniChar](repeating: 0, count: 255) + var actualLength = 0 + + let status = UCKeyTranslate( + base, + keyCode, + UInt16(kUCKeyActionDown), + modifierKeyState, + keyboardType, + 0, // options 0: return no dead keys, pure deadKeyState tracking + &deadKeyState, + chars.count, + &actualLength, + &chars + ) + + if status != noErr { + return StrokeOutput(output: nil, rejection: "statusError") + } + + if deadKeyState != 0 { + return StrokeOutput(output: nil, rejection: "deadKey") + } + + if actualLength == 0 { + return StrokeOutput(output: nil, rejection: "empty") + } + + if actualLength > 1 { + return StrokeOutput(output: nil, rejection: "multiScalar") + } + + let codeUnit = chars[0] + guard let scalar = UnicodeScalar(codeUnit) else { + return StrokeOutput(output: nil, rejection: "invalidUTF16") + } + + let val = scalar.value + if val == 0x20 { + // Space is explicitly allowed + return StrokeOutput(output: " ", rejection: nil) + } + + // Reject surrogates and private use area + if (val >= 0xD800 && val <= 0xDFFF) || (val >= 0xE000 && val <= 0xF8FF) || val > 0xFFFF { + return StrokeOutput(output: nil, rejection: "privateUseOrSurrogate") + } + + // Reject controls, newlines, C0 and C1 controls + if CharacterSet.controlCharacters.contains(scalar) || + CharacterSet.newlines.contains(scalar) || + val < 0x20 || + (val >= 0x7F && val <= 0x9F) { + return StrokeOutput(output: nil, rejection: "control") + } + + return StrokeOutput(output: String(Character(scalar)), rejection: nil) + } + } + + public static func exportTable( + source: TISInputSource, + sourceID: String, + keyboardType: UInt32 + ) -> LayoutTable? { + guard let layoutDataRef = TISGetInputSourceProperty(source, kTISPropertyUnicodeKeyLayoutData) else { + return nil + } + let layoutData = unsafeBitCast(layoutDataRef, to: CFData.self) as Data + + var keyOutputs: [String: KeyCodeOutputs] = [:] + + // Modifiers shifted right 8 bits: + // unmodified: 0 + // shift: 2 (shiftKey >> 8) + // caps: 4 (alphaLock >> 8) + // shiftCaps: 6 ((shiftKey | alphaLock) >> 8) + // option: 8 (optionKey >> 8) + // shiftOption: 10 ((shiftKey | optionKey) >> 8) + // capsOption: 12 ((alphaLock | optionKey) >> 8) + // shiftCapsOption: 14 ((shiftKey | alphaLock | optionKey) >> 8) + + for keyCode in UInt16(0)..<128 { + let unmodified = translateKey(layoutData: layoutData, keyCode: keyCode, modifierKeyState: 0, keyboardType: keyboardType) + let shift = translateKey(layoutData: layoutData, keyCode: keyCode, modifierKeyState: 2, keyboardType: keyboardType) + let caps = translateKey(layoutData: layoutData, keyCode: keyCode, modifierKeyState: 4, keyboardType: keyboardType) + let shiftCaps = translateKey(layoutData: layoutData, keyCode: keyCode, modifierKeyState: 6, keyboardType: keyboardType) + let option = translateKey(layoutData: layoutData, keyCode: keyCode, modifierKeyState: 8, keyboardType: keyboardType) + let shiftOption = translateKey(layoutData: layoutData, keyCode: keyCode, modifierKeyState: 10, keyboardType: keyboardType) + let capsOption = translateKey(layoutData: layoutData, keyCode: keyCode, modifierKeyState: 12, keyboardType: keyboardType) + let shiftCapsOption = translateKey(layoutData: layoutData, keyCode: keyCode, modifierKeyState: 14, keyboardType: keyboardType) + + let outputs = KeyCodeOutputs( + keyCode: keyCode, + unmodified: unmodified, + shift: shift, + caps: caps, + shiftCaps: shiftCaps, + option: option, + shiftOption: shiftOption, + capsOption: capsOption, + shiftCapsOption: shiftCapsOption + ) + keyOutputs[String(keyCode)] = outputs + } + + return LayoutTable( + schemaVersion: 1, + sourceID: sourceID, + keyboardType: keyboardType, + osVersion: osVersionString(), + osBuild: osBuildVersion(), + keyOutputs: keyOutputs + ) + } + + public static func findAllInstalledSources() -> [String: TISInputSource] { + guard let list = TISCreateInputSourceList(nil, true)?.takeRetainedValue() as? [TISInputSource] else { + return [:] + } + var result: [String: TISInputSource] = [:] + for src in list { + guard let typePtr = TISGetInputSourceProperty(src, kTISPropertyInputSourceType) else { continue } + let type = Unmanaged.fromOpaque(typePtr).takeUnretainedValue() as String + guard type == "TISTypeKeyboardLayout" else { continue } + guard let idPtr = TISGetInputSourceProperty(src, kTISPropertyInputSourceID) else { continue } + let id = Unmanaged.fromOpaque(idPtr).takeUnretainedValue() as String + result[id] = src + } + return result + } + + public static func exportAllStandardTables(outputDirectory: URL, keyboardTypes: [UInt32]) throws -> [URL] { + let sources = findAllInstalledSources() + let standardSourceIDs = [ + "com.apple.keylayout.ABC", + "com.apple.keylayout.US", + "com.apple.keylayout.British", + "com.apple.keylayout.Dvorak", + "com.apple.keylayout.Colemak", + "com.apple.keylayout.Ukrainian", + "com.apple.keylayout.Ukrainian-PC", + "com.apple.keylayout.Ukrainian-QWERTY", + "com.apple.keylayout.Russian", + "com.apple.keylayout.RussianWin", + "com.apple.keylayout.Russian-Phonetic" + ] + + let encoder = JSONEncoder() + encoder.outputFormatting = [.prettyPrinted, .sortedKeys] + + var writtenURLs: [URL] = [] + try FileManager.default.createDirectory(at: outputDirectory, withIntermediateDirectories: true) + + for sourceID in standardSourceIDs { + guard let source = sources[sourceID] else { + print("Warning: Source not installed on this system: \(sourceID)") + continue + } + for kbdType in keyboardTypes { + guard let table = exportTable(source: source, sourceID: sourceID, keyboardType: kbdType) else { + print("Warning: Failed to export table for \(sourceID)@\(kbdType)") + continue + } + let fileURL = outputDirectory.appendingPathComponent("\(sourceID)@\(kbdType).json") + let data = try encoder.encode(table) + try data.write(to: fileURL) + writtenURLs.append(fileURL) + } + } + + return writtenURLs + } +} diff --git a/Sources/LayoutBenchmark/main.swift b/Sources/LayoutBenchmark/main.swift new file mode 100644 index 0000000..7379630 --- /dev/null +++ b/Sources/LayoutBenchmark/main.swift @@ -0,0 +1,455 @@ +import Foundation + +func main() { + let args = CommandLine.arguments + if args.count < 2 { + printUsage() + exit(1) + } + + let command = args[1] + let options = Options(Array(args.dropFirst(2))) + switch command { + case "export-layout-tables": + handleExportLayoutTables(options) + case "generate-seed-corpus": + handleGenerateSeedCorpus(options) + case "validate": + handleValidate(options) + case "run": + handleRun(options) + case "summarize": + handleSummarize(options) + case "self-check": + let passed = SelfChecks.runAll() + exit(passed ? 0 : 1) + case "record-baseline": + handleRecordBaseline(options) + case "compare": + print("competitor_unavailable: compare is not implemented yet (Plan-Item 03.5)") + exit(1) + default: + print("Unknown command: \(command)") + printUsage() + exit(1) + } +} + +func printUsage() { + print(""" + LayoutBenchmark: SwitchFix Layout Reliability & False-Positive Benchmark + + Commands: + export-layout-tables --output [--keyboard-types ] + generate-seed-corpus --version v1|v2 [--corpus ] [--tables ] [--commit ] + validate [--corpus ] [--tables ] [--strict] + run [--adapter switchfix] [--split development|holdout|all] [--layer decision|replay] [--profile boundary-layout-only] + [--corpus ] [--tables ] [--output ] [--baseline ] + record-baseline --baselines [--corpus ] [--tables ] [--commit ] [--runner-log =]... + summarize [--results ] + self-check + compare + """) +} + +/// `--name value` options; repeatable options keep every value. +struct Options { + private var values: [String: [String]] = [:] + private var flags: Set = [] + + init(_ args: [String]) { + var i = 0 + while i < args.count { + let arg = args[i] + if arg.hasPrefix("--"), i + 1 < args.count, !args[i + 1].hasPrefix("--") { + values[arg, default: []].append(args[i + 1]) + i += 2 + } else { + flags.insert(arg) + i += 1 + } + } + } + + func value(_ name: String) -> String? { values[name]?.last } + func all(_ name: String) -> [String] { values[name] ?? [] } + func flag(_ name: String) -> Bool { flags.contains(name) } + func url(_ name: String, default path: String) -> URL { URL(fileURLWithPath: value(name) ?? path) } +} + +func loadTables(from tablesDir: URL) throws -> [String: LayoutTable] { + var tables: [String: LayoutTable] = [:] + let tableFiles = try FileManager.default.contentsOfDirectory(at: tablesDir, includingPropertiesForKeys: nil) + .filter { $0.pathExtension == "json" } + for tURL in tableFiles { + tables[tURL.deletingPathExtension().lastPathComponent] = try LayoutTable.load(from: tURL) + } + return tables +} + +func loadTrials(corpusDir: URL, split: String) throws -> [Trial] { + let splits: [String] + switch split { + case "development", "holdout": splits = [split] + case "all": splits = ["development", "holdout"] + default: throw NSError(domain: "LayoutBenchmark", code: 1, userInfo: [NSLocalizedDescriptionKey: "Unknown split '\(split)'"]) + } + let decoder = JSONDecoder() + var trials: [Trial] = [] + for name in splits { + let content = try String(contentsOf: corpusDir.appendingPathComponent("\(name).jsonl"), encoding: .utf8) + for line in content.components(separatedBy: .newlines) { + let trimmed = line.trimmingCharacters(in: .whitespacesAndNewlines) + if trimmed.isEmpty { continue } + trials.append(try decoder.decode(Trial.self, from: Data(trimmed.utf8))) + } + } + return trials +} + +func prettyEncoder() -> JSONEncoder { + let encoder = JSONEncoder() + encoder.outputFormatting = [.prettyPrinted, .sortedKeys] + return encoder +} + +func handleExportLayoutTables(_ options: Options) { + guard let output = options.value("--output") else { + print("export-layout-tables needs --output ; frozen table versions are never regenerated in place") + exit(1) + } + let outputDir = URL(fileURLWithPath: output) + let existing = (try? FileManager.default.contentsOfDirectory(atPath: outputDir.path))?.filter { $0.hasSuffix(".json") } ?? [] + guard existing.isEmpty else { + print("Refusing to overwrite \(existing.count) existing tables in \(outputDir.path); export into a new tables version") + exit(1) + } + let keyboardTypes = options.value("--keyboard-types")? + .split(separator: ",") + .compactMap { UInt32($0.trimmingCharacters(in: .whitespaces)) } ?? [91, 37] + + do { + let written = try TableExporter.exportAllStandardTables(outputDirectory: outputDir, keyboardTypes: keyboardTypes) + print("Successfully exported \(written.count) layout tables to \(outputDir.path)") + } catch { + print("Error exporting layout tables: \(error)") + exit(1) + } +} + +func handleGenerateSeedCorpus(_ options: Options) { + guard let version = options.value("--version"), let definitions = SeedCorpusDefinitions.version(version) else { + print("generate-seed-corpus needs --version v1|v2") + exit(1) + } + let corpusDir = options.url("--corpus", default: "Benchmarks/layout-corpus/\(version)") + let tablesDir = options.url("--tables", default: "Benchmarks/layout-tables/v1") + let commit = options.value("--commit") ?? BenchmarkEnvironment.sourceState(commitOverride: nil).commit + + do { + try ManifestGenerator.generateSeedCorpusAndManifest(corpusDir: corpusDir, tablesDir: tablesDir, sourceCommit: commit, definitions: definitions) + print("Successfully generated seed corpus \(version) and manifest at \(corpusDir.path)") + } catch { + print("Error generating seed corpus: \(error)") + exit(1) + } +} + +func handleValidate(_ options: Options) { + let corpusDir = options.url("--corpus", default: "Benchmarks/layout-corpus/v1") + let tablesDir = options.url("--tables", default: "Benchmarks/layout-tables/v1") + let strict = options.flag("--strict") + + do { + let result = try CorpusValidator.validate(corpusDirectory: corpusDir, tablesDirectory: tablesDir) + for warning in result.warnings { + print(" warning: \(warning)") + } + if !result.passed || (strict && !result.warnings.isEmpty) { + print("Validation FAILED: \(result.errors.count) errors, \(result.warnings.count) warnings\(strict ? " (strict)" : ""):") + for err in result.errors { + print(" - \(err)") + } + exit(1) + } + print("Validation PASSED: \(result.trialsCount) trials validated across \(result.splitsCount) splits (\(result.warnings.count) warnings).") + exit(0) + } catch { + print("Validation error: \(error)") + exit(1) + } +} + +func execute(trials: [Trial], tables: [String: LayoutTable], layer: BenchmarkLayer) -> [TrialResult] { + let adapter = SwitchFixAdapter(tables: tables) + guard adapter.prewarm() else { + print("Failed to prewarm SwitchFix adapter dictionaries") + exit(1) + } + return trials.map { adapter.executeTrial(trial: $0, layer: layer) } +} + +func handleRun(_ options: Options) { + let adapterName = options.value("--adapter") ?? "switchfix" + let split = options.value("--split") ?? "development" + let profile = options.value("--profile") ?? "boundary-layout-only" + let corpusDir = options.url("--corpus", default: "Benchmarks/layout-corpus/v1") + let tablesDir = options.url("--tables", default: "Benchmarks/layout-tables/v1") + + guard adapterName == "switchfix" else { + print("competitor_unavailable: adapter '\(adapterName)' is not available") + exit(1) + } + guard profile == "boundary-layout-only" else { + print("Unsupported profile: \(profile)") + exit(1) + } + guard let layer = BenchmarkLayer(rawValue: options.value("--layer") ?? "replay"), layer != .app else { + print("Unsupported layer: the app layer is measured by manual conformance runs") + exit(1) + } + + do { + try CorpusValidator.requireVerifiedInputs(corpusDirectory: corpusDir, tablesDirectory: tablesDir) + let tables = try loadTables(from: tablesDir) + let trials = try loadTrials(corpusDir: corpusDir, split: split) + guard !trials.isEmpty else { + print("No trials found for split '\(split)' in \(corpusDir.path)") + exit(1) + } + + let results = execute(trials: trials, tables: tables, layer: layer) + let report = Scorer.score(trials: trials, results: results) + let metadata = BenchmarkEnvironment.metadata(corpusDir: corpusDir, trials: trials, profile: profile, layer: layer, commitOverride: options.value("--commit")) + let md = Scorer.renderMarkdown(report: report, split: split, adapter: adapterName) + print(md) + + if let output = options.value("--output") { + let outDir = URL(fileURLWithPath: output) + try FileManager.default.createDirectory(at: outDir, withIntermediateDirectories: true) + let reportURL = outDir.appendingPathComponent("report.md") + try md.write(to: reportURL, atomically: true, encoding: .utf8) + + struct OutputEnvelope: Codable { + let created: String + let adapter: String + let split: String + let metadata: RunMetadata + let report: BenchmarkScoreReport + let results: [TrialResult] + } + let envelope = OutputEnvelope( + created: ISO8601DateFormatter().string(from: Date()), + adapter: adapterName, + split: split, + metadata: metadata, + report: report, + results: results + ) + let jsonURL = outDir.appendingPathComponent("results.json") + try prettyEncoder().encode(envelope).write(to: jsonURL) + print("\nSaved report to \(reportURL.path) and results to \(jsonURL.path)") + } + + print("\ncomparisonStatus: \(metadata.comparisonStatus)") + + if let baselinePath = options.value("--baseline") { + let baseline = try JSONDecoder().decode(BaselineRecord.self, from: Data(contentsOf: URL(fileURLWithPath: baselinePath))) + guard baseline.metadata.corpusManifestSha256 == metadata.corpusManifestSha256 else { + print("Gate FAILED: baseline was recorded on a different corpus (manifest \(baseline.metadata.corpusManifestSha256.prefix(12)) vs \(metadata.corpusManifestSha256.prefix(12)))") + exit(2) + } + guard baseline.metadata.layer == metadata.layer else { + print("Gate FAILED: baseline was recorded on layer '\(baseline.metadata.layer)' but current run is on layer '\(metadata.layer)'") + exit(2) + } + let findings = BaselineGate.regressions(current: report, baseline: baseline, split: split) + if !report.complete || !findings.isEmpty { + print("Gate FAILED against \(baselinePath):") + if !report.complete { + print(" - run is incomplete: \(report.errorTrials) errors, \(report.missingTrials) missing") + } + for finding in findings { + print(" - \(finding.trialID): \(finding.baseline.rawValue) -> \(finding.current?.rawValue ?? "missing_trial")") + } + exit(2) + } + print("Gate PASSED against \(baselinePath): no trial lost its pass or coverage") + } + } catch { + print("Execution error: \(error)") + exit(1) + } +} + +func handleSummarize(_ options: Options) { + let resultsDir = options.url("--results", default: "Benchmarks/results/current") + let reportURL = resultsDir.appendingPathComponent("report.md") + if let content = try? String(contentsOf: reportURL, encoding: .utf8) { + print(content) + } else { + print("No report.md found at \(resultsDir.path)") + exit(1) + } +} + +func handleRecordBaseline(_ options: Options) { + guard let baselines = options.value("--baselines") else { + print("record-baseline needs --baselines ; recorded baselines are never overwritten in place") + exit(1) + } + let corpusDir = options.url("--corpus", default: "Benchmarks/layout-corpus/v1") + let tablesDir = options.url("--tables", default: "Benchmarks/layout-tables/v1") + let baselinesDir = URL(fileURLWithPath: baselines) + let baselineJSONURL = baselinesDir.appendingPathComponent("baseline.json") + let baselineMDURL = baselinesDir.appendingPathComponent("baseline.md") + guard !FileManager.default.fileExists(atPath: baselineJSONURL.path) else { + print("Refusing to overwrite \(baselineJSONURL.path); record a new baseline version instead") + exit(1) + } + + var runnerChecks: [String: RunnerCheck] = [:] + for entry in options.all("--runner-log") { + let parts = entry.split(separator: "=", maxSplits: 1).map(String.init) + guard parts.count == 2, let check = BenchmarkEnvironment.runnerCheck(logPath: parts[1]) else { + print("Could not read runner results from '\(entry)'") + exit(1) + } + runnerChecks[parts[0]] = check + } + + do { + try CorpusValidator.requireVerifiedInputs(corpusDirectory: corpusDir, tablesDirectory: tablesDir) + let tables = try loadTables(from: tablesDir) + let devTrials = try loadTrials(corpusDir: corpusDir, split: "development") + let holdoutTrials = try loadTrials(corpusDir: corpusDir, split: "holdout") + let allTrials = devTrials + holdoutTrials + let devResults = execute(trials: devTrials, tables: tables, layer: .replay) + let holdoutResults = execute(trials: holdoutTrials, tables: tables, layer: .replay) + let allResults = devResults + holdoutResults + + let metadata = BenchmarkEnvironment.metadata( + corpusDir: corpusDir, + trials: allTrials, + profile: "boundary-layout-only", + layer: .replay, + commitOverride: options.value("--commit") + ) + let record = BaselineRecord( + schemaVersion: 2, + baselineVersion: baselinesDir.pathComponents.suffix(2).joined(separator: "/"), + created: ISO8601DateFormatter().string(from: Date()), + metadata: metadata, + runnerChecks: runnerChecks, + development: Scorer.score(trials: devTrials, results: devResults), + holdout: Scorer.score(trials: holdoutTrials, results: holdoutResults), + combined: Scorer.score(trials: allTrials, results: allResults) + ) + + let rawOutputDir = URL(fileURLWithPath: "Benchmarks/results/baselines").appendingPathComponent(record.baselineVersion) + try FileManager.default.createDirectory(at: rawOutputDir, withIntermediateDirectories: true) + try prettyEncoder().encode(allResults).write(to: rawOutputDir.appendingPathComponent("all-results.json")) + + try FileManager.default.createDirectory(at: baselinesDir, withIntermediateDirectories: true) + try prettyEncoder().encode(record).write(to: baselineJSONURL) + let md = renderBaselineMarkdown(record: record, trials: allTrials, results: allResults) + try md.write(to: baselineMDURL, atomically: true, encoding: .utf8) + + print(md) + print("\nRecorded \(baselineJSONURL.path), \(baselineMDURL.path); raw results in \(rawOutputDir.path)") + } catch { + print("Error recording baseline: \(error)") + exit(1) + } +} + +func renderBaselineMarkdown(record: BaselineRecord, trials: [Trial], results: [TrialResult]) -> String { + let m = record.metadata + var lines: [String] = [] + lines.append("# SwitchFix Baseline \(record.baselineVersion)") + lines.append("") + lines.append("Generated by `LayoutBenchmark record-baseline`; every value below is measured or read from the corpus.") + lines.append("") + lines.append("- **Recorded**: `\(record.created)`") + lines.append("- **Source**: `\(m.sourceCommit)` (\(m.sourceTreeState))") + lines.append("- **Environment**: macOS \(m.osVersion) (\(m.osBuild)), \(m.architecture), keyboard types \(m.keyboardTypes.map(String.init).joined(separator: ", ")), \(m.swiftVersion)") + lines.append("- **Profile**: `\(m.profile)` (layer `\(m.layer)`), dictionaries `\(m.dictionaryMode)`, \(m.personalExceptions) personal exceptions, comparison `\(m.comparisonStatus)`") + lines.append("- **Corpus**: `\(m.corpusVersion)` (manifest SHA-256 `\(m.corpusManifestSha256)`), tables `\(m.tablesVersion)`") + lines.append("- **Detector thresholds**: " + m.detectorThresholds.keys.sorted().map { "\($0)=\(m.detectorThresholds[$0] ?? 0)" }.joined(separator: ", ")) + lines.append("") + lines.append("## Runner Results") + lines.append("") + if record.runnerChecks.isEmpty { + lines.append("No runner logs were supplied.") + } else { + lines.append("| Runner | Passed | Failed | Failures | Log |") + lines.append("| --- | --- | --- | --- | --- |") + for name in record.runnerChecks.keys.sorted() { + guard let check = record.runnerChecks[name] else { continue } + let failures = check.failures.isEmpty ? "none" : check.failures.joined(separator: "; ") + lines.append("| `\(name)` | \(check.passed) | \(check.failed) | \(failures) | `\(check.log)` |") + } + } + lines.append("") + lines.append("## Aggregate Results") + lines.append("") + lines.append("| Split | Trials | Groups | Complete | Pos / Neg | TP | FN | Text FP | Layout FP | Recall (95% CI) | Text-Damage FP (95% CI) | Layout-Only FP (95% CI) | Precision (95% CI) |") + lines.append("| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |") + func row(_ name: String, _ r: BenchmarkScoreReport) -> String { + func cell(_ metric: ScoredMetric, _ digits: Int) -> String { + "\(Scorer.percent(metric.rate, digits: digits)) \(metric.interval?.formatted ?? "")" + } + return "| **\(name)** | \(r.totalTrials) | \(r.groupCount) | \(r.complete ? "yes" : "NO") | \(r.nPositive) / \(r.nNegative) | \(r.truePositives) | \(r.falseNegatives) | \(r.textDamageFPs) | \(r.layoutOnlyFPs) | \(cell(r.recall, 1)) | \(cell(r.textDamageFPRate, 2)) | \(cell(r.layoutOnlyFPRate, 2)) | \(cell(r.precision, 1)) |" + } + lines.append(row("Development", record.development)) + lines.append(row("Holdout", record.holdout)) + lines.append(row("Combined", record.combined)) + lines.append("") + lines.append("## Direction Breakdown (Combined)") + lines.append("") + lines.append("| Direction | Trials | TP | FN | TN | Text FP | Layout FP | Errors | Unsupported | Recall | Text-Damage FP |") + lines.append("| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |") + for dir in record.combined.directionBreakdown.keys.sorted() { + guard let d = record.combined.directionBreakdown[dir] else { continue } + lines.append("| \(dir) | \(d.total) | \(d.tp) | \(d.fn) | \(d.tn) | \(d.textFP) | \(d.layoutFP) | \(d.errors) | \(d.unsupported) | \(Scorer.percent(d.recall)) | \(Scorer.percent(d.textDamageFPRate, digits: 2)) |") + } + lines.append("") + lines.append("## Category Breakdown (Combined)") + lines.append("") + lines.append("| Category | Trials | TP | FN | TN | Text FP | Layout FP |") + lines.append("| --- | --- | --- | --- | --- | --- | --- |") + for category in record.combined.categoryBreakdown.keys.sorted() { + guard let c = record.combined.categoryBreakdown[category] else { continue } + lines.append("| \(category) | \(c.total) | \(c.tp) | \(c.fn) | \(c.tn) | \(c.textFP) | \(c.layoutFP) |") + } + lines.append("") + lines.append("## Trials That Did Not Pass") + lines.append("") + let resultsByID = Dictionary(results.map { ($0.trialID, $0) }, uniquingKeysWith: { first, _ in first }) + let failing = trials.filter { !(record.combined.trialOutcomes[$0.id]?.isPassing ?? false) } + if failing.isEmpty { + lines.append("None.") + } else { + lines.append("| Trial | Label | Category | Outcome | Expected | Observed | Reason codes |") + lines.append("| --- | --- | --- | --- | --- | --- | --- |") + for trial in failing { + let result = resultsByID[trial.id] + let outcome = record.combined.trialOutcomes[trial.id]?.rawValue ?? "missing" + let observed = result.map { "`\($0.finalText)` @ \($0.finalSourceID)" } ?? "missing" + let reasons = result?.reasonCodes.joined(separator: ", ") ?? "" + lines.append("| \(trial.id) | \(trial.label) | \(trial.category) | \(outcome) | `\(trial.expected.text)` @ \(trial.expected.sourceID) | \(observed) | \(reasons) |") + } + } + if !record.combined.reasonCodeCounts.isEmpty { + lines.append("") + lines.append("## Reason Codes (Combined)") + lines.append("") + for code in record.combined.reasonCodeCounts.keys.sorted() { + lines.append("- `\(code)`: \(record.combined.reasonCodeCounts[code] ?? 0) trials") + } + } + lines.append("") + return lines.joined(separator: "\n") +} + +main() diff --git a/Sources/SwitchFixApp/AppDelegate.swift b/Sources/SwitchFixApp/AppDelegate.swift index 687f9ce..b9ada59 100644 --- a/Sources/SwitchFixApp/AppDelegate.swift +++ b/Sources/SwitchFixApp/AppDelegate.swift @@ -8,6 +8,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate { private var statusBarController: StatusBarController? private var keyboardMonitor: KeyboardMonitor? private var inputEngine: InputEngine? + private var personalExceptionsPublisher: PersonalExceptionsPublisher? private var captureState: CaptureStateStore? private var focusCoordinator: AccessibilityFocusCoordinator? private let inputSourceManager = InputSourceManager.shared @@ -24,6 +25,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate { func applicationDidFinishLaunching(_ notification: Notification) { SwitchFixLog.app.notice("launched, pid=\(ProcessInfo.processInfo.processIdentifier)") + PreferencesManager.shared.removeLegacySettings() statusBarController = StatusBarController() inputSourceManager.refreshCurrentInputSource() previousLayout = inputSourceManager.currentLayout() @@ -75,6 +77,11 @@ final class AppDelegate: NSObject, NSApplicationDelegate { self?.focusCoordinator?.focusMayChange(pid: pid, epoch: epoch) } } + let publisher = PersonalExceptionsPublisher { [weak engine] policy in + engine?.updatePersonalExceptions(policy) + } + publisher.publishIfChanged(PreferencesManager.shared.personalExceptionsPolicy) + personalExceptionsPublisher = publisher inputEngine = engine inputSourceManager.setSelectionCallbacks( willSelect: { [weak self] layout, sourceID in @@ -372,6 +379,8 @@ final class AppDelegate: NSObject, NSApplicationDelegate { ?? inputSourceManager.preferredUkrainianVariant() let toVariant = inputSourceManager.ukrainianVariant(forInputSourceID: newSourceID) ?? inputSourceManager.preferredUkrainianVariant() + // Install the new correction epoch before scheduling layout-switch corrections. + updateDetectionConfiguration(allowedLayouts: readyLayouts) if expectedGeneratedSelection { monitorFailure = nil inputEngine?.handleGeneratedLayoutContext(context) @@ -385,7 +394,6 @@ final class AppDelegate: NSObject, NSApplicationDelegate { toVariant: toVariant ) } - updateDetectionConfiguration(allowedLayouts: readyLayouts) refreshRuntimeReadiness() } @@ -444,6 +452,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate { ) captureState?.updateHotkeys(currentHotkeyConfiguration()) inputEngine?.updatePreferences(currentPreferencesSnapshot()) + personalExceptionsPublisher?.publishIfChanged(PreferencesManager.shared.personalExceptionsPolicy) updateDetectionConfiguration(allowedLayouts: readyLayouts) refreshRuntimeReadiness() } @@ -529,9 +538,12 @@ final class AppDelegate: NSObject, NSApplicationDelegate { let currentVariant = currentLayout == .ukrainian ? inputSourceManager.currentUkrainianVariant() ?? preferredVariant : preferredVariant + if currentLayout.isCyrillic { + PreferencesManager.shared.lastCyrillicLayout = currentLayout + } inputEngine?.updateDetectionConfiguration( allowedLayouts: effectiveAllowed, - allowCyrillicToCyrillic: PreferencesManager.shared.allowCyrillicToCyrillic, + preferredCyrillicLayout: PreferencesManager.shared.lastCyrillicLayout, activeSourceSupportedLayouts: inputSourceManager.activeSourceSupportedLayouts(), ukrainianFromVariant: currentVariant, ukrainianToVariant: preferredVariant diff --git a/Sources/TestRunner/main.swift b/Sources/TestRunner/main.swift index c7f1eb5..03959e2 100644 --- a/Sources/TestRunner/main.swift +++ b/Sources/TestRunner/main.swift @@ -17,6 +17,10 @@ func assert(_ condition: Bool, _ message: String, file: String = #file, line: In } } +func check(_ condition: Bool, _ message: String, file: String = #file, line: Int = #line) { + assert(condition, message, file: file, line: line) +} + func assertEqual(_ a: T, _ b: T, _ message: String = "", file: String = #file, line: Int = #line) { if a == b { passed += 1 @@ -28,7 +32,9 @@ func assertEqual(_ a: T, _ b: T, _ message: String = "", file: Str func runSuite(_ name: String, _ block: () -> Void) { print("--- \(name) ---") + fflush(stdout) block() + fflush(stdout) } func dictionaryPath(for language: Language) -> String { @@ -36,14 +42,14 @@ func dictionaryPath(for language: Language) -> String { return "\(root)/Sources/Dictionary/Resources/\(language.rawValue).txt" } -func forEachDictionaryWord(language: Language, _ block: (String) -> Void) { +func forEachDictionaryWord(language: Language, _ block: @escaping (String) -> Void) { let path = dictionaryPath(for: language) guard let content = try? String(contentsOfFile: path, encoding: .utf8) else { print(" WARN: could not read dictionary at \(path)") return } - for line in content.split(whereSeparator: \.isNewline) { + content.enumerateLines { line, _ in let word = line.trimmingCharacters(in: .whitespacesAndNewlines).lowercased() if !word.isEmpty { block(word) @@ -1356,58 +1362,359 @@ runSuite("InputSourceDiscoveryEngine: Apple native Ukrainian layout ignores Opti assertEqual(desc?.isCustom, false, "Apple native layout is not custom") } +runSuite("InputSourceDiscoveryEngine: Apple native layout is classified by its primary language only") { + // What macOS reports for "Ukrainian – Legacy": every language its script can write. + let appleUkProvider = MockInputSourcePropertyReader( + id: "com.apple.keylayout.Ukrainian", + localizedName: "Ukrainian – Legacy", + languages: ["uk", "be", "bg", "bs_Cyrl", "mk", "myv", "ru", "sr", "sr_Cyrl"], + unshiftedKeys: [0: "ф", 1: "и", 11: "і", 12: "й", 30: "ї", 39: "є"] + ) + let appleDesc = InputSourceDiscoveryEngine.classify(provider: appleUkProvider) + assertEqual(appleDesc?.supportedLayouts, Set([.ukrainian]), "'ru' further down Apple's list must not make the Ukrainian layout a Russian one") + assertEqual(appleDesc?.ukrainianVariant, .legacy, "legacy variant is still detected") + + let customProvider = MockInputSourcePropertyReader( + id: "org.example.keylayout.cyrillic-duo", + localizedName: "Cyrillic Duo", + languages: ["uk", "ru"] + ) + let customDesc = InputSourceDiscoveryEngine.classify(provider: customProvider) + assertEqual(customDesc?.supportedLayouts, Set([.ukrainian, .russian]), "third-party layouts keep every language they declare") +} + +runSuite("WordValidator: 'VNC' and 'vnc' are recognized as valid English words via allow override") { + assert(WordValidator.shared.validate("vnc", language: .english).isValid, "'vnc' should be valid English") + assert(WordValidator.shared.validate("VNC", language: .english).isValid, "'VNC' should be valid English") +} + runSuite("WordValidator: 'блін' is recognized as a valid Ukrainian word via allow override") { let result = WordValidator.shared.validate("блін", language: .ukrainian) assert(result.isValid, "'блін' should be a valid word in Ukrainian") } -runSuite("LayoutDetector: Cyrillic-to-Cyrillic cross-conversion is disabled by default") { +func flushWord(_ detector: LayoutDetector, _ word: String) -> DetectionResult? { + detector.discardBuffer() + for char in word { + detector.addCharacter(String(char)) + } + return detector.flushBuffer(boundaryCharacter: " ") +} + +runSuite("LayoutDetector: Cyrillic source is never converted to another Cyrillic layout") { let detector = LayoutDetector() - let mockDelegate = MockDetectorDelegate() - detector.delegate = mockDelegate detector.currentLayout = .ukrainian detector.ukrainianFromVariant = .legacy detector.allowedLayouts = [.english, .ukrainian, .russian] - assertEqual(detector.allowCyrillicToCyrillic, false, "allowCyrillicToCyrillic must default to false") + assert(flushWord(detector, "блін") == nil, "'блін' must not be converted to Russian 'блин'") + assert(flushWord(detector, "єто") == nil, "'єто' must not be converted to Russian 'это'") + detector.currentLayout = .russian + assert(flushWord(detector, "вы") == nil || flushWord(detector, "вы")?.targetLayout == .english, + "Russian text must never become Ukrainian") +} - for char in "блін" { - detector.addCharacter(String(char)) - } - let result = detector.flushBuffer(boundaryCharacter: " ") - assertEqual(mockDelegate.results.count, 0, "no Russian correction should be emitted for 'блін'") - assert(result == nil, "'блін' must not be converted to Russian 'блин'") +runSuite("LayoutMapper: Ukrainian <-> Russian conversion is the identity") { + assertEqual(LayoutMapper.convert("єто", from: .ukrainian, to: .russian), "єто", "UK→RU must not convert") + assertEqual(LayoutMapper.convert("вы", from: .russian, to: .ukrainian), "вы", "RU→UK must not convert") + let targets = LayoutMapper.convertToAlternatives("вы", from: .russian).map { $0.0 } + assertEqual(targets, [.english], "a Cyrillic source may only convert to English") } -runSuite("LayoutDetector: Cyrillic-to-Cyrillic conversion respects allowCyrillicToCyrillic setting") { +runSuite("LayoutDetector: Latin typed under Ukrainian layout is never replaced by Russian") { let detector = LayoutDetector() - let mockDelegate = MockDetectorDelegate() - detector.delegate = mockDelegate detector.currentLayout = .ukrainian - detector.ukrainianFromVariant = .legacy + detector.currentInputSourceID = "com.apple.keylayout.Ukrainian" + detector.activeSourceSupportedLayouts = [.ukrainian] detector.allowedLayouts = [.english, .ukrainian, .russian] - detector.allowCyrillicToCyrillic = false + detector.preferredSourceIDProvider = { layout in layout == .ukrainian ? "com.apple.keylayout.Ukrainian" : nil } + detector.preferredCyrillicLayout = .ukrainian + assert(flushWord(detector, "ds") == nil, "'ds' under Ukrainian must not become Russian 'вы'") + detector.preferredCyrillicLayout = nil + assert(flushWord(detector, "ds") == nil, "'ds' under Ukrainian must not become Russian even without history") +} - // In legacy Ukrainian mapping: "єто" converts to Russian "это" - for char in "єто" { - detector.addCharacter(String(char)) +runSuite("LayoutDetector: Latin text converts only to the last-used Cyrillic layout") { + let detector = LayoutDetector() + detector.currentLayout = .english + detector.currentInputSourceID = "com.apple.keylayout.ABC" + detector.activeSourceSupportedLayouts = [.english] + detector.allowedLayouts = [.english, .ukrainian, .russian] + detector.preferredSourceIDProvider = { _ in nil } + + detector.preferredCyrillicLayout = .ukrainian + let ua = flushWord(detector, "ghbdsn") + assertEqual(ua?.targetLayout, .ukrainian, "Latin 'ghbdsn' should convert to Ukrainian when UA was last used") + assertEqual(ua?.convertedWord, "привіт", "converted word should be Ukrainian 'привіт'") + + detector.preferredCyrillicLayout = .russian + let ru = flushWord(detector, "ghbdtn") + assertEqual(ru?.targetLayout, .russian, "Latin 'ghbdtn' should convert to Russian when RU was last used") + assertEqual(ru?.convertedWord, "привет", "converted word should be Russian 'привет'") + + detector.preferredCyrillicLayout = .ukrainian + assert(flushWord(detector, "ds")?.targetLayout != .russian, "'ds' must not become Russian 'вы' when UA was last used") +} + +// ============================================================================= +// Run-level context: words typed back-to-back share one layout +// ============================================================================= + +runSuite("LayoutDetector: Ambiguous word outlives a short neighbour until the run resolves") { + let detector = LayoutDetector() + detector.currentLayout = .ukrainian + + assert(flushWord(detector, "філ") == nil, "valid Ukrainian 'філ' must not be rewritten in isolation") + let me = flushWord(detector, "ьу") + assertEqual(me?.originalWord, "ьу", "a lone two-letter word is too weak to drag the ambiguous word along") + assertEqual(me?.convertedWord, "me") + + let more = flushWord(detector, "ьщку") + assertEqual(more?.originalWord, "філ me ьщку", "the confirming word must reach back over everything on screen since the run began") + assertEqual(more?.convertedWord, "ask me more") + assertEqual(more?.targetLayout, .english) + assert(more?.shouldSwitchLayout == true, "a resolved run switches the layout") +} + +runSuite("LayoutDetector: Ambiguous words of any length are reachable") { + let detector = LayoutDetector() + detector.currentLayout = .ukrainian + + assert(flushWord(detector, "туче") == nil, "valid Ukrainian 'туче' must not be rewritten in isolation") + let more = flushWord(detector, "ьщку") + assertEqual(more?.originalWord, "туче ьщку") + assertEqual(more?.convertedWord, "next more") +} + +runSuite("LayoutDetector: Words already corrected are not retyped") { + let detector = LayoutDetector() + detector.currentLayout = .ukrainian + detector.ukrainianFromVariant = .legacy + + assertEqual(flushWord(detector, "фил")?.convertedWord, "ask", "'фил' is not Ukrainian, so it converts on its own") + let me = flushWord(detector, "ьу") + assertEqual(me?.originalWord, "ьу", "the second word must not delete and retype the first") + assert(me?.shouldSwitchLayout == true, "two agreeing short words switch the layout") + + let mixed = LayoutDetector() + mixed.currentLayout = .ukrainian + mixed.ukrainianFromVariant = .legacy + assertEqual(flushWord(mixed, "фил")?.convertedWord, "ask") + assert(flushWord(mixed, "еру") == nil, "'еру' stands as typed") + let more = flushWord(mixed, "ьщку") + assertEqual(more?.originalWord, "еру ьщку", "the sweep starts at the first word that changes, not at one already corrected") + assertEqual(more?.convertedWord, "the more") +} + +runSuite("LayoutDetector: Ambiguous word sides with the Ukrainian text before it") { + let detector = LayoutDetector() + detector.currentLayout = .ukrainian + detector.ukrainianFromVariant = .legacy + + assert(flushWord(detector, "лист") == nil, "'лист' is plain Ukrainian") + assert(flushWord(detector, "від") == nil, "'від' also reads as English 'dbl', but stands as typed") + let google = flushWord(detector, "пщщпду") + assertEqual(google?.originalWord, "пщщпду", "'від' follows a word that only makes sense in Ukrainian and must stay") + assertEqual(google?.convertedWord, "google") +} + +runSuite("LayoutDetector: Ambiguous word sides with the English text before it") { + let detector = LayoutDetector() + detector.currentLayout = .english + + assert(flushWord(detector, "check") == nil, "'check' is plain English") + assert(flushWord(detector, "the") == nil, "'the' also reads as Ukrainian 'еру', but stands as typed") + let hello = flushWord(detector, "ghbdsn") + assertEqual(hello?.originalWord, "ghbdsn", "'the' follows a word that only makes sense in English and must stay") + assertEqual(hello?.convertedWord, "привіт") +} + +runSuite("LayoutDetector: Run sweep stops at Enter and at words it cannot place") { + do { + let detector = LayoutDetector() + detector.currentLayout = .ukrainian + detector.addCharacter("еру") + assert(detector.flushBuffer(boundaryCharacter: "\n") == nil, "'еру' stands as typed") + let middle = flushWord(detector, "ьшввду") + assertEqual(middle?.originalWord, "ьшввду", "text before Enter may already be submitted and must not be rewritten") + assertEqual(middle?.convertedWord, "middle") } - let suppressedResult = detector.flushBuffer(boundaryCharacter: " ") - assertEqual(mockDelegate.results.count, 0, "Cyrillic cross-conversion must be suppressed when disabled") - assert(suppressedResult == nil, "result should be nil when allowCyrillicToCyrillic is false") + do { + let detector = LayoutDetector() + detector.currentLayout = .ukrainian + assert(flushWord(detector, "еру") == nil, "'еру' stands as typed") + assert(flushWord(detector, "щщщщ") == nil, "'щщщщ' is a word in neither layout") + let middle = flushWord(detector, "ьшввду") + assertEqual(middle?.originalWord, "ьшввду", "the sweep must not jump over a word it cannot convert") + } +} - // Now enable Cyrillic-to-Cyrillic - detector.allowCyrillicToCyrillic = true - for char in "єто" { - detector.addCharacter(String(char)) +// The same rules through the real InputEngine, where the delete count decides what the app loses. + +final class CorrectionPlanLog: @unchecked Sendable { + private let lock = NSLock() + private var plans: [CorrectionPlan] = [] + func add(_ plan: CorrectionPlan) { lock.lock(); plans.append(plan); lock.unlock() } + func all() -> [CorrectionPlan] { lock.lock(); defer { lock.unlock() }; return plans } +} + +enum TypedKey { + case text(String) + case space + case backspace +} + +final class DetectionObserver: LayoutDetectorDelegate { + let onDetection: () -> Void + init(_ onDetection: @escaping () -> Void) { self.onDetection = onDetection } + func layoutDetector(_ detector: LayoutDetector, didDetectWrongLayout result: DetectionResult, boundaryCharacter: String?) { + onDetection() } - let allowedResult = detector.flushBuffer(boundaryCharacter: " ") - assert(allowedResult != nil, "conversion should be allowed when allowCyrillicToCyrillic is true") - if let allowedResult { - assertEqual(allowedResult.targetLayout, .russian, "target layout should be Russian") - assertEqual(allowedResult.convertedWord, "это", "converted word should be Russian 'это'") +} + +func typeOnUkrainianLayout(_ keys: [TypedKey], variant: UkrainianKeyboardVariant, failEmissionAt: Int = 0, rejectFirstDetection: Bool = false) -> [CorrectionPlan] { + let context = InputContextSnapshot( + epoch: 1, + frontmostPID: 100, + appAllowed: true, + layout: .ukrainian, + inputSourceID: "com.apple.keylayout.Ukrainian", + secureFocus: .notSecure + ) + let store = CaptureStateStore(context: context, hotkeys: HotkeyConfiguration(hotkeyModifiers: 0)) + let log = CorrectionPlanLog() + let detector = LayoutDetector() + var didReject = false + detector.preferredSourceIDProvider = { _ in nil } + let observer = DetectionObserver { + if rejectFirstDetection && !didReject { + didReject = true + // A newer physical event makes the pending request ineligible without + // changing the run retained by the input state machine. + _ = store.capture(timestamp: 0, kind: .character(""), keyCode: 0, + flagsRawValue: 0, isAutorepeat: false, sourcePID: 1, sourceUserData: 0) + } + } + detector.delegate = observer + let engine = InputEngine( + captureState: store, + initialContext: context, + preferences: InputPreferencesSnapshot(isEnabled: true, correctionMode: .automatic), + detector: detector, + correctionEmission: { plan in + log.add(plan) + return log.all().count != failEmissionAt + } + ) + engine.updateDetectionConfiguration( + allowedLayouts: [.english, .ukrainian], + preferredCyrillicLayout: .ukrainian, + activeSourceSupportedLayouts: [.ukrainian], + ukrainianFromVariant: variant, + ukrainianToVariant: variant + ) + func send(_ kind: CapturedInput.Kind) { + engine.enqueue(store.capture( + timestamp: 0, + kind: kind, + keyCode: 0, + flagsRawValue: 0, + isAutorepeat: false, + sourcePID: 1, + sourceUserData: 0 + )) + } + // Detection and emission hop queues, and a correction is dropped if the next key beats it. + func settle() { + let drained = DispatchSemaphore(value: 0) + engine.drainPipeline { drained.signal() } + drained.wait() + } + for key in keys { + switch key { + case .text(let text): + for character in text { send(.character(String(character))) } + case .space: + send(.boundary(" ")) + settle() + case .backspace: + send(.delete) + } + } + withExtendedLifetime(observer) {} + return log.all() +} + +runSuite("InputEngine: First word typed after backspacing is corrected once the run resolves") { + let plans = typeOnUkrainianLayout( + [.backspace, .backspace, .text("фил"), .space, .text("ьу"), .space, .text("йгуиешщти"), .space], + variant: .legacy + ) + assertEqual(plans.count, 2, "'ask' may be a fragment and must wait; 'me' and 'questions' each trigger a correction") + assertEqual(plans.first?.originalText, "ьу") + assertEqual(plans.last?.originalText, "фил me йгуиешщти") + assertEqual(plans.last?.correctedText, "ask me questions") + assertEqual(plans.last?.deleteCount, 17, "three words, two spaces between them and the trailing space") + assertEqual(plans.last?.targetLayout, .english) +} + +runSuite("InputEngine: Run sweep deletes exactly what is on screen") { + let plans = typeOnUkrainianLayout( + [.text("філ"), .space, .text("ьу"), .space, .text("ьщку"), .space], + variant: .standard + ) + assertEqual(plans.last?.correctedText, "ask me more") + assertEqual(plans.last?.replacementText, "ask me more ") + assertEqual(plans.last?.deleteCount, 12) +} + +runSuite("InputEngine: Failed or rejected corrections leave observed run text intact") { + let keys: [TypedKey] = [.text("філ"), .space, .text("ьу"), .space, .text("ьщку"), .space] + for reject in [false, true] { + let plans = typeOnUkrainianLayout(keys, variant: .standard, + failEmissionAt: reject ? 0 : 1, rejectFirstDetection: reject) + assertEqual(plans.count, reject ? 1 : 2, "exercise the intended cancellation/emission path") + assertEqual(plans.last?.originalText, "філ ьу ьщку", "undo must retain the actual, uncorrected screen text") + assertEqual(plans.last?.correctedText, "ask me more") + assertEqual(plans.last?.replacementText, "ask me more ") + assertEqual(plans.last?.deleteCount, 12) } } +runSuite("InputEngine: Failed sweep preserves earlier confirmed conversions only") { + let plans = typeOnUkrainianLayout( + [.text("філ"), .space, .text("ьу"), .space, .text("ьщку"), .space, .text("йгуиешщти"), .space], + variant: .standard, failEmissionAt: 2 + ) + assertEqual(plans.count, 3) + assertEqual(plans.last?.originalText, "філ me ьщку йгуиешщти", "failed sweep must not mark earlier words converted; confirmed 'me' stays converted") + assertEqual(plans.last?.correctedText, "ask me more questions") + assertEqual(plans.last?.replacementText, "ask me more questions ") + assertEqual(plans.last?.deleteCount, 22) +} + +runSuite("InputEngine: Backspacing into a deferred word never deletes the text before it") { + // Screen after the last space: "еруь " — five characters. + let plans = typeOnUkrainianLayout( + [.text("еру"), .space, .backspace, .text("ь"), .space], + variant: .standard + ) + assertEqual(plans.count, 1) + assertEqual(plans.first?.originalText, "еруь", "the reopened word must not also be counted as an earlier word") + assertEqual(plans.first?.correctedText, "them") + assertEqual(plans.first?.deleteCount, 5) +} + +runSuite("InputEngine: A second space ends the run") { + // Screen after the last space: "еру ьшввду " — the journal holds no token for the extra space. + let plans = typeOnUkrainianLayout( + [.text("еру"), .space, .space, .text("ьшввду"), .space], + variant: .standard + ) + assertEqual(plans.count, 1) + assertEqual(plans.first?.originalText, "ьшввду", "a span that skips a character would delete one too few") + assertEqual(plans.first?.deleteCount, 7) +} + // ============================================================================= // Synthetic Coverage Tests (EN ↔︎ UK) // ============================================================================= @@ -1474,6 +1781,443 @@ runSuite("Synthetic: EN → UK coverage") { assert(total > 0, "english dictionary should not be empty") } +// ============================================================================= +// Personal Exceptions (Plan 02 Slice 1) +// ============================================================================= + +runSuite("PersonalExceptions: Normalization & Canonicalization") { + // Case variants + assertEqual(PersonalExceptionsPolicy.canonicalize("Apple"), "apple", "lowercase Apple") + assertEqual(PersonalExceptionsPolicy.canonicalize("APPLE"), "apple", "lowercase APPLE") + assertEqual(PersonalExceptionsPolicy.canonicalize("КиЇв"), "київ", "lowercase Ukrainian letters") + + // NFC / NFD canonical composition + let decomposed = "e\u{0301}" + let precomposed = "é" + assertEqual(PersonalExceptionsPolicy.canonicalize(decomposed), precomposed, "NFD must compose to NFC") + assertEqual(PersonalExceptionsPolicy.canonicalize(precomposed), precomposed, "NFC must stay NFC") + + // All three apostrophes: ASCII, U+2019, U+02BC + assertEqual(PersonalExceptionsPolicy.canonicalize("don't"), "don't", "ASCII apostrophe") + assertEqual(PersonalExceptionsPolicy.canonicalize("don’t"), "don't", "U+2019 curly apostrophe") + assertEqual(PersonalExceptionsPolicy.canonicalize("сімʼя"), "сім'я", "U+02BC modifier letter apostrophe") + assertEqual(PersonalExceptionsPolicy.canonicalize("м’ясо"), "м'ясо", "Ukrainian curly apostrophe") + assertEqual(PersonalExceptionsPolicy.canonicalize("мʼясо"), "м'ясо", "Ukrainian modifier letter apostrophe") + + // Diacritics preserved (no accent stripping) + assertEqual(PersonalExceptionsPolicy.canonicalize("café"), "café", "diacritics must not be stripped") + assert(PersonalExceptionsPolicy.canonicalize("café") != "cafe", "café is distinct from cafe") + + // Hyphens and backticks remain distinct + assertEqual(PersonalExceptionsPolicy.canonicalize("foo-bar"), "foo-bar", "hyphen preserved") + assertEqual(PersonalExceptionsPolicy.canonicalize("foo`bar"), "foo`bar", "backtick preserved") + assert(PersonalExceptionsPolicy.canonicalize("foo-bar") != PersonalExceptionsPolicy.canonicalize("foo`bar"), "hyphen and backtick are distinct") + assert(PersonalExceptionsPolicy.canonicalize("foo'bar") != PersonalExceptionsPolicy.canonicalize("foo-bar"), "apostrophe and hyphen are distinct") +} + +runSuite("PersonalExceptions: Validation rules & boundaries") { + // Valid words + check(PersonalExceptionsPolicy.validate(candidate: "apple") == .success("apple"), "valid word apple") + check(PersonalExceptionsPolicy.validate(candidate: "don't") == .success("don't"), "valid word don't") + check(PersonalExceptionsPolicy.validate(candidate: "v2") == .success("v2"), "valid word v2") + check(PersonalExceptionsPolicy.validate(candidate: "сім'я") == .success("сім'я"), "valid word сім'я") + check(PersonalExceptionsPolicy.validate(candidate: "foo-bar") == .success("foo-bar"), "valid word foo-bar") + check(PersonalExceptionsPolicy.validate(candidate: "foo`bar") == .success("foo`bar"), "valid word foo`bar") + check(PersonalExceptionsPolicy.validate(candidate: " swift ") == .success("swift"), "whitespace trimmed before validation") + + // Empty + check(PersonalExceptionsPolicy.validate(candidate: "") == .failure(.empty), "empty rejected") + check(PersonalExceptionsPolicy.validate(candidate: " ") == .failure(.empty), "whitespace-only rejected") + + // Length ceilings: 64 Characters, 256 bytes + let word64 = String(repeating: "a", count: 64) + check(PersonalExceptionsPolicy.validate(candidate: word64) == .success(word64), "64 chars accepted") + let word65 = String(repeating: "a", count: 65) + check(PersonalExceptionsPolicy.validate(candidate: word65) == .failure(.tooLong(65)), "65 chars rejected") + + // No letters + check(PersonalExceptionsPolicy.validate(candidate: "12345") == .failure(.noLetters), "pure digits rejected") + check(PersonalExceptionsPolicy.validate(candidate: "-") == .failure(.noLetters), "pure punctuation rejected") + + // Invalid characters + check(PersonalExceptionsPolicy.validate(candidate: "foo bar") == .failure(.invalidCharacter(" ")), "space rejected") + check(PersonalExceptionsPolicy.validate(candidate: "foo\nbar") == .failure(.invalidCharacter("\n")), "newline rejected") + check(PersonalExceptionsPolicy.validate(candidate: "foo🎉") == .failure(.invalidCharacter("🎉")), "emoji rejected") + check(PersonalExceptionsPolicy.validate(candidate: "https://apple.com") == .failure(.invalidCharacter(":")), "url rejected") + check(PersonalExceptionsPolicy.validate(candidate: "/usr/bin") == .failure(.invalidCharacter("/")), "path rejected") + check(PersonalExceptionsPolicy.validate(candidate: "user@domain") == .failure(.invalidCharacter("@")), "@ rejected") + + // Punctuation positioning + check(PersonalExceptionsPolicy.validate(candidate: "-foo") == .failure(.invalidPunctuationPosition), "leading hyphen rejected") + check(PersonalExceptionsPolicy.validate(candidate: "'foo") == .failure(.invalidPunctuationPosition), "leading apostrophe rejected") + check(PersonalExceptionsPolicy.validate(candidate: "foo-") == .failure(.invalidPunctuationPosition), "trailing hyphen rejected") + check(PersonalExceptionsPolicy.validate(candidate: "\u{0301}foo") == .failure(.invalidCharacter("\u{0301}")), "leading combining mark rejected") + check(PersonalExceptionsPolicy.validate(candidate: "foo'") == .failure(.invalidPunctuationPosition), "trailing apostrophe rejected") + check(PersonalExceptionsPolicy.validate(candidate: "foo--bar") == .failure(.invalidPunctuationPosition), "adjacent hyphens rejected") + check(PersonalExceptionsPolicy.validate(candidate: "foo''bar") == .failure(.invalidPunctuationPosition), "adjacent apostrophes rejected") +} + +runSuite("PersonalExceptions: Key derivation & matching") { + // Key derivation from tokens + assertEqual(PersonalExceptionsPolicy.deriveMatchingKey(from: "foo"), "foo", "derive plain foo") + assertEqual(PersonalExceptionsPolicy.deriveMatchingKey(from: "foo,"), "foo", "derive foo,") + assertEqual(PersonalExceptionsPolicy.deriveMatchingKey(from: "foo."), "foo", "derive foo.") + assertEqual(PersonalExceptionsPolicy.deriveMatchingKey(from: "\"foo"), "foo", "derive \"foo") + assertEqual(PersonalExceptionsPolicy.deriveMatchingKey(from: "foo!?"), "foo", "derive foo!?") + assertEqual(PersonalExceptionsPolicy.deriveMatchingKey(from: "don't"), "don't", "derive don't with internal apostrophe") + assertEqual(PersonalExceptionsPolicy.deriveMatchingKey(from: "don’t"), "don't", "derive don’t with curly apostrophe") + assertEqual(PersonalExceptionsPolicy.deriveMatchingKey(from: "сімʼя"), "сім'я", "derive сімʼя with U+02BC") + assertEqual(PersonalExceptionsPolicy.deriveMatchingKey(from: "kj,"), "kj", "derive kj, -> kj (documented collision)") + + // Matching policy + let policy = PersonalExceptionsPolicy(words: ["foo", "don't", "сім'я", "cafe"]) + check(policy.matches(rawToken: "foo"), "exact match foo") + check(policy.matches(rawToken: "Foo"), "case variant Foo matches") + check(policy.matches(rawToken: "FOO"), "all-caps FOO matches") + check(policy.matches(rawToken: "foo,"), "token with comma foo, matches") + check(policy.matches(rawToken: "foo."), "token with period foo. matches") + check(policy.matches(rawToken: "\"foo"), "token with leading quote \"foo matches") + check(policy.matches(rawToken: "don't"), "don't matches") + check(policy.matches(rawToken: "don’t"), "don’t with curly apostrophe matches") + check(policy.matches(rawToken: "сімʼя"), "сімʼя with modifier apostrophe matches") + + // Non-matches (no substring, no prefix, distinct tokens) + check(!policy.matches(rawToken: "foobar"), "prefix foobar must not match foo") + check(!policy.matches(rawToken: "foo-bar"), "hyphenated foo-bar must not match foo") + check(!policy.matches(rawToken: "bar"), "unrelated word must not match") + check(!policy.matches(rawToken: "café"), "café must not match cafe (accents distinct)") +} + +runSuite("PersonalExceptions: Store persistence & isolation") { + autoreleasepool { + let suiteName = "com.switchfix.tests.personal-exceptions.\(UUID().uuidString)" + let isolatedDefaults = UserDefaults(suiteName: suiteName)! + defer { + isolatedDefaults.removePersistentDomain(forName: suiteName) + } + + let store = PersonalExceptionsStore(defaults: isolatedDefaults) + + // Empty startup + let initialPolicy = store.loadPolicy() + check(initialPolicy.isEmpty, "initial store must be empty") + check(initialPolicy.count == 0, "initial count is 0") + + // Add words + let add1 = store.add(candidate: "Apple") + check(add1 == .success(PersonalExceptionsPolicy(words: ["apple"]), addedWord: "apple"), "add Apple succeeds as canonical apple") + let add2 = store.add(candidate: "don’t") + check(add2 == .success(PersonalExceptionsPolicy(words: ["apple", "don't"]), addedWord: "don't"), "add don’t succeeds as don't") + + // Duplicate Add + let addDup = store.add(candidate: "apple") + check(addDup == .duplicate("apple"), "duplicate add returns duplicate status") + + // Reload from new instance (restart simulation) + let reloadedStore = PersonalExceptionsStore(defaults: isolatedDefaults) + let reloadedPolicy = reloadedStore.loadPolicy() + check(reloadedPolicy.count == 2, "reloaded policy has 2 words") + check(reloadedPolicy.contains("apple"), "reloaded contains apple") + check(reloadedPolicy.contains("don't"), "reloaded contains don't") + + // Remove word + let removedPolicy = store.remove(word: "Apple") + check(removedPolicy.count == 1, "policy after removal has 1 word") + check(!removedPolicy.contains("apple"), "apple was removed") + check(removedPolicy.contains("don't"), "don't remains") + + _ = store.add(candidate: "Apple") + _ = store.add(candidate: "keep") + let batchRemoved = store.remove(words: ["APPLE", "don’t", "missing"]) + check(batchRemoved.words == ["keep"], "batch removal canonicalizes words and preserves unselected entries") + check(reloadedStore.loadPolicy() == batchRemoved, "batch removal persists the resulting policy") + check(store.remove(words: []) == batchRemoved, "empty batch removal leaves policy unchanged") + + // Malformed defaults handling + // 1. Wrong schemaVersion + isolatedDefaults.set(["schemaVersion": 99, "words": ["corrupted"]], forKey: PersonalExceptionsStore.preferenceKey) + check(store.loadPolicy().isEmpty, "unknown schemaVersion must default to empty policy") + + // 2. Oversized envelope (> 1,000 items) + var oversizedWords: [String] = [] + for i in 0..<1001 { + oversizedWords.append("word\(i)") + } + isolatedDefaults.set(["schemaVersion": 1, "words": oversizedWords], forKey: PersonalExceptionsStore.preferenceKey) + check(store.loadPolicy().isEmpty, "oversized envelope (> 1,000) must default to empty policy") + + // 3. Invalid member inside bounded envelope is skipped, valid members loaded + isolatedDefaults.set(["schemaVersion": 1, "words": ["valid", "invalid emoji 🎉", "another-valid", "1234"]], forKey: PersonalExceptionsStore.preferenceKey) + let filteredPolicy = store.loadPolicy() + check(filteredPolicy.count == 2, "filtered policy should load 2 valid words") + check(filteredPolicy.contains("valid"), "contains valid") + check(filteredPolicy.contains("another-valid"), "contains another-valid") + isolatedDefaults.removePersistentDomain(forName: suiteName) + } +} + +runSuite("PersonalExceptions: Detector current word and layout-only suppression") { + let detector = LayoutDetector() + let mockDelegate = MockDetectorDelegate() + detector.delegate = mockDelegate + detector.currentLayout = .english + detector.personalExceptions = PersonalExceptionsPolicy(words: ["ghbdsn", "futynf"]) + + // "ghbdsn" is normally corrected from EN -> UK ("привіт"). Because it is protected, it must NOT correct. + for char in "ghbdsn" { + detector.addCharacter(String(char)) + } + let res = detector.flushBuffer(boundaryCharacter: " ") + check(res == nil, "protected word 'ghbdsn' must return nil detection") + check(mockDelegate.results.isEmpty, "protected word must emit no delegate results") + + // "futynf" is Ukrainian "агента" typed on the English layout: a text replacement with a switch. + // Because it is protected, neither the replacement nor the switch may happen. + for char in "futynf" { + detector.addCharacter(String(char)) + } + let resLayout = detector.flushBuffer(boundaryCharacter: " ") + check(resLayout == nil, "protected layout-only word 'futynf' must return nil") + check(mockDelegate.results.isEmpty, "protected layout-only word must emit no switch") +} + +runSuite("PersonalExceptions: Detector backward sweep barriers") { + // 1. Run sweep stops before protected earlier word + let detector = LayoutDetector() + let mockDelegate = MockDetectorDelegate() + detector.delegate = mockDelegate + detector.currentLayout = .english + detector.personalExceptions = PersonalExceptionsPolicy(words: ["ghbdsn"]) + + // Type protected word "ghbdsn " (would be "привіт") + for char in "ghbdsn" { detector.addCharacter(String(char)) } + _ = detector.flushBuffer(boundaryCharacter: " ") + check(mockDelegate.results.isEmpty, "first word is protected") + + // Type "hj,jnf " (wrong layout for Ukrainian "робота") + for char in "hj,jnf" { detector.addCharacter(String(char)) } + let res = detector.flushBuffer(boundaryCharacter: " ") + check(res != nil, "second word should correct") + if let r = res { + // Must only correct "hj,jnf" -> "робота", NOT sweep across "ghbdsn"! + assertEqual(r.originalWord, "hj,jnf", "sweep must not cross protected word 'ghbdsn'") + assertEqual(r.convertedWord, "робота", "converted text must be only 'робота'") + } + + // 2. Already converted token in journal is protected if its display or typed matches exception + let detector2 = LayoutDetector() + detector2.currentLayout = .english + for char in "hj,jnf" { detector2.addCharacter(String(char)) } + _ = detector2.flushBuffer(boundaryCharacter: " ") + detector2.personalExceptions = PersonalExceptionsPolicy(words: ["робота"]) + + for char in "ghbdsn" { detector2.addCharacter(String(char)) } + let res2 = detector2.flushBuffer(boundaryCharacter: " ") + check(res2 != nil, "second word should correct") + if let r = res2 { + assertEqual(r.originalWord, "ghbdsn", "sweep must stop before converted protected word 'робота'") + } +} + +runSuite("PersonalExceptions: Explicit triggers bypass protection") { + let detector = LayoutDetector() + detector.currentLayout = .english + detector.personalExceptions = PersonalExceptionsPolicy(words: ["ghbdsn"]) + + // Hotkey trigger bypasses personal exceptions + detector.currentTrigger = .hotkey + for char in "ghbdsn" { detector.addCharacter(String(char)) } + let resHotkey = detector.flushBuffer(boundaryCharacter: nil) + check(resHotkey != nil, "hotkey trigger must bypass personal exceptions") + assertEqual(resHotkey?.convertedWord, "привіт", "hotkey must convert protected word") + + // LayoutSwitch trigger bypasses personal exceptions + detector.currentTrigger = .layoutSwitch + for char in "ghbdsn" { detector.addCharacter(String(char)) } + let resSwitch = detector.flushBuffer(boundaryCharacter: nil) + check(resSwitch != nil, "layoutSwitch trigger must bypass personal exceptions") +} + +// ============================================================================= +// Personal Exceptions: remediation coverage (verification of 02.1–02.3) +// ============================================================================= + +runSuite("PersonalExceptions: Letter rule, byte ceiling and shared tokenizer keys") { + check(PersonalExceptionsPolicy.validate(candidate: "\u{0301}") == .failure(.noLetters), "a lone combining mark is not a letter") + + // Each Character is ạ (U+1EA1, 3 bytes) plus U+0301 (2 bytes) after NFC: 5 bytes per Character. + let heavy = String(repeating: "a\u{0323}\u{0301}", count: 64) + assertEqual(heavy.precomposedStringWithCanonicalMapping.count, 64, "fixture is 64 Characters") + check(PersonalExceptionsPolicy.validate(candidate: heavy) == .failure(.byteLimitExceeded(320)), "64 Characters over 256 UTF-8 bytes are rejected") + let atLimit = String(repeating: "a\u{0323}\u{0301}", count: 51) + if case .success(let canonical) = PersonalExceptionsPolicy.validate(candidate: atLimit) { + assertEqual(canonical.utf8.count, 255, "255 bytes are accepted") + } else { + check(false, "51 heavy Characters (255 bytes) must be accepted") + } + + // Keys come from the detector's own tokenizers. + assertEqual(PersonalExceptionsPolicy.deriveMatchingKey(from: "(foo)"), "foo", "brackets are stripped from the key") + assertEqual(PersonalExceptionsPolicy.deriveMatchingKey(from: "сімʼя,"), "сім'я", "U+02BC stays inside the key, comma is stripped") + check(PersonalExceptionsPolicy.deriveMatchingKey(from: "!?") == nil, "punctuation-only tokens have no key") +} + +runSuite("PersonalExceptions: Store capacity, stored types and deterministic serialization") { + let suiteName = "com.switchfix.tests.personal-exceptions.remediation.\(UUID().uuidString)" + let defaults = UserDefaults(suiteName: suiteName)! + let store = PersonalExceptionsStore(defaults: defaults) + let key = PersonalExceptionsStore.preferenceKey + + // 1,000-entry capacity on Add. + defaults.set(["schemaVersion": 1, "words": (0..<1000).map { "w\($0)" }], forKey: key) + assertEqual(store.loadPolicy().count, 1000, "1,000 stored entries load") + check(store.add(candidate: "extra") == .invalid(.capacityExceeded), "Add beyond 1,000 entries reports capacity") + check(store.add(candidate: "w7") == .duplicate("w7"), "duplicate Add at capacity stays a harmless no-op") + assertEqual(store.loadPolicy().count, 1000, "capacity rejection does not change the stored list") + + // Malformed types are unreadable and are not overwritten by loading. + defaults.set(["schemaVersion": 1, "words": "foo"], forKey: key) + check(store.loadPolicyReportingIssue().issue == .unreadable, "words of the wrong type are unreadable") + defaults.set(["foo", "bar"], forKey: key) + check(store.loadPolicyReportingIssue().issue == .unreadable, "an envelope of the wrong type is unreadable") + _ = store.loadPolicy() + check((defaults.array(forKey: key) as? [String]) == ["foo", "bar"], "loading never overwrites an unreadable value") + defaults.set(["schemaVersion": 99, "words": ["corrupted"]], forKey: key) + check(store.loadPolicyReportingIssue() == (PersonalExceptionsPolicy.empty, .unreadable), "unknown version is unreadable and loads nothing") + + // Non-string and invalid members are skipped and reported. + defaults.set(["schemaVersion": 1, "words": ["valid", 42, "two words", "valid"]], forKey: key) + let mixed = store.loadPolicyReportingIssue() + check(mixed.policy == PersonalExceptionsPolicy(words: ["valid"]), "valid members load and duplicates collapse") + check(mixed.issue == .skippedEntries(2), "skipped members are counted for the Settings message") + check(store.loadPolicyReportingIssue().policy.count == 1, "reading twice gives the same result") + + // Deterministic serialization: insertion order does not matter. + defaults.removeObject(forKey: key) + check(store.loadPolicyReportingIssue().issue == nil, "empty storage reports no issue") + _ = store.add(candidate: "zebra") + _ = store.add(candidate: "Apple") + _ = store.add(candidate: "mango") + let stored = defaults.dictionary(forKey: key) + check((stored?["words"] as? [String]) == ["apple", "mango", "zebra"], "words are stored sorted and canonical") + check((stored?["schemaVersion"] as? Int) == 1, "schema version is stored") + check(Set(stored?.keys.map { $0 } ?? []) == ["schemaVersion", "words"], "nothing else is stored") + + defaults.removePersistentDomain(forName: suiteName) + check(UserDefaults(suiteName: suiteName)?.object(forKey: key) == nil, "isolated defaults are removed after the test") +} + +runSuite("PersonalExceptions: Protected word keeps its punctuation and documented collision") { + let control = LayoutDetector() + control.currentLayout = .english + let corrected = flushWord(control, "kj,") + assertEqual(corrected?.convertedWord, "лоб", "control: 'kj,' is corrected to 'лоб' without exceptions") + + let detector = LayoutDetector() + detector.currentLayout = .english + detector.personalExceptions = PersonalExceptionsPolicy(words: ["kj"]) + check(flushWord(detector, "kj,") == nil, "protecting 'kj' also leaves 'kj,' as typed (documented trade-off)") + + let quoted = LayoutDetector() + quoted.currentLayout = .english + quoted.personalExceptions = PersonalExceptionsPolicy(words: ["ghbdsn"]) + check(flushWord(quoted, "ghbdsn.") == nil, "protected word before a period is left as typed") + check(flushWord(quoted, "Ghbdsn,") == nil, "case variant before a comma is left as typed") +} + +runSuite("PersonalExceptions: Layout-only prediction suppression") { + func desynchronizedDetector() -> LayoutDetector { + let detector = LayoutDetector() + detector.currentLayout = .english + detector.activeSourceSupportedLayouts = [.english] + detector.allowedLayouts = [.english, .ukrainian] + return detector + } + let control = flushWord(desynchronizedDetector(), "агента") + check(control != nil && control?.originalWord == control?.convertedWord && control?.shouldSwitchLayout == true, "control: Cyrillic captured under English is a layout-only switch") + + let detector = desynchronizedDetector() + detector.personalExceptions = PersonalExceptionsPolicy(words: ["агента"]) + check(flushWord(detector, "агента") == nil, "a protected word produces no layout-only switch") +} + +runSuite("PersonalExceptions: Protected word does not vote toward a later switch") { + func detector(_ policy: PersonalExceptionsPolicy) -> LayoutDetector { + let detector = LayoutDetector() + detector.currentLayout = .ukrainian + detector.ukrainianFromVariant = .legacy + detector.personalExceptions = policy + return detector + } + let control = detector(.empty) + _ = flushWord(control, "фил") + check(flushWord(control, "ьу")?.shouldSwitchLayout == true, "control: two agreeing short words switch the layout") + + let protected = detector(PersonalExceptionsPolicy(words: ["ше"])) + assertEqual(flushWord(protected, "фил")?.convertedWord, "ask", "first short word converts without switching") + check(flushWord(protected, "ше") == nil, "protected short word is left as typed") + let afterProtected = flushWord(protected, "ьу") + assertEqual(afterProtected?.originalWord, "ьу", "the sweep does not reach the protected word") + check(afterProtected?.shouldSwitchLayout == false, "the protected word resets pending switch confirmations") +} + +runSuite("PersonalExceptions: Barrier at run start, middle, end and beside converted text") { + func sequence(_ policy: PersonalExceptionsPolicy, boundary: String = " ") -> [DetectionResult?] { + let detector = LayoutDetector() + detector.currentLayout = .ukrainian + detector.personalExceptions = policy + return ["філ", "ьу", "ьщку"].map { word in + detector.discardBuffer() + for char in word { detector.addCharacter(String(char)) } + return detector.flushBuffer(boundaryCharacter: boundary) + } + } + + let control = sequence(.empty) + assertEqual(control[2]?.originalWord, "філ me ьщку", "control: the confirming word sweeps back over the run") + + let start = sequence(PersonalExceptionsPolicy(words: ["філ"])) + assertEqual(start[1]?.convertedWord, "me", "run start protected: the short word still converts") + assertEqual(start[2]?.originalWord, "ьщку", "run start protected: the sweep stops before 'філ'") + assertEqual(start[2]?.convertedWord, "more") + + let middle = sequence(PersonalExceptionsPolicy(words: ["ьу"])) + check(middle[1] == nil, "run middle protected: the protected word is left as typed") + assertEqual(middle[2]?.originalWord, "ьщку", "run middle protected: the sweep stops before 'ьу'") + + let end = sequence(PersonalExceptionsPolicy(words: ["ьщку"])) + check(end[0] == nil, "run end protected: 'філ' stays as typed") + assertEqual(end[1]?.convertedWord, "me") + check(end[2] == nil, "run end protected: the protected word triggers no sweep") + + // 'ьу' was converted to 'me' before the confirming word; protecting its on-screen + // spelling must stop the sweep from deleting and retyping it. + let display = sequence(PersonalExceptionsPolicy(words: ["me"])) + assertEqual(display[1]?.convertedWord, "me", "typed 'ьу' is not protected, so it converts") + assertEqual(display[2]?.originalWord, "ьщку", "the sweep stops before the converted text 'me'") + assertEqual(display[2]?.convertedWord, "more") + + let newlineControl = sequence(.empty, boundary: "\n") + let newlineMiddle = sequence(PersonalExceptionsPolicy(words: ["ьу"]), boundary: "\n") + check(newlineMiddle[1] == nil, "newline boundary: protected word is left as typed") + check(!(newlineMiddle[2]?.originalWord.contains("ьу") ?? false), "newline boundary: the protected word is never in a replacement span") + check(newlineControl[2] != nil, "newline control: the confirming word still corrects") +} + +runSuite("PersonalExceptions: Empty-policy parity") { + let words = ["філ", "ьу", "ьщку", "ghbdsn", "hj,jnf", "туче", "ьщку", "kj,", "агента"] + func results(_ policy: PersonalExceptionsPolicy) -> [String] { + let detector = LayoutDetector() + detector.currentLayout = .ukrainian + detector.personalExceptions = policy + return words.map { word in + guard let r = flushWord(detector, word) else { return "nil" } + return "\(r.originalWord)->\(r.convertedWord)|\(r.targetLayout.rawValue)|\(r.shouldSwitchLayout)" + } + } + assertEqual(results(.empty), results(PersonalExceptionsPolicy(words: ["unrelated"])), "an unrelated exception changes no detection") +} + // ============================================================================= // Performance // ============================================================================= diff --git a/Sources/UI/PreferencesManager.swift b/Sources/UI/PreferencesManager.swift index 557d4de..3c77f08 100644 --- a/Sources/UI/PreferencesManager.swift +++ b/Sources/UI/PreferencesManager.swift @@ -23,7 +23,8 @@ public class PreferencesManager { static let revertHotkeyKeyCode = "SwitchFix_revertHotkeyKeyCode" static let revertHotkeyModifiers = "SwitchFix_revertHotkeyModifiers" static let enabledLayouts = "SwitchFix_enabledLayouts" - static let allowCyrillicToCyrillic = "SwitchFix_allowCyrillicToCyrillic" + static let lastCyrillicLayout = "SwitchFix_lastCyrillicLayout" + static let legacyAllowCyrillicToCyrillic = "SwitchFix_allowCyrillicToCyrillic" } public var enabledLayouts: Set { @@ -42,13 +43,53 @@ public class PreferencesManager { } } - public var allowCyrillicToCyrillic: Bool { - get { defaults.object(forKey: Keys.allowCyrillicToCyrillic) as? Bool ?? false } + /// The Cyrillic layout the user was last on. Latin text is only converted to this layout. + public var lastCyrillicLayout: Layout? { + get { defaults.string(forKey: Keys.lastCyrillicLayout).flatMap { Layout(rawValue: $0) }.flatMap { $0.isCyrillic ? $0 : nil } } set { - guard newValue != self.allowCyrillicToCyrillic else { return } - defaults.set(newValue, forKey: Keys.allowCyrillicToCyrillic) + guard let newValue, newValue.isCyrillic, newValue != lastCyrillicLayout else { return } + defaults.set(newValue.rawValue, forKey: Keys.lastCyrillicLayout) + } + } + + /// Drops the removed Cyrillic-to-Cyrillic setting. + public func removeLegacySettings() { + defaults.removeObject(forKey: Keys.legacyAllowCyrillicToCyrillic) + } + + public let exceptionsStore = PersonalExceptionsStore() + + public var personalExceptionsPolicy: PersonalExceptionsPolicy { + exceptionsStore.loadPolicy() + } + + /// A recoverable problem with the saved list, for a generic Settings message. + public var personalExceptionsLoadIssue: PersonalExceptionsLoadIssue? { + exceptionsStore.loadPolicyReportingIssue().issue + } + + public func addPersonalException(_ candidate: String) -> AddExceptionResult { + let result = exceptionsStore.add(candidate: candidate) + if case .success = result { + NotificationCenter.default.post(name: .preferencesDidChange, object: nil) + } + return result + } + + @discardableResult + public func removePersonalException(_ word: String) -> PersonalExceptionsPolicy { + return removePersonalExceptions([word]) + } + + @discardableResult + public func removePersonalExceptions(_ words: Set) -> PersonalExceptionsPolicy { + guard !words.isEmpty else { return exceptionsStore.loadPolicy() } + let previous = exceptionsStore.loadPolicy() + let policy = exceptionsStore.remove(words: words) + if policy != previous { NotificationCenter.default.post(name: .preferencesDidChange, object: nil) } + return policy } public var isEnabled: Bool { diff --git a/Sources/UI/SettingsView.swift b/Sources/UI/SettingsView.swift index 512681c..7416338 100644 --- a/Sources/UI/SettingsView.swift +++ b/Sources/UI/SettingsView.swift @@ -67,9 +67,6 @@ class SettingsViewModel: ObservableObject { @Published var enabledLayouts: Set = PreferencesManager.shared.enabledLayouts { didSet { PreferencesManager.shared.enabledLayouts = enabledLayouts } } - @Published var allowCyrillicToCyrillic: Bool = PreferencesManager.shared.allowCyrillicToCyrillic { - didSet { PreferencesManager.shared.allowCyrillicToCyrillic = allowCyrillicToCyrillic } - } init() { reloadLayouts() @@ -90,9 +87,6 @@ class SettingsViewModel: ObservableObject { if self.enabledLayouts != PreferencesManager.shared.enabledLayouts { self.enabledLayouts = PreferencesManager.shared.enabledLayouts } - if self.allowCyrillicToCyrillic != PreferencesManager.shared.allowCyrillicToCyrillic { - self.allowCyrillicToCyrillic = PreferencesManager.shared.allowCyrillicToCyrillic - } reloadLayouts() } @@ -324,6 +318,149 @@ class ExclusionsViewModel: ObservableObject { } } +class PersonalExceptionsViewModel: ObservableObject { + @Published var words: [String] = [] + @Published var selection: Set = [] + @Published var newWordInput: String = "" + @Published var statusMessage: String? + @Published var isErrorMessage: Bool = false + @Published var loadIssueMessage: String? + + init() { + reload() + NotificationCenter.default.addObserver(self, selector: #selector(reload), name: .preferencesDidChange, object: nil) + } + + deinit { + NotificationCenter.default.removeObserver(self) + } + + @objc func reload() { + let currentWords = PreferencesManager.shared.personalExceptionsPolicy.words.sorted() + if self.words != currentWords { + self.words = currentWords + } + selection.formIntersection(words) + let issueMessage: String? + switch PreferencesManager.shared.personalExceptionsLoadIssue { + case .unreadable: + issueMessage = "Saved personal exceptions could not be read and are not in use. Adding or removing a word saves a new list." + case .skippedEntries: + issueMessage = "Some saved personal exceptions were invalid and are ignored. Adding or removing a word saves the cleaned list." + case nil: + issueMessage = nil + } + if loadIssueMessage != issueMessage { + loadIssueMessage = issueMessage + } + } + + func addWord() { + let trimmed = newWordInput.trimmingCharacters(in: .whitespacesAndNewlines) + guard !trimmed.isEmpty else { return } + + let result = PreferencesManager.shared.addPersonalException(trimmed) + switch result { + case .success(_, let added): + newWordInput = "" + statusMessage = "Added '\(added)'" + isErrorMessage = false + reload() + case .duplicate: + statusMessage = "Already in the list" + isErrorMessage = false + case .invalid(let error): + statusMessage = error.localizedDescription + isErrorMessage = true + } + } + + func removeSelected() { + PreferencesManager.shared.removePersonalExceptions(selection) + selection.removeAll() + statusMessage = nil + reload() + } +} + +struct PersonalExceptionsView: View { + @StateObject private var model = PersonalExceptionsViewModel() + + var body: some View { + VStack(alignment: .leading, spacing: 8) { + HStack { + Text("Personal Exceptions").font(.headline) + Spacer() + Text("\(model.words.count)/\(PersonalExceptionsPolicy.maxEntries)") + .font(.caption) + .foregroundColor(.secondary) + } + Text("These words stay as typed during automatic correction. Hotkey and layout-switch corrections still work.") + .font(.caption) + .foregroundColor(.secondary) + + VStack(spacing: 0) { + if model.words.isEmpty { + Text("No exceptions added yet.") + .font(.callout) + .foregroundColor(.secondary) + .frame(maxWidth: .infinity, minHeight: 120, alignment: .center) + } else { + List(model.words, id: \.self, selection: $model.selection) { word in + Text(word) + .font(.body) + .tag(word) + } + .frame(height: 120) + } + + Divider() + + HStack(spacing: 8) { + TextField("Add word (e.g. Swift, don't, Kyiv)…", text: $model.newWordInput) + .textFieldStyle(.roundedBorder) + .onSubmit { + model.addWord() + } + + Button("Add") { + model.addWord() + } + .disabled(model.newWordInput.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty) + + Divider().frame(height: 16) + + Button(action: model.removeSelected) { + Image(systemName: "minus") + .frame(width: 20, height: 20) + } + .buttonStyle(.borderless) + .disabled(model.selection.isEmpty) + .accessibilityLabel("Remove Selected") + } + .padding(6) + .background(Color(nsColor: .controlBackgroundColor)) + } + .overlay( + RoundedRectangle(cornerRadius: 6) + .stroke(Color.gray.opacity(0.3), lineWidth: 1) + ) + + if let issue = model.loadIssueMessage { + Text(issue) + .font(.caption) + .foregroundColor(.orange) + } + + if let message = model.statusMessage { + Text(message) + .font(.caption) + .foregroundColor(model.isErrorMessage ? .red : .secondary) + } + } + } +} + struct RunningAppPickerView: View { @ObservedObject var model: ExclusionsViewModel @Environment(\.dismiss) private var dismiss @@ -531,16 +668,6 @@ struct KeyboardLayoutsView: View { RoundedRectangle(cornerRadius: 6) .stroke(Color.gray.opacity(0.3), lineWidth: 1) ) - - VStack(alignment: .leading, spacing: 4) { - Toggle("Allow Cyrillic-to-Cyrillic correction (Ukrainian ↔ Russian)", isOn: $model.allowCyrillicToCyrillic) - .font(.subheadline) - - Text("Disabled by default to avoid unintended corrections between similar Cyrillic alphabets.") - .font(.caption) - .foregroundColor(.secondary) - } - .padding(.top, 4) } } } @@ -642,6 +769,11 @@ struct SettingsView: View { Divider() + // PERSONAL EXCEPTIONS + PersonalExceptionsView() + + Divider() + // EXCLUDED APPS ExcludedAppsView() @@ -649,7 +781,7 @@ struct SettingsView: View { } .padding(30) } - .frame(width: 480, height: 750) + .frame(width: 480, height: 850) .onAppear { model.syncLaunchAtLogin() } } } diff --git a/docs/index.html b/docs/index.html index 7695915..def9cce 100644 --- a/docs/index.html +++ b/docs/index.html @@ -798,7 +798,7 @@
- Free & Open Source macOS Utility • v0.0.11 + Free & Open Source macOS Utility • v0.0.12

Never retype
@@ -941,8 +941,8 @@

Built for Speed, Privacy & Precision

⚡️
-

Instant & Seamless

-

Engineered in pure Swift with Carbon Text Input Services. Detects layout mismatches in sub-milliseconds without key lag or typing stutter.

+

Smarter Phrase Correction

+

When a later word confirms a layout mistake, SwitchFix can also fix earlier words in the same typing run—even words that looked valid on their own.

@@ -965,15 +965,16 @@

Selection Hotkey

🌐
-

Three Layout Systems

-

Full dictionary and phonetic layout mapping for English (US, British, ABC, Dvorak, Colemak), Ukrainian, and Russian.

+

Safer Language Pairing

+

English pairs with your most recently used Ukrainian or Russian layout. Cyrillic languages stay separate, and native macOS language metadata helps discover your installed layouts.

-
🎯
-

Smart App Exclusion

-

Automatically stays quiet in Terminals, IDEs, code editors, and command prompts so code identifiers and shell flags are never mangled.

+
📝
+

Personal Exceptions

+

Keep names, abbreviations, and favorite words as typed. Add them in Settings to protect them from automatic correction; explicit hotkey and layout-switch corrections still work.

+
diff --git a/docs/plans/01-dynamic-layout-mapping.md b/docs/plans/01-dynamic-layout-mapping.md new file mode 100644 index 0000000..2c14173 --- /dev/null +++ b/docs/plans/01-dynamic-layout-mapping.md @@ -0,0 +1,185 @@ +# Dynamic, source-specific keyboard layout mapping + +Status: Phase A is implementation-ready; Phase B is gated on benchmark evidence (see [Phase B](#phase-b--keystroke-provenance-gated)). Revised 2026-10-19 after review against the baseline. Baseline: `4b928be`. Platform: macOS 13+, existing SwiftPM targets, no added dependencies. Lands after plan 02, which introduces the shared `invalidateCorrectionConfiguration()` primitive and `CorrectionTrigger`; if plan 02 has not landed, add the primitive here with focused tests. + +## Outcome and scope + +Convert mistaken-layout text using the actual installed source tables, rather than assuming every English source is US QWERTY and every Cyrillic source has standard positions. Support English ↔ Ukrainian and English ↔ Russian. Keep Ukrainian ↔ Russian excluded. Preserve existing dictionary decisions, application exclusions, secure-field checks, bounded buffers, cancellation, rollback, and native undo handling. + +The work has two phases: + +- **Phase A — real tables, exact sources, text-level mapping.** Build per-source tables from macOS layout data, bind every correction to exact source IDs, and convert token text through the [canonical reverse rule](#canonical-reverse-rule). This covers Dvorak, Colemak, Apple Ukrainian, Ukrainian-PC, Ukrainian-QWERTY and Russian Phonetic without carrying keystrokes through the pipeline. Supported output is stateless, single-BMP-scalar printable output on unmodified and Shift strokes. Option output, Caps-exact punctuation, dead keys/composition, surrogate pairs and multi-character output are rejected, so a ready source can still skip individual tokens. +- **Phase B — keystroke provenance.** Carry each typed key's stroke through capture, state machine, run tokens, journal and requests, and capture Option output. Start it only when Phase A benchmark results show losses caused by text-level mapping. + +Phase A is sufficient for source identity: `InputStateMachine.updateContext` clears the run on every context change and the context includes `inputSourceID`, so every token in a run, including tokens reached by a sweep, was typed on the request's source. + +### Measured facts behind this design + +Recorded 2026-10-18 with an independent `UCKeyTranslate` probe on macOS 15.7.9 (24G830), keyboard type 91 (`LMGetKbdType()`): + +- `com.apple.keylayout.Ukrainian` puts `и` on S (key 1), `і` on B (key 11) and `ґ` on `\` (key 42), keeps `/` and `?` on key 44, and types `,` and `.` with Shift+6 and Shift+7. The static `enToUkStandard` table assumes `і` on S, `ґ` on `` ` `` and `.` on `/`, which matches Ukrainian-PC. Variant probing currently covers only the S/B swap; `ґ` and the punctuation keys convert wrongly today. +- No `Ukrainian-Legacy` source exists on this macOS version; Apple `Ukrainian` itself has the arrangement the code calls "legacy". Installed Apple sources include `ABC`, `US`, `British`, `Colemak`, `Dvorak` (plus `-Left`/`-Right`), `Ukrainian`, `Ukrainian-PC`, `Ukrainian-QWERTY`, `Russian`, `RussianWin` and `Russian-Phonetic`. +- Building complete tables (128 keys × 8 Shift/Caps/Option states) for five sources took 1.7 ms. Asynchronous building is unnecessary. +- The previously proposed reverse rule ("every source stroke that emits a scalar must yield the same target output") rejected `.`, `,`, `;`, `'`, `[` and `]` on ABC → Ukrainian/Russian, and `,`/`.` on Ukrainian → ABC, because Caps Lock and numeric-keypad variants of the same character convert differently (ABC `.` → `ю`, `Ю`, `.`, `,`). Ordinary sentences such as `Ghbdsn.` were rejected. The canonical rule accepts them. + +## Current implementation and required changes + +- `Sources/Core/LayoutMapper.swift` owns static QWERTY character maps, permissively preserves unknown characters, and exposes Ukrainian standard/legacy variants. Keep its language eligibility policy (`isEligibleTarget`); replace its production conversion. +- `InputSourceDiscoveryEngine.swift` already probes macOS source tables. `CarbonTISPropertyAdapter.swift` returns only `.first` from a 4-unit UTF-16 buffer; this cannot represent translation validity or composition. Discovery is useful evidence, not conversion readiness. +- `InputSourceManager.swift` caches sources and preferences, including exact source selection via `switchToSource(id:layout:)`. `fallbackSupportedLayouts` returns `[.english]` for anything unmatched and `currentLayout()` defaults to English; remove that inference for correction eligibility. +- `KeyboardMonitor` has a separate Shift-only HID translation cache built from the current source with `TISCopyCurrentKeyboardLayoutInputSource` as fallback. Phase A replaces it with a lookup into the captured source's snapshot table; no other source may supply characters. Any Option-modified key is classified as `.navigation` (`KeyboardMonitor.swift:372`), which also invalidates focus; Phase A keeps that behavior. +- `LayoutDetector` converts current and prior words via `LayoutMapper` and tries alternate Ukrainian variants (`LayoutDetector.swift:440–464`, `735–751`). Phase A converts through the exact source and target tables and removes variant guessing. +- `InputEngine` reads `detectionConfiguration` at detection time (`InputEngine.swift:375`). Phase A captures the mapping snapshot in the request at creation time. +- `TextCorrector` and `performSelectionCorrection` call `switchTo(layout)` after generating text. Bind and validate the exact source used to produce the replacement, including selection correction and undo. +- `CapturedInput` records key code, flags and source ID. Add the event's keyboard type. + +## Concrete data and API design + +Add `Sources/Core/KeyboardLayoutMapping.swift` containing small value types and the pure mapper. Do not introduce a service hierarchy. + +```swift +struct KeyStroke: Hashable, Sendable { + let keyCode: UInt16 + let modifiers: UInt8 // Carbon high-byte bits: Shift, Caps, Option only +} +struct SourceKey: Hashable, Sendable { + let sourceID: String + let keyboardType: UInt32 +} +struct SourceMapping: Sendable { + let key: SourceKey + let layoutDataHash: UInt64 + let outputs: [KeyStroke: String] // all eight modifier states + let reverse: [String: [KeyStroke]] // canonical candidates only + let rejected: [KeyStroke: TranslationRejection] +} +struct MappingSnapshot: Sendable { + let generation: UInt64 + let sources: [SourceKey: SourceMapping] + let preferredSourceIDs: [Layout: String] +} +struct MappingBinding: Equatable, Sendable { + let generation: UInt64 + let sourceID: String + let targetID: String + let keyboardType: UInt32 +} +``` + +Provide `convertText(_:from:to:keyboardType:snapshot:)` returning `Result`. `MappedText` includes the full replacement and binding. Failures distinguish unavailable source, unsupported pair, stale snapshot, unknown keyboard type, unsupported output, unmappable scalar and ambiguous reverse mapping, each with a stable reason code the benchmark can report. Do not return partially converted strings. Keep failures internal except for a short manual-correction status message; never log token contents. `outputs` keeps all eight modifier states so fixture tables are complete and Phase B can use them; `reverse` holds only the canonical subset. + +Language identity remains separate from physical mapping: the request includes source/target `Layout` values for dictionary and eligibility logic, plus exact source IDs. Reuse `LayoutMapper.isEligibleTarget`; add explicit opposite-script validation before mapping. + +## Native table builder + +Extend `CarbonTISPropertyAdapter` with a translation result API that preserves the complete UTF-16 output, status and returned dead-key state. Keep `translatedCharacter` as a guarded discovery wrapper until its callers migrate. Add a fake translator at this boundary for runnable fixtures. + +Build synchronously on the main thread inside `refreshInstalledSources()`: enumerate enabled keyboard-layout sources as today, copy `kTISPropertyUnicodeKeyLayoutData` into owned `Data`, build immutable tables for the current keyboard type, and publish descriptors and tables together under the existing state lock. `AppDelegate` first touches `InputSourceManager.shared` on the main thread; add a main-thread precondition. No background builder, request counter or "mapping unavailable" window is needed. TIS objects and unowned pointers never leave the builder; layout pointers remain inside `withUnsafeBytes`. + +For virtual keys 0..<128 and all eight Shift/Caps/Option combinations: + +1. Translate with `kUCKeyActionDown`, dead state initialized to zero and options zero. Any nonzero returned dead state marks that stroke unsupported, even if a spacing character was emitted. +2. For a stateless result, decode the complete output buffer. Allocate 255 UTF-16 units, check status/length, and reject invalid UTF-16 instead of using replacement-character decoding. +3. Accept exactly one BMP scalar, excluding surrogate code units, controls, private-use function-key output and nonprinting output. Allow ordinary space only for explicit separator handling; never include Return/Tab/function/navigation keys in a printable map. +4. Populate `outputs`, then build `reverse` from the canonical candidates only. + +Use Carbon modifier constants shifted right eight bits, not raw `CGEventFlags`. Map the flags explicitly. Command/Control combinations remain commands and invalidate tracking as today. + +`kUCKeyTranslateNoDeadKeysBit` is passed as an options mask at three sites. The bit constant is zero; the corresponding mask is `1 << kUCKeyTranslateNoDeadKeysBit`. Treat each site differently: + +- `CarbonTISPropertyAdapter.swift:56` (discovery): switch to the mask. Discovery may show a dead key's spacing character. +- `Utils/KeyCodeMapping.swift:28` (hotkey label in Settings): switch to the mask. +- `KeyboardMonitor.swift:454` (HID capture): **do not** switch to the mask. With options zero a dead key returns no characters, so the event falls through to `.navigation` and tracking stops, which is the safe outcome. The mask would put the spacing accent into the buffer while the application composes a different character, so buffer and screen would disagree. Phase A replaces this translation with a snapshot lookup in which dead-key strokes have no entry. + +Apple documents full-string output, modifier-byte conversion, keyboard type and dead-state behavior in [UCKeyTranslate](https://developer.apple.com/documentation/coreservices/1390584-uckeytranslate); the [bit constant definition](https://developer.apple.com/documentation/coreservices/1390507-key_translation_options_flag/kuckeytranslatenodeadkeysbit?language=objc) explains the mask distinction. UCKeyTranslate is used here because the request concerns a specific inactive source's layout data, with an existing Carbon implementation; keep it isolated. + +## Canonical reverse rule + +Phase A uses one rule for typed tokens and selected text. For each scalar: + +1. Candidates are strokes on main-block keys only. Exclude numeric-keypad key codes (65, 67, 69, 71, 75, 76, 78, 81–89, 91, 92, 95) and every non-printing key. Allowed modifier states are unmodified and Shift. Caps Lock and Option strokes are never reverse candidates: selected text carries no Caps state, and Option output is not captured in Phase A. +2. Use the lowest tier that contains the scalar (unmodified before Shift). +3. Every candidate in that tier must produce the same supported output in the target table; otherwise reject the whole token or selection. Identical outputs from duplicate keys are safe. No dictionary-based tie-breaking. +4. Spaces, tabs and line breaks in selections are copied as separators. Emoji, combining sequences, non-BMP characters and scalars with no candidate reject the whole selection. No normalization or accent stripping. This is intentionally stricter than the current preserve-unknown behavior. When a selection is rejected, do not silently convert a buffered word instead; leave the selection untouched and report briefly. + +Case: uppercase letters come from the Shift tier. Keep the detector's existing `applyCase`, so an all-caps word typed under Caps Lock still converts to all caps (`GHBDSN,` → `ПРИВІТб` → `ПРИВІТБ` on Ukrainian-PC). Punctuation typed under Caps Lock inside a mixed-case word converts as unshifted; Phase B addresses that if the benchmark shows it matters. + +Known consequence: Apple Ukrainian types `,` and `.` with Shift+6 and Shift+7, so key-for-key inversion of `Руддщ, цщкдв.` is `Hello^ world&`. This is physically correct but rarely intended. Keep key-for-key behavior in Phase A, give it a benchmark stratum, and treat any change as a product decision. + +Measured expectations to seed fixtures (re-verify against native on-screen output in the manual matrix): + +| From → to | Input | Expected | +| --- | --- | --- | +| ABC → Ukrainian-PC | `ghbdsn` | `привіт` | +| ABC → Ukrainian (Apple) | `Ghsdbn.` | `Привітю` | +| Ukrainian (Apple) → ABC | `руддщ цщкдв` | `hello world` | +| Ukrainian (Apple) → ABC | `Руддщ, цщкдв.` | `Hello^ world&` | + +## Typed text and keyboard type + +Convert the captured token text with the canonical rule, using the request's exact source (`context.inputSourceID`), the keyboard type captured with the word, and the preferred target source captured in the snapshot. Sweeps use the same source because the run is cleared on any context change. If the source or target is not ready, abstain. Dictionary casing helpers may only apply case as described above. Preserve existing punctuation guards and script checks. + +Each key event carries its keyboard type in `CGEventField.keyboardEventKeyboardType` (raw value 10). Record it in `CapturedInput`. The snapshot holds tables per `(source ID, keyboard type)`. The first key event whose keyboard type has no tables marks the current word untracked until the next boundary and schedules a main-thread build for that type (about 0.35 ms per source), which publishes a new generation. A word is never converted with another keyboard's table, so switching between ANSI, ISO and JIS keyboards is handled rather than documented as a limitation. Selected text uses the keyboard type of the most recent key event. + +The `KeyboardMonitor` HID fallback looks up the captured source's table for the event's keyboard type and Shift/Caps state. It never falls back to `TISCopyCurrentKeyboardLayoutInputSource` or another source. Dead-key strokes have no entry, so those events stay `.navigation`. + +## Snapshot lifecycle, readiness, and cancellation + +`InputSourceManager` publishes one immutable snapshot through its existing lock. Generations are monotonic. Rebuild synchronously on enabled-source changes, preferred-source changes, a new keyboard type, wake and an explicit readiness refresh. Compare layout-data hashes so a content change under an unchanged source ID publishes a new generation. Avoid filesystem watchers: explicit refresh/restart covers third-party resource edits that macOS does not announce. + +Every publication calls `CaptureStateStore.invalidateCorrectionConfiguration()`, so buffered run, detector journal, pending detection, selection callbacks, correction emission and delayed source selection are invalidated by the existing correction-epoch checks. Snapshot generation identifies table provenance; `correctionEpoch` remains the single authorization check for configuration changes. Check the binding at request creation, before deletion/paste, between existing emission phases and again on the main-thread selection hop. Do not weaken focus, PID, permission, secure-state or edit-generation checks. + +Separate three states: discovery identifies a language; support permits an English/Cyrillic pair; readiness proves exact source tables and a usable pair exist. `availableLayouts` can still populate menus, but runtime readiness and detector allowed targets must use ready preferred-source pairs. Unknown sources and input methods never default to ready English. Hybrid metadata cannot promise letters absent from its tables, and existing same-source automatic-switch suppression remains. + +## Every correction mode and undo + +Automatic and hotkey-buffer correction bind the observed source plus the preferred target ID captured in the snapshot. Selection correction binds the explicit source and target used by the canonical rule. Layout-switch mode converts the buffered word from the old exact source (`AppDelegate.previousInputSourceID`) to the user's newly selected exact source, only for an eligible opposite-script pair; source IDs replace `fromVariant`/`toVariant` in `handleLayoutChange`. Switching between sources of the same language or script invalidates tracked state without converting text; preserve the existing no same-script conversion policy. + +Extend `CorrectionPlan` and selection requests with the mapping binding and exact source IDs. `rebaseUndoContext` (`TextCorrector.swift:202`) rebuilds plans field by field and must copy the binding. Replace all layout-only post-correction switches with `switchToSource(id:layout:)`. Validate selectability before text emission and recheck before switching. If source selection subsequently fails, keep the applied text, report the failure and never choose another source silently. Preserve current rollback behavior for interrupted text emission. + +Undo stores literal original/corrected strings and the original exact source ID; it must not reconvert text. Allow existing safe immediate undo across SwitchFix's own expected source transition, while rejecting external source changes, stale generations, missing original source and all current edit/focus failures. Native Cmd-Z continues to invalidate tracking as today. + +## Ordered implementation and commits (Phase A) + +1. **Lock mapping safety and add the native builder.** Translation result API and fake, pure mapper with the canonical rule, and fixture checks in `Sources/TestRunner/main.swift` using the frozen tables from plan 03 (`Benchmarks/layout-tables/v1`). Switch only the discovery and hotkey-label call sites to the no-dead-keys mask. Production conversion stays unchanged. +2. **Publish ready snapshots synchronously.** Manager, keyboard-type capture in `CapturedInput`/`KeyboardMonitor`, AppDelegate refresh plumbing, runtime readiness and status messages, invalidation through `invalidateCorrectionConfiguration()`. Test unavailable sources, unknown keyboard types and content changes under one source ID. +3. **Convert through exact sources.** Detector (current word and sweeps), hotkey, selection and layout-switch paths, and the `KeyboardMonitor` HID lookup. Requests carry the snapshot. Remove the Ukrainian variant fallbacks. +4. **Bind source selection and undo.** `CorrectionPlan`/selection bindings, `switchToSource(id:layout:)` everywhere, `rebaseUndoContext` and undo. +5. **Remove static maps, verify hardware, document.** Delete production static maps once no call site uses them; keep variant metadata only where useful for discovery labels. Run the manual matrix, add concise user guidance, and rerun the benchmark against the frozen baseline. + +Each commit must build and pass its applicable runner. Expected values come from the frozen fixture tables and native on-screen observations, never from the function under test. No XCTest target registration is necessary. + +## Phase B — keystroke provenance (gated) + +Start Phase B only if the post-Phase-A benchmark attributes missed or wrong repairs to text-level mapping (reason codes `ambiguous_reverse`, `caps_punctuation`, `option_output`), and record that decision in this file. Scope at that point: + +- Add `TypedUnit { text; stroke }` and store units alongside the bounded current buffer. A delete removes the last unit only for the existing single-character deletion. A mismatch, multi-unit edit, overflow, navigation, tap reset or untracked deletion clears provenance in the same state transition. +- Extend `RunToken`, `DetectionRequest` and journal entries with bounded units. Sweeps map each token with its own units; already corrected tokens contribute display text only. Stop a sweep at missing provenance or an incompatible generation. Keep the 16-token bound. +- Convert stroke by stroke through the target table, retaining modifiers including Caps, and verify the reconstructed source text equals the token. Physical outputs then override `applyCase`. +- Capture Option output by reclassifying Option-only key events in `KeyboardMonitor.classify` as characters when the snapshot has a stateless output for that stroke. Option+Delete deletes a word and must stay an untracked deletion rather than `.delete`; Option+Space types U+00A0 and must not be treated as an ordinary space; Option+arrows and Option+Forward-Delete stay navigation; Command/Control remain commands. +- Detect empty/dead-key events before a composed character can enter a word as ordinary typing. + +## Verification and acceptance + +Run from repository root: + +```sh +swift build +swift run -c release TestRunner +swift run -c release InputPipelineTestRunner +swift build -c release +``` + +`Tests/SwitchFixTests/LayoutMapperTests.swift` is a placeholder comment and is not registered in `Package.swift`; existing mapper coverage lives in `TestRunner`, so add new cases there. Run the native-source smoke probe through a small explicit opt-in mode in TestRunner, separate from deterministic fixtures, and print skipped source IDs when the machine lacks them. The pipeline check `TextCorrector must apply layout-only plan without deleting text` (`InputPipelineTestRunner/main.swift:584`) needs Accessibility and fails in sessions without it; record that as environment-dependent, not a regression. + +Required deterministic Phase A cases: the measured expectations above; US/ABC/British parity; Dvorak/Colemak fixtures; Apple Ukrainian versus Ukrainian-PC versus Ukrainian-QWERTY positions, including `ґ` on key 42; Russian and Russian Phonetic; keypad duplicates of `.`, `/`, `*`, `+`, `-` and `=` ignored; Caps-only and Option-only strokes ignored by the reverse rule; unmodified-before-Shift tier selection; duplicate main-block keys with matching and conflicting target output; dead state with and without visible output; empty/invalid/multi-unit/non-BMP output; partial unmappable word and selection; zero postings on rejection; unknown/missing source; unknown keyboard type marks the word untracked; same ID with changed data; source removal or preference change while detection or clipboard work waits; target source identity preserved through delayed switch and immediate undo; a rebased undo plan keeps its binding. + +Manual matrix: built-in ANSI plus an ISO external keyboard (keys 10/50 differ), including switching keyboards mid-session; JIS is required before advertising JIS support. Test each available ABC/US, British, Dvorak, Colemak, Ukrainian/Ukrainian-PC/Ukrainian-QWERTY, Russian/RussianWin/Phonetic source against its actual on-screen output in TextEdit and a browser textarea. Exercise all three modes, selections with punctuation/newlines, undo, fast typing during correction, secure fields, source removal, sleep/wake, keyboard reconnection and a dead-key English source. Record OS/build, keyboard type, source IDs, expected/actual text, exact active source after correction and any skipped hardware. Do not infer hardware coverage from fixture success. + +Acceptance: no production QWERTY fallback; no partial conversion; no source-ID drift; unsupported tokens remain untouched; stale work emits nothing; all existing trust-boundary checks remain; all runners pass apart from documented permission-dependent checks; supported hardware cases match native output. The benchmark must show no unexplained regression against the frozen baseline; any changed abstention is recorded explicitly with its reason code. + +## Integration contracts and non-goals + +Personal exceptions (plan 02) receive the original token before conversion and may veto an automatic correction; they do not alter source tables or resolve mapping ambiguity. Mapping publication reuses plan 02's `invalidateCorrectionConfiguration()`; mapping generation identifies source provenance rather than creating a competing authorization scheme. Benchmark fixture tables live in `Benchmarks/layout-tables/v1` (plan 03); Core exposes a pure loader for them so benchmarks never depend on the operator's installed sources. Mapping rejections are reported with reason codes, separately from dictionary abstention. + +Non-goals: new languages, spell correction, IME/composition emulation, arbitrary Unicode deletion semantics, per-device HID identification (keyboard type is used instead), cloud sync, new dependencies, copying Klavko implementation, or broader detector threshold tuning. Intentional ceilings should receive small `ponytail:` comments in the eventual mapper naming the unmodified/Shift-only reverse tiers and the single-BMP/stateless limit, and when to revisit them (Phase B evidence). diff --git a/docs/plans/02-personal-exceptions.md b/docs/plans/02-personal-exceptions.md new file mode 100644 index 0000000..ca01de8 --- /dev/null +++ b/docs/plans/02-personal-exceptions.md @@ -0,0 +1,111 @@ +# Personal automatic-correction exceptions + +Status: implementation-ready plan; no feature code is included. Revised 2026-10-19 after review against the baseline. + +Baseline: SwitchFix commit `4b928be`. This plan lands before plan 01 and introduces the shared `CaptureStateStore.invalidateCorrectionConfiguration()` primitive and `CorrectionTrigger`, which plan 01 reuses. Run its cases through the shared benchmark. Keep existing correction safety checks intact. + +## Outcome and scope + +Users can protect a word that SwitchFix repeatedly changes incorrectly. Settings contains a persistent list with Add and Remove controls. After successfully reverting a single-word automatic correction, the user can explicitly choose **Never Auto-correct Last Reverted Word** in the menu bar. Merely reverting never adds an exception. + +An exception suppresses automatic text replacement and the associated automatic layout switch. It does not change dictionary validity, language detection outside the protected token, manually requested correction, excluded-app behavior, or access requirements. No dependency, learned-word database, clipboard access, or new Accessibility text read is required. + +The first release has no repeated-undo prompt, counters, notifications, automatic learning, or history. The fixed menu action supplies an explicit, discoverable learning path without interrupting typing. A repeated-undo suggestion is a separate future feature requiring a specified threshold and dismissal policy; it is not a condition for completing this plan. + +## Existing integration points + +- `Sources/Core/InputEngine.swift` schedules automatic boundary detection, manual hotkey detection, selected-text correction, and correction emission on separate queues. Its current `DetectionRequest` does not identify why detection was requested. +- `Sources/Core/LayoutDetector.swift` maintains a journal and can extend a correction backward across preceding words in `sweepRun`. Checking only the current word would leave earlier protected words vulnerable. +- `Sources/Core/TextCorrector.swift` stores the applied `CorrectionPlan` in `UndoState`. `undo` currently returns only `Bool`; successful restoration is the authoritative point for offering an exception. +- `Sources/UI/PreferencesManager.swift` persists settings and posts `.preferencesDidChange`. `AppDelegate.preferencesDidUpdate` forwards snapshots to the engine. Core cannot import UI. +- `Sources/UI/SettingsView.swift` already supplies list/add/remove patterns for excluded apps. `StatusBarController.swift` has explicit menu-item enablement and can expose the learning action without another shortcut. +- `Sources/TestRunner/main.swift` and `Sources/InputPipelineTestRunner/main.swift` are executable assertion runners. Add tests there; do not create an XCTest-only target. + +## Behavioral contract + +### Matching and scope + +Use one global set of original, typed tokens across applications, source IDs, and languages. This means “leave this spelling alone,” including after switching from QWERTY to Dvorak. Do not key entries by a converted candidate or current target language. This choice is independent of dynamic mapper source identity. + +Add a small Core value type, `PersonalExceptionsPolicy`, with a single canonicalization implementation and immutable `Set` lookup. Canonicalization is NFC normalization, locale-independent Swift `lowercased()`, replacement of U+2019 RIGHT SINGLE QUOTATION MARK and U+02BC MODIFIER LETTER APOSTROPHE with ASCII apostrophe, then NFC again. Do not use compatibility normalization, transliteration, accent stripping, stemming, or fuzzy matching. Keep backticks and hyphens distinct. + +Settings trims surrounding whitespace before validation; engine lookup never trims captured fragments into a different token. Accept 1–64 Swift `Character`s and at most 256 UTF-8 bytes after normalization, containing at least one Unicode letter. Allow letters and combining marks, ASCII digits, and internal `'`, backtick, or `-`; punctuation may not be first, last, or adjacent to another punctuation character. Reject whitespace, controls, format characters, emoji, paths, URLs, and multiple tokens. A malformed persisted entry must never become a broader rule. + +Derive the matching key in two steps, both from existing detector code: take the token after `splitTrailingBoundary` (`LayoutDetector.swift:1104`), then drop its leading and trailing non-letter, non-digit characters (the prefix and suffix from `splitTokenForValidation`, `LayoutDetector.swift:1073`), and canonicalize the rest. The second step is required because `splitTrailingBoundary` deliberately keeps ``, . ; ' [ ] ` < > : " { } ~`` in the token (they are letters on Cyrillic layouts), so on its own `foo,` would never match `foo`, and a word before a comma or period would stay unprotected. A protected token is left entirely as typed, including the stripped characters. Accepted trade-off: protecting `kj` also suppresses the correction of `kj,` (`лоб` typed on the wrong layout); the benchmark records this collision in the personalized profile. Put the key derivation in one shared helper used by current-token checks, sweep barriers, engine safeguards and receipt creation; do not add another tokenizer. Protect the entire core token, not a substring or prefix: protecting `foo` does not protect `foobar`; `foo-bar` remains distinct from `foo`; `cafe` remains distinct from `café`. Case variants match. Canonically equivalent composed/decomposed Unicode and the three specified apostrophes match. Settings displays the canonical stored form so its scope is visible. Duplicate Add is a harmless no-op with an inline “Already in the list” message. + +Tokenization must agree with this contract: `KeyboardMonitor.boundaryCharacterSet` already permits ASCII and curly apostrophes, backticks, and hyphens. U+02BC is a modifier letter (Lm), so neither the monitor's nor the detector's boundary set contains it today; add regression cases confirming the complete word, including U+02BC, reaches detection. Do not change boundary rules as part of this work. + +### Automatic versus explicit actions + +Introduce `CorrectionTrigger` with `.automaticBoundary`, `.hotkey`, and `.layoutSwitch`; carry it in `DetectionRequest` and `CorrectionPlan`, including selection plans. Give it no default value in either initializer: every construction site (`InputEngine.prepareCorrection`, the undo inverse plan, `TextCorrector.rebaseUndoContext`, `performSelectionCorrection`, and test helpers) must pass it explicitly. `rebaseUndoContext` (`TextCorrector.swift:202`) rebuilds the stored plan field by field after SwitchFix's own source switch; a defaulted field there would silently turn a hotkey correction into an automatic one, which could produce a learning receipt, or drop an automatic correction's eligibility. The same applies to the single-token provenance below. + +Only `.automaticBoundary` consults personal exceptions. `.hotkey` always permits deliberate current-word or selected-text conversion; `.layoutSwitch` likewise bypasses exceptions because the user explicitly changed the system layout in that correction mode. All retain existing secure-field, application, dictionary-readiness, length, sequence, and focus checks. The existing revert-hotkey fallback to manual correction remains explicit and bypasses exceptions. + +For automatic detection: + +1. If the current captured word matches an exception, return no correction, emit no layout switch, and reset pending confidence/switch state so this word does not vote toward a later switch. +2. Keep the token represented as a journal barrier. A backward sweep stops before any protected token and may still correct the contiguous eligible suffix after it. It must never delete and reinsert the protected token merely to reach earlier words. +3. Apply the same barrier to `journal.display` when an earlier token was already converted, in addition to its original `typed` spelling. This protects newly added exceptions against rewriting text already shown on screen. +4. Do not mark an exception “valid in every dictionary.” That would change unrelated language scoring and make manual behavior inconsistent. + +Implement the check inside the detector path used by normal direct callers and the engine; filtering only after `DetectionResult` creation is insufficient because journal/confidence state can already have changed. A detector policy property or explicit parameter must default to the empty policy for source compatibility. In manual mode install the empty policy for that request. The injected `exactDetection` test seam must also pass through engine-level current-token and sweep-span safeguards so it cannot bypass protection in pipeline tests. + +## Storage and configuration ordering + +Add `Sources/Core/PersonalExceptionsPolicy.swift` for normalization, validation, and immutable snapshots, and `Sources/Core/PersonalExceptionsStore.swift` for a small injected-`UserDefaults` store. Keeping the store in Core allows real persistence tests without importing the UI target. `PreferencesManager` owns the production instance and exposes validated snapshots and add/remove operations; it does not expose raw mutable defaults. + +Persist a property-list dictionary under `SwitchFix_personalExceptions_v1` with `schemaVersion: 1` and sorted canonical `words: [String]`. No timestamp, original casing, app identifier, target spelling, or captured context belongs on disk. Limit the set to 1,000 entries. Validate types, version, count, individual lengths and characters; reject an oversized or unknown-version envelope, skip invalid members in an otherwise bounded envelope, deduplicate valid members, and report a generic recoverable settings message. Do not overwrite an unreadable value merely by opening Settings. A subsequent explicit edit may replace it with the sanitized v1 list. Inject an isolated defaults suite for tests and remove that suite afterward. + +Publish validated immutable snapshots through the existing preference-update path; never read `UserDefaults` from the event tap or detection hot path. Serialize production edits on the main thread. On a genuine set change: + +1. Invalidate pending correction work immediately through `CaptureStateStore.invalidateCorrectionConfiguration()`, which increments the existing `correctionEpoch` under its lock. Reuse the same method as the dynamic-mapping plan; do not introduce a second staleness counter. +2. Queue the new snapshot and returned epoch on `inputQueue`, reset the run/buffer, and enqueue detector policy/reset changes on `detectionQueue` before scheduling subsequent requests. Each request uses the policy associated with its epoch rather than reading a newer policy mid-flight. +3. Retain the existing eligibility checks before and during event emission. Old queued requests cannot apply after an edit; if an edit interrupts an active replacement, use the current rollback behavior. A correction completed before invalidation is not retroactively undone. + +Do not rely on `updateCorrectionEnabled` for this invalidation: it does not increment the epoch when enabled state is unchanged. Adding/removing exceptions must work while SwitchFix remains enabled. This plan adds the shared invalidation primitive with focused tests; plan 01 calls the same method for mapping publication. `InputEngine` keeps its own copy of the epoch for new requests, so the primitive's caller must also push the returned epoch to the engine, as `updatePreferences` does today. + +## Explicit learning after revert + +Add enough provenance to an applied plan to distinguish a captured single automatic token from a sweep, selection, layout-only action, and manual correction. Do not infer it by splitting `originalText`: the detector can return a multiword span and selected text can resemble a single word. + +Replace or supplement the internal Boolean undo result with a typed `UndoOutcome` exposing an optional `RevertedTokenReceipt` only after the entire inverse text emission succeeds. The receipt contains only the trigger and the matching key derived from the plan's original token by the shared helper above; if that key fails validation, issue no receipt. Keep the external fallback behavior: a failed/stale undo can still request manual correction, but never issues a learning receipt. Layout-only undo, interrupted/rolled-back inverse emission, native application Undo, multiword sweep, selected text, and manual-correction undo produce no candidate. + +Use the original typed spelling from the stored plan, never `correctedText`, the current buffer, selected text, or an Accessibility read. Publish the receipt from the engine to `AppDelegate` on the main thread. Hold at most one in-memory candidate, expiring ten minutes after successful revert; check expiry when refreshing the menu and executing the action. Clear it on the next successful correction, replacement by another successful revert, permissions loss, focus resolved as secure, or successful save. Do not clear it on unknown focus: every mouse-down, including the click that opens the SwitchFix menu, is captured as `.focusMayChange` and sets focus to unknown (`KeyboardMonitor.swift:323`, `CapturedInput.swift:371`), so clearing there would make the menu action unreachable. Keeping an already captured receipt while opening the menu does not authorize another text read or edit. + +The menu item uses the fixed label **Never Auto-correct Last Reverted Word**, with no raw word in menu titles, tooltips, or logs. Enable only for a current valid candidate not already protected and while storage capacity permits. Clicking it only adds that original token to preferences and consumes the candidate; it does not touch the frontmost application. The user can review/remove the word in Settings. An already-saved duplicate is safe and consumes the candidate. Show a short inline/settings status for capacity or persistence problems; do not silently discard an explicit Add. + +## UI tasks + +Add a **Personal Exceptions** section to `SettingsView.swift`, between layouts and excluded apps. Include concise copy: “These words stay as typed during automatic correction. Hotkey and layout-switch corrections still work.” Use a selectable list, one text field, Add, and Remove Selected. Display an empty state, count/limit, validation messages, and keyboard-accessible controls with labels for icon-only buttons. Add supports Return; Remove requires a selection. No bulk import/export or additional window is needed. + +`SettingsViewModel` refreshes the list on `.preferencesDidChange`, using equality checks to avoid notification loops. `StatusBarController` exposes a candidate-update/action callback; `AppDelegate` owns engine-to-UI wiring and calls the preferences operation. UI code must not reach into the corrector's undo lock. A fixed confirmation/status text may indicate the save succeeded; never write user tokens to `SwitchFixLog` or analytics. + +## Verification and acceptance + +Extend `TestRunner` with table-driven normalization/validation cases: case variants, NFC/NFD, all three apostrophes, diacritics, backticks, hyphens, digits, duplicates, no prefix/substring match, multiword/URL/control/emoji rejection, character/byte ceilings, and 1,000-entry capacity. Matching-key cases: protected `foo` matches `foo,`, `foo.` and `"foo` and leaves the punctuation on screen; protected `kj` suppresses `kj,` (the documented trade-off); a U+02BC word reaches detection whole. Persistence cases cover empty startup, restart, removal, malformed types, unknown version, oversized envelope, invalid entries, deterministic serialization, and isolated defaults cleanup. + +Extend detector tests with protected current word, layout-only prediction suppression, confidence reset, protected earlier word, barrier beside already-converted journal entries, protected-token-at-run-start/middle/end, newline boundaries, and empty-policy parity. Assert exact resulting replacement spans, not just “no crash.” Use a known existing correction fixture and protect its original spelling; do not rely on dictionary additions to invent a false positive. + +Extend `InputPipelineTestRunner` using existing injected detection/emission and semaphore patterns: a blocked automatic request cannot emit after Add or Remove; configuration publication precedes new input; selected text and hotkey/layout-switch bypass work; focus/access/security cancellation still wins; empty policies preserve baseline behavior. Add deterministic success/failure seams around undo outcome generation so these tests require no system permissions or synthesized keystrokes. Verify only a completed single automatic revert offers the original token; all ineligible cases above do not. A plan rebased by `rebaseUndoContext` keeps its trigger and provenance. A `.focusMayChange` input after the revert keeps the candidate; a secure focus resolution clears it. Test candidate expiry with an injected clock rather than sleeping ten minutes. + +Run from the repository root on macOS: + +```sh +swift build +swift run -c release TestRunner +swift run -c release InputPipelineTestRunner +``` + +Manually verify Settings Add/Remove/relaunch, duplicate and capacity messages, menu learning after a real revert, protected typing, explicit override, and a sweep beside a protected word in TextEdit. Verify VoiceOver labels/tab navigation. Do not claim hardware integration is covered by injected tests. The shared benchmark adds paired empty-policy/protected-policy cases; a policy-created abstention on a positive example is a missed correction, not a false-positive reduction without cost. + +Acceptance: all existing runners pass; an empty list preserves existing results; a protected token is never included in an automatic replacement span or associated automatic switch; explicit correction still works; stale work is invalidated; persistence survives restart; no undo path silently learns; no captured word is logged. Document any unavailable manual checks. + +## Commit slices and dependencies + +1. **Preserve intentional spellings consistently:** Core policy/store, bounds, persistence tests. No UI/pipeline behavior yet. +2. **Respect exceptions without rewriting protected context:** trigger provenance, detector current-token/sweep barriers, `invalidateCorrectionConfiguration()`, pipeline regressions. Plan 01 builds on these `CapturedInput.swift`, `InputEngine.swift`, and `TextCorrector.swift` changes; keep them additive. +3. **Let users manage protected words:** PreferencesManager/AppDelegate wiring, Settings list and operations; verify restart and accessibility. +4. **Learn only from an explicit successful revert:** typed undo outcome, candidate lifecycle, menu action, focused tests and manual verification. +5. **Measure correction tradeoffs:** add the exception fixtures to plan 03's versioned corpus once its schema exists. The feature itself does not depend on the benchmark runner or dynamic source tables. + +Remaining risks are intentional global scope (one spelling is protected everywhere), case/apostrophe equivalence, and reduced automatic recall for protected words. Keep those visible in UI copy and benchmark results. Revisit source-specific rules only if real user evidence shows global behavior is insufficient. diff --git a/docs/plans/03-comparative-false-positive-benchmark.md b/docs/plans/03-comparative-false-positive-benchmark.md new file mode 100644 index 0000000..ea15983 --- /dev/null +++ b/docs/plans/03-comparative-false-positive-benchmark.md @@ -0,0 +1,133 @@ +# Plan 03 — Reproducible false-positive benchmark + +Status: implementation plan; no benchmark has been implemented or executed by this document. Revised 2026-10-19 after review against the baseline. + +## Outcome and boundaries + +Build a small, local benchmark that answers two separate questions: how often does SwitchFix change intentionally correct input, and how often does it successfully repair an actual layout mistake? Add an optional Klavko comparison using exactly the same labeled trials and explicitly documented configuration. Save machine-readable observations and a readable report; never infer a competitor score when its adapter is unavailable. + +The first deliverable must run against SwitchFix alone, without Accessibility permissions, XCTest, a network connection, or a Klavko installation. Use Swift/Foundation and existing targets. Do not introduce a benchmarking framework or change detection thresholds to make a report look better. + +Establish the current SwitchFix baseline **before** implementing personal exceptions or dynamic mappings. Freeze those results, implement Plan 02 and then Plan 01 Phase A, and rerun the same corpus and configuration after each. Additional fixtures introduced during implementation belong to development data and a new corpus version; do not quietly alter the original baseline. + +## Existing integration points + +- `Package.swift` already defines `TestRunner` and `InputPipelineTestRunner` executable targets, with no XCTest dependency. Preserve them and add a similarly small `LayoutBenchmark` executable target depending on `Core`, `Dictionary`, and `Utils`. +- `Sources/Core/LayoutDetector.swift` exposes `addCharacter`, `flushBuffer`, `deleteLastCharacter`, `invalidateSync`, `syncRun`, and `reset`. Its state includes recent context, pending confirmations, allowed layouts, preferred Cyrillic layout, and physical source IDs. A benchmark that checks isolated words only will miss its contextual behavior. +- `Sources/Core/InputStateMachine.swift` is a public, synchronous value type that turns `CapturedInput` into append, flush, invalidate, manual correction, and revert commands. `InputEngine` already accepts `correctionEmission`, exposes `drain`, and carries sequence/context generations. `drain` only flushes the input queue (`InputEngine.swift:268`); detection and correction run on separate queues, so it cannot prove that no correction will follow. An injected `exactDetection` stub is appropriate for harness tests, never for measuring detector quality. +- `LayoutDetector.preferredSourceIDProvider` defaults to `InputSourceManager.shared` (`LayoutDetector.swift:137`), whose initializer enumerates the operator's TIS sources and reads their saved `SwitchFix_preferredInputSources`. The adapter must override it; the default `TextCorrector()` also touches `InputSourceManager.shared`. +- `DictionaryLoader` provides `prewarm`, `enableTextFallbackForTesting`, and `resetForTesting`. Explicit text fallback is suitable for a source-tree baseline, but must be recorded as a dictionary mode, not presented as the installed app's compiled-dictionary measurement. +- Inspected Klavko checkout: `https://github.com/stepsergius/Klavko.git` at `6510e64e5d54b15fbc578bce4830255937b628b4`. The inspected copy lived in `/private/tmp`, which macOS purges; clone it into an ignored path such as `Benchmarks/results/external/klavko` instead. `KlavkoCore.WordAnalyzer` exposes `shouldConvert` and `shouldConvertPrefix`, using `SpellChecking` and `MacSpellChecker`. The app's internal `SwitchEngine` adds context, early conversions, alternative selection, undo, and insertion behavior. Calling `WordAnalyzer` alone does not benchmark that engine. + +## Three measurement layers + +| Layer | What is measured | What it cannot establish | +| --- | --- | --- | +| Decision | Finished-word decisions with supplied context and source/alternative mapping | Whole-engine behavior, prefix corrections, real insertion safety | +| Sequence replay | Physical-key/event sequence, maintained text/layout state, emitted automatic actions and checkpoints | Successful injection into a real application | +| App conformance | Actual application text and active input source after controlled trials | Large-corpus reproducibility without a dedicated desktop environment | + +Publish separate tables. The first layer is a restricted common comparison, not the headline claim that one full application is safer. Sequence observations can be scored only where an adapter implements the actual relevant policy; an approximation must identify itself and remain outside comparative release claims. + +## Corpus and labeling contract + +Create `Benchmarks/layout-corpus/v1/{development,holdout}.jsonl`, `manifest.json`, and `README.md`. Store one independent trial per JSONL row. A trial can contain several words; contextual sweeps require scoring a run rather than treating every boundary as statistically independent. + +Proposed schema, illustrated by a complete positive trial: + +```json +{"schemaVersion":1,"id":"uk-positive-001","groupID":"greeting-001","split":"development","languages":["en","uk"],"category":"layout-error","label":"repair","provenance":{"kind":"synthetic","generator":"manual-v1","seed":0,"review":"reviewed"},"initial":{"sourceID":"com.apple.keylayout.ABC","language":"en","text":"","caretUTF16":0,"preferredCyrillic":"uk"},"events":[{"type":"key","keyCode":5,"modifiers":[],"rendered":"g"},{"type":"key","keyCode":4,"modifiers":[],"rendered":"h"},{"type":"key","keyCode":11,"modifiers":[],"rendered":"b"},{"type":"key","keyCode":2,"modifiers":[],"rendered":"d"},{"type":"key","keyCode":1,"modifiers":[],"rendered":"s"},{"type":"key","keyCode":45,"modifiers":[],"rendered":"n"},{"type":"boundary","keyCode":49,"rendered":" "}],"expected":{"text":"привіт ","language":"uk","sourceID":"com.apple.keylayout.Ukrainian","allowedAutomaticActions":["replaceSuffix","switchSource"],"checkpoints":[{"afterEvent":7,"text":"привіт ","language":"uk"}]}} +``` + +`afterEvent` counts applied events: `7` means the state after the first seven events (here, after the boundary). Event indices in results are zero-based. + +Implementation must validate actual installed source IDs before using this example in a live trial. Deterministic rendering uses frozen layout tables shipped with the benchmark: `Benchmarks/layout-tables/v1/@.json`, one file per source, containing the output of every key code 0..<128 in all eight Shift/Caps/Option states plus rejected strokes. Generate them once with an explicit opt-in `LayoutBenchmark export-layout-tables` command on a real Mac, record macOS version/build and keyboard type in each file, review them against on-screen output, and hash them into the manifest. Never regenerate them silently; a change is a new tables version. Plan 01 consumes the same files as its deterministic fixtures, so the baseline does not wait for plan 01. `rendered` is the initial-source oracle and must equal the table output for that key; validation rejects rows where it does not. Following an automatic switch, physical keys must render through the new source's table rather than continuing to append this original string. Otherwise mid-word switching is measured incorrectly. + +Define these labels explicitly: + +- `repair`: an intentional layout error with one reviewed expected final text/source. A correct source switch alone is insufficient if the text remains wrong. +- `preserve`: intentional input whose final text and source must remain as typed, with `allowedAutomaticActions: []`. Include natural words, names, code, unfamiliar tokens and accidental-looking but intentional text. +- `abstain`: ambiguity or an unsupported/unsafe context requires no automatic action. These are eligible negatives in a separately reported abstention stratum. Unsupported fixtures for an adapter are instead `unavailable`, never successful abstentions. + +Allow multiple expected source IDs only when reviewers explicitly establish equivalence. Do not accept arbitrary output or accept both preservation and conversion for the same trial. Every trial must define expected text, final layout/source, and permitted automatic actions; additionally forbid changes to protected prefixes or ranges. Checkpoints capture temporary damage even when later actions restore the expected final text. Action contracts describe observable effects, not implementation-specific numbers of backspaces. + +Add optional event types for delete, navigation, explicit source change, focus/context change, manual correction and revert. Keep automatic-quality scores separate from manual-action conformance. Store timestamps as deterministic relative ticks; timing stress belongs to a separately configured replay profile. + +Freeze a 200-trial seed corpus as v1 so the baseline does not wait on a large labeling effort: 100 development and 100 holdout trials, each split containing 20 `repair`, 60 `preserve`, and 20 `abstain` trials. Divide each label equally between English-to-Ukrainian and Ukrainian-to-English contexts. Grow toward 1,000 trials (500/500 with 100/300/100 per split) as v2 and later versions; never edit a frozen version. Use disjoint base groups across splits; generated variants do not count as independent evidence. These sizes establish a regression baseline, not a false-positive claim: zero false positives on the 80 development negatives still has a Wilson 95% upper bound of about 4.6%. Start with independently authored English/Ukrainian positives and negatives in both directions. Keep English/Russian and three-language arbitration as named extensions, not an aggregate that changes the primary comparison. Reserve explicit strata for one-to-three-character words, contextual confirmation, mixed-language sentences, apostrophes/case/punctuation, digits, URLs/email, paths/code, acronyms, names, unknown words, intentional gibberish, deletion/navigation, and back-to-back words. Add mapping variants and personal-exception tests once Plans 01 and 02 land. Unsupported layout combinations receive coverage counts and no quality score. + +Labels must derive from intended text and human review, never `WordValidator`, `NSSpellChecker`, or either application's dictionary. Deterministic synthetic generation may render independently authored intended text through verified key maps; do not build the entire corpus from the dictionary being evaluated. Review synthetic positives for ambiguous collisions before assigning `repair`. + +Split by `groupID` before generation: a base sentence, its case/punctuation variants, transliterations, reversed mapping and derived prefixes all remain in one split. Hash the frozen corpus. Keep a holdout inaccessible to threshold tuning during development, and record every formal holdout evaluation. Once its individual failures guide fixes, retire it as a development set and version a fresh holdout before making new generalization claims. Public holdouts are useful regression sets but cease to be blind once inspected. + +Accept deidentified real examples only through explicit opt-in submission and review. No live keystroke telemetry, clipboard collection or automatic export of exception lists. Remove identifying tokens before commit and retain provenance/consent records outside the public corpus when necessary. + +## Adapter and execution contract + +Use a versioned JSONL request/result boundary for external programs. Requests contain fixture, profile, dictionary mode and mapping snapshot hashes. Results include adapter/version, capabilities, trial ID, status (`ok`, `unsupported`, `error`), final text/source, ordered actions with event indices, checkpoints, and diagnostic reason codes. Never include private user data in diagnostics. Set protocol version to 1; reject unknown versions, duplicate/missing trial IDs, unknown source IDs, out-of-range event indices, invalid caret/range boundaries, and results for unrequested trials. Bound each request/result line to 1 MiB, trial events to 512, and external worker output to 64 MiB per invocation. Use a 30-second setup timeout and 5-second per-trial timeout after warmup; terminate the worker and mark remaining trials unavailable on process failure. Keep timing limits in the recorded profile, not hidden defaults. + +The internal SwitchFix adapter creates fresh detector and source state per trial, prewarms required dictionaries and fails setup if readiness is false. Override `preferredSourceIDProvider` and every other source lookup with values from the trial and the frozen tables; never consult the operator's input sources or defaults. Replay runs synchronously on the real `InputStateMachine` and `LayoutDetector`: feed each event as a `CapturedInput`, handle the resulting commands, apply emitted replacements to a virtual editor, verify the expected suffix before replacing it, apply source switches, and feed the resulting state back to later events. Do not reimplement state-machine rules in the adapter. To avoid duplicating the engine's request assembly (`precedingRun` selection and detector setup, `InputEngine.swift:326–393`), extract it into a small Core function used by both `InputEngine` and the adapter; this refactor must keep both existing runners green. + +Cross-check representative traces against `InputEngine` using its real detector and injected emission. Add a test seam, `drainPipeline(completion:)`, that hops input → detection → input → correction queues in order, so a negative trial can conclude "no correction" without a timeout; `drain` alone is insufficient. Use explicit completion assertions; avoid sleeps. If a trace contains asynchronous interleavings that cannot be expressed deterministically, put it in the app/stress suite rather than pretending an arbitrary sleep establishes correctness. Include Plan 01/02 configuration changes through their shared `invalidateCorrectionConfiguration()`/correction-epoch path once available, and verify stale observations cannot emit replacements. + +### Optional Klavko adapter without vendoring + +The inspected checkout has `NOTICE` for a specific third-party file but no root license establishing general redistribution permission. Do not vendor Klavko code, add a package dependency, ship patched sources, or copy its rules into SwitchFix. Confirm permission before any future redistribution or incorporation into the product. This does not impose an extra approval gate on local comparison using a separate public checkout. Publishing results that name Klavko is a separate owner decision; until it is made, comparative results stay local and out of the repository and release notes. + +Use an external-process protocol wrapper built locally against the separate checkout, or consume observations from human-run conformance trials. The fixture runner, schema, scorer and our baseline remain usable without that executable. Requesting comparison without a working competitor command exits nonzero with `competitor_unavailable`; an ordinary SwitchFix-only run succeeds and reports `comparisonStatus: not_run`. + +Implement `scripts/build-klavko-benchmark-adapter.sh` taking explicit checkout/output paths. Verify the pinned SHA and compile the original `Sources/KlavkoCore/**/*.swift` paths into a temporary `KlavkoCore` module/library using `xcrun swiftc -emit-library -emit-module -module-name KlavkoCore`, the selected SDK from `xcrun --show-sdk-path`, and a compatible macOS deployment target. Compile our own small `Benchmarks/adapters/KlavkoDecisionAdapter.swift` with that module's `-I`/`-L` paths and local runtime library path; leave all generated artifacts outside the checkout or under ignored results. Quote paths and collect sources recursively, including `Translations/`; do not copy or patch external files. Call public `WordAnalyzer` APIs and `MacSpellChecker`, with explicit context and source maps. This avoids the root package's FluidAudio dependency and XCTest targets. Verified 2026-10-18: the 30 `KlavkoCore` sources at the pinned SHA build into `libKlavkoCore.dylib` and `KlavkoCore.swiftmodule` with `xcrun swiftc -emit-library -emit-module -module-name KlavkoCore -sdk "$(xcrun --show-sdk-path)" -target arm64-apple-macos13.0` and no errors. The adapter script must still report actual toolchain failures on other machines. Never substitute invented results or a stub spell checker for a failed competitor build. + +Core-only sequence support must remain declared unsupported for full-app policy until a separate adapter can exercise `SwitchEngine`, or real-app traces establish conformance. Any integration requiring copied or modified competitor sources needs a separate reuse decision. Human trials are an acceptable small initial comparison. Save redacted observation JSON in a separately labeled manual results folder; no automated statistical ranking from a handful of trials. + +## Controlled profiles and environment capture + +1. `boundary-layout-only`: English/Ukrainian only; automatic layout switching on; Klavko `switchWhileTyping` off; spelling, typography, capitalization, spacing, list continuation and replacements off; exceptions empty. Use equivalent supported boundaries. Preserve each detector's own default decision thresholds and disclose them. +2. `native-layout-only`: same scope with Klavko prefix switching enabled and SwitchFix's actual native timing. Feed full key sequences, inspect intermediate checkpoints and report earliest intervention. Never silently disable the competitor's distinguishing behavior in the only published comparison. +3. `personalized`: start from clean profiles, apply the same explicit exception intents and labeled training sequence, then score a disjoint evaluation set. Keep untrained results visible. Plan 02 uses exact original tokens normalized with NFC, lowercase and typographic-apostrophe folding; exceptions form backward-sweep barriers. Explicit hotkey/selection and user-initiated layout-switch correction bypass them. Cover those contracts without forcing Klavko's different prefix-exception behavior into the same semantics. + +An optional full-defaults trial may be useful but must classify spelling/formatting changes separately. Differences in feature scope cannot be charged as layout errors by accident. + +Record SwitchFix and Klavko SHAs, dirty-tree state, adapter revision, corpus/config hashes, Swift version, build flags, architecture, macOS version/build, keyboard hardware type, input-source IDs/order and mapping hashes, dictionary asset/override hashes and binary/text mode, threshold settings, exception/replacement counts, enabled app/system text services, locale and warm/cold state. Klavko's system dictionary depends on macOS and learned spelling: use a dedicated clean account and record this limitation; do not claim byte-identical system dictionary reproducibility or export private learned words. + +## Scoring and interpretation + +Score eligible trials, not keys or each repeated correction. Denominators come from validated label/capability eligibility before execution; errors are reported as failures/coverage loss and cannot disappear from reports. + +- `N_negative`: eligible `preserve` plus `abstain` trials. Report both strata and their total. +- Text-damage FP rate: negative trials with any unauthorized text mutation divided by `N_negative`, including damage later undone. +- Layout-only FP rate: negative trials with unauthorized source change and no text damage divided by `N_negative`. Also report any unauthorized source change and the union intervention FP rate; distinguish a proposal at decision layer from an applied action at replay/app layer. +- `N_positive`: eligible `repair` trials. TP requires exact expected final text and allowed final source by the checkpoint/deadline, without forbidden intermediate damage. Recall is `TP / N_positive`; all other positive outcomes are FN, subdivided into no action, wrong repair, too late and unsafe mutation. +- Precision is exact successful repairs divided by all trials receiving automatic repair attempts. Its non-TP denominator includes negative interventions and incorrect/partial positive repairs. Label this strict trial-level definition. Return `null` when no repair was attempted. Every rate with a zero denominator is `null`, never zero. A run with missing/error observations must show an incomplete status and cannot pass a quality gate by treating those trials as correct preservation. + +Always show counts, coverage, timeout/error counts, micro-average, each direction/category and worst adequately sampled language/direction. Report final-state success separately from damage-free success. A permanently disabled detector has zero FP, zero recall and undefined precision; include that control to prove it cannot win the benchmark. + +Use Wilson 95% intervals for proportions and mark small strata inconclusive. With zero FP, show the nonzero upper bound; approximately 3,840 independent negatives with zero events are needed for a two-sided Wilson upper bound near 0.1%. Synthetic variants within one group are correlated: publish group counts and avoid confidence claims treating all variants as independent; use group-level summaries or a deterministic seeded cluster bootstrap for paired score differences. Do not claim superiority from overlapping, low-count or environment-mismatched observations. + +## Implementation sequence and files + +1. **Freeze baseline inputs.** Add corpus/schema/provenance documentation, the predeclared split manifest, the 200-trial seed corpus and the frozen `Benchmarks/layout-tables/v1` files with their export command. Commit before feature work; record current source SHA in results metadata. +2. **Build own runner.** Add `Sources/LayoutBenchmark/{main,Corpus,SwitchFixAdapter,Scoring}.swift` and the executable target. Extract the engine's request assembly into Core and add the `drainPipeline` seam described above. Keep the adapter protocol as plain Codable request/results; avoid a plugin framework. Support `validate`, `run`, `compare`, and `summarize` subcommands and an explicit `--layer decision|replay|app` selector with deterministic JSON output and concise Markdown reports. +3. **Add runnable checks.** Add `Sources/LayoutBenchmark/SelfChecks.swift`, invoked by `swift run -c release LayoutBenchmark self-check`. Cover invalid rows/duplicate IDs/leaking groups, scoring tables, UTF-16 caret versus grapheme handling, delete/reset, suffix mismatch, source switch rendering and transient damage. Fake result adapters test scoring only. Include controls that always preserve, always replace, repair the wrong text and return errors; only the known-good control should satisfy both recall and FP checks. +4. **Record current SwitchFix baseline.** Run development plus the declared formal baseline set with readiness checks; save aggregate artifacts in `Benchmarks/baselines/` and raw local output under gitignored `Benchmarks/results/` (add both `Benchmarks/results/` and the external checkout path to `.gitignore`). Record existing runner results with the baseline: at `4b928be`, `TestRunner` passed 313/313; `InputPipelineTestRunner` passed 962/963 in a session without Accessibility, failing only `TextCorrector must apply layout-only plan without deleting text` (`InputPipelineTestRunner/main.swift:584`), which calls the real `TextCorrector.apply`. That failure is environment-dependent, not a regression; release CI on hosted runners passes it. Resolve harness defects before tuning features. +5. **Enable comparative adapter conditionally.** Implement external command invocation with timeout, protocol/version validation and output-size limits. Pin checkout/environment and build the separate core wrapper. Run a small paired sample before a full comparison; report unsupported full-engine trials separately. +6. **Verify app conformance.** On an isolated desktop, run one app at a time in TextEdit and a plain browser input/textarea, then another commonly used native editor. Reset text, source and app state between trials. Observe actual text and input source, prefix timing, undo, focus changes and secure-field abstention. Disable system autocorrection consistently and exclude unsupported secure-field observations explicitly. Keep permission-dependent checks manual/opt-in. +7. **Rerun after Plan 02 and after Plan 01 Phase A.** Compare identical paired trials against the frozen baseline, report new capability coverage and personalized results separately, and inspect every new damage case before release. Report mapping rejections by reason code (`ambiguous_reverse`, `caps_punctuation`, `option_output`, and the rest); these counts decide whether Plan 01 Phase B starts. + +Planned commands after implementation: + +```sh +swift run -c release LayoutBenchmark self-check +swift run -c release LayoutBenchmark validate --corpus Benchmarks/layout-corpus/v1 +swift run -c release LayoutBenchmark run --adapter switchfix --split development --profile boundary-layout-only --output Benchmarks/results/current +swift run -c release LayoutBenchmark compare --competitor-command /absolute/path/to/klavko-decision-adapter --layer decision --profile boundary-layout-only --output Benchmarks/results/paired +swift run -c release TestRunner +swift run -c release InputPipelineTestRunner +``` + +## CI and acceptance criteria + +Add `.github/workflows/layout-benchmark.yml` on a pinned macOS image with toolchain/environment artifacts. Pull requests run self-checks, schema validation and a bounded development smoke corpus with existing runners. Cache builds only; do not cache private spelling state. Keep the smoke budget below five minutes after compilation; full synthetic corpus and external comparisons are manual/scheduled jobs on an appropriate desktop runner, never a flaky mandatory hosted-UI check. Competitor absence is an explicit skip in optional CI and a failure when a requested comparison requires it. + +Initial gates are provisional engineering checks, not product-quality promises: no new known text-damage regressions, no silent coverage loss, no unauthorized changes in safety fixtures, and, while the seed corpus has only 20 `repair` trials per split, no newly failing `repair` trial without a reviewed, recorded explanation. Switch to a percentage recall gate (no drop greater than two percentage points) once a corpus version has at least 100 `repair` trials per split. Inspect each direction and worst sufficiently sampled language, so a good English aggregate cannot mask Ukrainian regressions. Do not promote percentage gates from small strata into confidence claims. Recalibrate thresholds only through a documented corpus/version decision after baseline sizes and uncertainty are known. + +Done means the own-code baseline is reproducible from a fresh checkout; every result is traceable to fixtures/config/source; malformed input and missing dictionaries fail visibly; scoring controls detect disabled/wrong detectors; paired comparisons use identical eligible trials with honest capability coverage; and any unresolved Klavko build or engine-access limitation is plainly reported. Full comparative completion additionally requires a real functioning competitor adapter or recorded app trials, rather than an empty comparison table. diff --git a/docs/plans/EXECUTION.md b/docs/plans/EXECUTION.md new file mode 100644 index 0000000..a82c261 --- /dev/null +++ b/docs/plans/EXECUTION.md @@ -0,0 +1,111 @@ +# Executing the layout-reliability plans + +You are implementing the plans in `docs/plans/` for SwitchFix, a Swift/SwiftPM macOS app. Work through the items below in order, one at a time, until every item is DONE, NEEDS-HUMAN or BLOCKED. Another agent will verify your work afterwards against the plans, the code and your records, so record evidence, not claims. + +## Read first, every iteration + +- `docs/plans/README.md` (order and shared contracts) and the plan section for the current item. The plans are the spec. Their `file:line` references are from `4b928be` and drift as earlier items land; locate code by symbol. +- `docs/plans/PROGRESS.md`, the only record of what is done. Item 0 creates it. + +## Loop protocol + +1. Read `PROGRESS.md` and take the first item with status TODO. If none remain, go to [Finish](#finish). +2. Re-read that item's plan section and the code it touches, then implement exactly that item. No drive-by refactors, no threshold tuning, no work belonging to later items. +3. Run the [checks](#checks). Save full command output to `Benchmarks/results/logs/-.log` (gitignored). +4. Update the item in `PROGRESS.md` (status, check results, deviations, needs-human steps) and commit code and progress together (see [Git](#git)). +5. When running under `/loop`, end the iteration here. Otherwise continue with the next item. + +## Items + +Copy this table into `PROGRESS.md` in item 0, with a Status column (TODO / DONE / NEEDS-HUMAN / BLOCKED) and a Notes column. + +| ID | Item | Spec | +| --- | --- | --- | +| 0 | Branch, commit the plans, create `PROGRESS.md`, add `Benchmarks/results/` to `.gitignore` | [Git](#git) | +| 03.1 | Benchmark inputs: schema/provenance docs, manifest, `LayoutBenchmark` target skeleton with `export-layout-tables`, frozen `Benchmarks/layout-tables/v1`, 200-trial seed corpus | Plan 03 step 1 | +| 03.2 | Runner: corpus loading, SwitchFix adapter replaying on `InputStateMachine` + `LayoutDetector`, scoring; Core extraction of the engine's request assembly; `drainPipeline` seam | Plan 03 step 2 | +| 03.3 | `self-check` subcommand with controls; `.github/workflows/layout-benchmark.yml` | Plan 03 step 3, CI section | +| 03.4 | Record and freeze the SwitchFix baseline | Plan 03 step 4 | +| 02.1 | Core policy/store, bounds, persistence tests | Plan 02 slice 1 | +| 02.2 | Trigger provenance, detector barriers, `invalidateCorrectionConfiguration()`, pipeline regressions | Plan 02 slice 2 | +| 02.3 | Preferences/AppDelegate wiring, Settings list | Plan 02 slice 3 | +| R1–R4 | Verification remediation (done out of band; see `PROGRESS.md`) | Verification 2026-10-19 | +| 02.4 | Typed undo outcome, candidate lifecycle, menu action | Plan 02 slice 4 | +| 02.5 | Exception fixtures as corpus v3 (built on v2); rerun the benchmark against baseline v2 | Plan 02 slice 5, plan 03 step 7 | +| 01A.1 | Native builder, pure mapper, canonical reverse rule, fixture checks, mask fix at the two allowed sites | Plan 01 Phase A commit 1 | +| 01A.2 | Synchronous snapshot publication, keyboard-type capture, readiness, invalidation | Plan 01 Phase A commit 2 | +| 01A.3 | Convert through exact sources in every path; remove Ukrainian variant fallbacks | Plan 01 Phase A commit 3 | +| 01A.4 | Source-selection and undo bindings | Plan 01 Phase A commit 4 | +| 01A.5 | Remove static maps, user guidance, benchmark rerun against baseline v2 | Plan 01 Phase A commit 5, plan 03 step 7 | +| 01B | Phase B gate decision | Plan 01 Phase B | +| 03.5 | Klavko decision-layer adapter and local comparison | Plan 03 step 5 | +| 03.6 | App conformance runs | Plan 03 step 6 | +| F | Final report | [Finish](#finish) | + +## Checks + +After every item that changes code: + +```sh +swift build +swift run -c release TestRunner +swift run -c release InputPipelineTestRunner +swift run -c release LayoutBenchmark self-check +swift run -c release LayoutBenchmark validate --corpus Benchmarks/layout-corpus/v2 --strict +swift run -c release LayoutBenchmark run --split development --corpus Benchmarks/layout-corpus/v2 \ + --baseline Benchmarks/baselines/v2/corpus-v2/baseline.json +``` + +Add the item-specific commands its plan names. The `run --baseline` gate exits 2 when any trial loses its pass or coverage or the run is incomplete; improvements pass. A trial that loses its pass because of an intended change is a deviation with a per-trial explanation, never a reason to re-record the baseline. + +Known counts at `c22be93` (after verification remediation): `TestRunner` 455/455. `InputPipelineTestRunner` 1012/1013, where the only allowed failure is `TextCorrector must apply layout-only plan without deleting text` when the session lacks Accessibility. `LayoutBenchmark self-check` 106/106. Any other failure blocks the item: fix it inside the item or mark the item BLOCKED. Record pass/fail counts per item. Counts only grow; removing or weakening an existing assertion is a deviation that needs a stated reason. + +Explain a benchmark outcome only with an experiment that changes the suspected cause and reruns (for example a scratch copy with one setting changed), never by inspection alone. The v1 baseline's four misses were attributed to static mapping by inspection; the real cause was a harness misconfiguration. + +Never record something as verified that you did not run. Manual checks the plans require (hardware matrix, VoiceOver, Settings relaunch, TextEdit/browser behavior, app conformance) go into the Needs-human section of `PROGRESS.md` with exact steps, unless you actually performed them, in which case record how. + +## Git + +- Item 0: confirm `HEAD` is `4b928be`, or a descendant with no changes under `Sources/`. Create branch `feat/layout-reliability` and commit `docs/plans/` (including this file) as the first commit. +- One commit per item. Every commit builds and passes the checks. +- Follow the repository's message style (`feat: …`, `fix: …`, `test: …`, `docs: …`, `chore: …`). The body says what changed and why. End the message with the trailer `Plan-Item: ` so each item is found with `git log --grep "Plan-Item: "`. Never add `Co-Authored-By` or any AI attribution line. +- Never push, force-push, amend or rebase earlier items' commits, or merge into `master`. +- Raw benchmark output and the Klavko checkout stay under gitignored `Benchmarks/results/`. Comparative Klavko results are never committed. +- Frozen: `Benchmarks/layout-tables/v1`, `Benchmarks/layout-corpus/v1` and `v2`, `Benchmarks/baselines/v1` (superseded, kept for reference) and `Benchmarks/baselines/v2`. Later fixtures go into a new corpus version (v3 onward, built on v2). The reference baseline is `Benchmarks/baselines/v2/corpus-v2/baseline.json`. +- A new baseline is recorded only with `LayoutBenchmark record-baseline` from a clean checkout of a committed revision (`git worktree add --detach `), passing `--runner-log` for each runner's log, and only when a recorded harness defect invalidates the current one. + +## Deviations + +The plans were reviewed against the code, but implementation will find more mismatches. When a plan instruction is wrong or impossible: + +- If the correction stays inside the item and changes no shared contract (README "Shared implementation contracts", types another plan consumes, user-visible behavior, corpus labels), make the smallest correct choice and add a Deviations entry to `PROGRESS.md`: plan reference, what the code actually does (file and symbol), what you did, and why. +- Otherwise mark the item BLOCKED with the exact question and stop. + +## Stop conditions + +Mark the item BLOCKED or NEEDS-HUMAN, write the reason in `PROGRESS.md`, commit, and end the loop when: + +- A check fails and you cannot fix it inside the item. +- The work needs a change to a shared contract, the execution order, or user-visible behavior beyond the plan. +- 01B: the reason-code counts say Phase B should start. Write a detailed Phase B section into plan 01 and stop for approval; do not implement it. If the counts say no, record them and mark 01B DONE (not started). +- 03.5 cannot clone the pinned Klavko commit or build it. Never publish or commit results that name Klavko. +- Anything needs granted system permissions or physical hardware you do not have. 03.6 is NEEDS-HUMAN unless you can genuinely run it. + +## Data rules + +- Corpus trials are synthetic: authored from intended text and rendered through the frozen layout tables, never derived from `WordValidator`, `NSSpellChecker` or either application's dictionary. Set provenance `"review": "pending-human"`; you may not mark trials `reviewed`. Freeze v1 anyway so later runs share a fixed baseline; human review results become a new corpus version. +- Never log, print or commit real typed text. No telemetry. +- Do not tune detector thresholds to improve benchmark numbers. + +## Finish + +Write `docs/plans/EXECUTION-REPORT.md` and commit it with `Plan-Item: F`. Include: + +- each item: status, commit SHAs, checks run with pass/fail counts, deviations; +- benchmark tables for baseline v2, after plan 02 and after plan 01 Phase A: trial counts, coverage, text-damage and layout-only FP rates with Wilson intervals, recall, precision, and mapping rejections by reason code, with artifact paths; +- every trial whose outcome changed between runs, with an explanation; +- the 01B decision and the numbers behind it; +- the Needs-human list with exact steps; +- open questions. + +Then stop and report that the branch is ready for verification. diff --git a/docs/plans/PROGRESS.md b/docs/plans/PROGRESS.md new file mode 100644 index 0000000..a66d816 --- /dev/null +++ b/docs/plans/PROGRESS.md @@ -0,0 +1,185 @@ +# Layout Reliability Implementation Progress + +Tracked execution of `docs/plans/EXECUTION.md`. + +## Items + +| ID | Item | Spec | Status | Notes | +| --- | --- | --- | --- | --- | +| 0 | Branch, commit the plans, create `PROGRESS.md`, add `Benchmarks/results/` to `.gitignore` | [Git](EXECUTION.md#git) | DONE | Created branch `feat/layout-reliability` from `4b928be`, added `Benchmarks/results/` to `.gitignore`, created `PROGRESS.md`. Baseline checks match known baseline. | +| 03.1 | Benchmark inputs: schema/provenance docs, manifest, `LayoutBenchmark` target skeleton with `export-layout-tables`, frozen `Benchmarks/layout-tables/v1`, 200-trial seed corpus | Plan 03 step 1 | DONE | Remediated in R2/R3. Added `LayoutBenchmark` target with `export-layout-tables` and `validate`, frozen 22 layout tables in `Benchmarks/layout-tables/v1`, generated 200-trial seed corpus in `Benchmarks/layout-corpus/v1` (100 dev, 100 holdout), manifest with SHA256 hashes, and schema documentation. All 200 trials validated against frozen tables. | +| 03.2 | Runner: corpus loading, SwitchFix adapter replaying on `InputStateMachine` + `LayoutDetector`, scoring; Core extraction of the engine's request assembly; `drainPipeline` seam | Plan 03 step 2 | DONE | Remediated in R2. Extracted `DetectionRequestAssembly` into Core (`DetectionConfiguration`, `assembleDetectionRequest`, `evaluateDetection`), added `drainPipeline(completion:)` multi-queue seam to `InputEngine`. Implemented `SwitchFixAdapter` with synchronous replay on `InputStateMachine` and `LayoutDetector` with virtual editor. Implemented `Scorer` with Wilson 95% intervals and stratified breakdowns. Added `run` and `summarize` subcommands to `LayoutBenchmark`. | +| 03.3 | `self-check` subcommand with controls; `.github/workflows/layout-benchmark.yml` | Plan 03 step 3, CI section | DONE | Remediated in R2. Implemented `SelfChecks` (36 assertions covering Wilson intervals, null precision on zero attempts, fake control adapters [preserve, replace, wrong, good], validation error detection, UTF-16 vs grapheme, delete/reset handling, suffix mismatch protection, source switch rendering, and transient damage checkpoints). Added `.github/workflows/layout-benchmark.yml` on `macos-15`. | +| 03.4 | Record and freeze the SwitchFix baseline | Plan 03 step 4 | DONE | Superseded by baseline v2 in R3 (harness misconfiguration). Recorded and froze the SwitchFix baseline v1 across all 200 trials (development, holdout, combined) in `Benchmarks/baselines/v1/baseline.{json,md}` with full environment metadata. Baseline achieved 90.0% recall (36/40), 0.00% text-damage FP (0/160, 95% Wilson CI [0.0%, 2.3%]), 0.00% layout-only FP (0/160), and 100% preservation (160/160 TN). All 4 false negatives isolated to static Ukrainian variant fallbacks. | +| 02.1 | Core policy/store, bounds, persistence tests | Plan 02 slice 1 | DONE | Remediated in R1. Implemented `PersonalExceptionsPolicy` (NFC, lowercasing, apostrophe normalization [ASCII, U+2019, U+02BC], letter requirement, character/byte bounds [1-64 chars, <=256 bytes], internal punctuation checks, matching key derivation) and `PersonalExceptionsStore` (UserDefaults storage under `SwitchFix_personalExceptions_v1` with `schemaVersion: 1`, 1,000 entry limit, envelope validation). Added 79 unit tests in `TestRunner` (392 passed). | +| 02.2 | Trigger provenance, detector barriers, `invalidateCorrectionConfiguration()`, pipeline regressions | Plan 02 slice 2 | DONE | Remediated in R1. Introduced `CorrectionTrigger` (.automaticBoundary, .hotkey, .layoutSwitch) carried explicitly in `DetectionRequest` and `CorrectionPlan`. Added `invalidateCorrectionConfiguration()` to `CaptureStateStore`. Integrated personal exceptions into `LayoutDetector` (current word suppression, confidence reset, backward sweep barrier on typed/display text, explicit trigger bypass) and `InputEngine` (safeguards on exactDetection). 405 passed in TestRunner, 972 passed in InputPipelineTestRunner. | +| 02.3 | Preferences/AppDelegate wiring, Settings list | Plan 02 slice 3 | DONE | Remediated in R1. Wired `PersonalExceptionsStore` into `PreferencesManager` with `addPersonalException` / `removePersonalException` publishing `.preferencesDidChange`. Updated `AppDelegate` to sync personal exceptions to `InputEngine` on startup and preference updates. Added Personal Exceptions UI section to `SettingsView.swift` with word list, count/limit indicator, Add field, and Remove Selected button. Added pipeline in-flight invalidation test (975 passed in InputPipelineTestRunner). | +| R1 | Verification remediation for 02.1–02.3 | Verification 2026-10-19 | DONE | Removed typed-word logging; exception keys reuse the detector tokenizers; exceptions republished only on a genuine set change; configuration changes no longer drop the next word; selected-text corrections carry their real trigger; load-issue message in Settings; 50 TestRunner and 37 pipeline assertions for the cases 02.1–02.3 left untested. | +| R2 | Verification remediation for the benchmark harness (03.1–03.4) | Verification 2026-10-19 | DONE | Adapter configures sources, variants and input classification as the app does; scorer counts errors, missing and unsupported trials honestly and reads checkpoints and allowed actions; self-checks drive the real code; validator checks manifests, carets, exact tables and label conflicts; generator is versioned and byte-deterministic; baselines record measured metadata; `run --baseline` gates per trial; CI no longer ignores pipeline failures. | +| R3 | Corpus v2: remove leaks and label conflicts, add cross-layout hard negatives | Verification 2026-10-19, plan 03 corpus contract | DONE | `Benchmarks/layout-corpus/v2`, 220 trials (110 per split: 20 repair, 70 preserve, 20 abstain), generated by `5daf653`; byte-deterministic; `validate --strict` passes with 0 warnings. v1 stays frozen. | +| R4 | Baseline v2, CI gate, execution instructions | Verification 2026-10-19, plan 03 step 4 | DONE | Recorded from a clean worktree at `c22be93`: corpus v2 40/40 recall, 8/180 text-damage FP; corpus v1 40/40, 1/160. v1 baseline marked superseded. CI validates v2 strictly and gates the development run against baseline v2. `EXECUTION.md` points later items at corpus v2 and baseline v2. | +| 02.4 | Typed undo outcome, candidate lifecycle, menu action | Plan 02 slice 4 | TODO | | +| 02.5 | Exception fixtures as a new corpus version; rerun the benchmark against the frozen baseline | Plan 02 slice 5, plan 03 step 7 | TODO | | +| 01A.1 | Native builder, pure mapper, canonical reverse rule, fixture checks, mask fix at the two allowed sites | Plan 01 Phase A commit 1 | TODO | | +| 01A.2 | Synchronous snapshot publication, keyboard-type capture, readiness, invalidation | Plan 01 Phase A commit 2 | TODO | | +| 01A.3 | Convert through exact sources in every path; remove Ukrainian variant fallbacks | Plan 01 Phase A commit 3 | TODO | | +| 01A.4 | Source-selection and undo bindings | Plan 01 Phase A commit 4 | TODO | | +| 01A.5 | Remove static maps, user guidance, benchmark rerun against the frozen baseline | Plan 01 Phase A commit 5, plan 03 step 7 | TODO | | +| 01B | Phase B gate decision | Plan 01 Phase B | TODO | | +| 03.5 | Klavko decision-layer adapter and local comparison | Plan 03 step 5 | TODO | | +| 03.6 | App conformance runs | Plan 03 step 6 | TODO | | +| F | Final report | [Finish](EXECUTION.md#finish) | TODO | | + +## Verification Records + +### Item 0 +- **Base Commit**: `4b928be253ce06c325150b122e120b77f174d6f3` (verified no changes under `Sources/`) +- **Checks**: + - `swift build`: Passed (`Benchmarks/results/logs/0-swift-build.log`) + - `swift run -c release TestRunner`: 313 passed, 0 failed (`Benchmarks/results/logs/0-TestRunner.log`) + - `swift run -c release InputPipelineTestRunner`: 962 passed, 1 failed (only expected accessibility failure: `TextCorrector must apply layout-only plan without deleting text`) (`Benchmarks/results/logs/0-InputPipelineTestRunner.log`) + +### Item 03.1 +- **Checks**: + - `swift build`: Passed (`Benchmarks/results/logs/03.1-swift-build.log`) + - `swift run -c release TestRunner`: 313 passed, 0 failed (`Benchmarks/results/logs/03.1-TestRunner.log`) + - `swift run -c release InputPipelineTestRunner`: 962 passed, 1 failed (only expected accessibility failure: `TextCorrector must apply layout-only plan without deleting text`) (`Benchmarks/results/logs/03.1-InputPipelineTestRunner.log`) + - `swift run -c release LayoutBenchmark validate --corpus Benchmarks/layout-corpus/v1`: 200 trials validated across 2 splits (`Benchmarks/results/logs/03.1-LayoutBenchmark-validate.log`) +- **Corpus & Tables**: + - 22 layout tables in `Benchmarks/layout-tables/v1` for 11 layouts on keyboardTypes 91 and 37 + - 200 seed trials (100 development, 100 holdout), 20 repair / 60 preserve / 20 abstain per split, 100% disjoint group IDs + - Integrity verified with SHA256 in `Benchmarks/layout-corpus/v1/manifest.json` + +### Item 03.2 +- **Checks**: + - `swift build`: Passed (`Benchmarks/results/logs/03.2-swift-build.log`) + - `swift run -c release TestRunner`: 313 passed, 0 failed (`Benchmarks/results/logs/03.2-TestRunner.log`) + - `swift run -c release InputPipelineTestRunner`: 966 passed, 1 failed (added drainPipeline & request assembly verification) (`Benchmarks/results/logs/03.2-InputPipelineTestRunner.log`) + - `swift run -c release LayoutBenchmark validate --corpus Benchmarks/layout-corpus/v1`: 200 trials validated across 2 splits (`Benchmarks/results/logs/03.2-LayoutBenchmark-validate.log`) + - `swift run -c release LayoutBenchmark run --adapter switchfix --split development --profile boundary-layout-only --output Benchmarks/results/dev-release`: 100 trials, 18/20 TP (90.0% recall, 90.0% precision), 0/80 text-damage FP (0.00% [0.0%, 4.6%]), 0/80 layout-only FP (0.00% [0.0%, 4.6%]), 80/80 TN (`Benchmarks/results/logs/03.2-LayoutBenchmark-run.log`) + +### Item 03.3 +- **Checks**: + - `swift build`: Passed (`Benchmarks/results/logs/03.3-swift-build.log`) + - `swift run -c release TestRunner`: 313 passed, 0 failed (`Benchmarks/results/logs/03.3-TestRunner.log`) + - `swift run -c release InputPipelineTestRunner`: 966 passed, 1 failed (`Benchmarks/results/logs/03.3-InputPipelineTestRunner.log`) + - `swift run -c release LayoutBenchmark self-check`: 36 passed, 0 failed (`Benchmarks/results/logs/03.3-LayoutBenchmark-self-check.log`) + - `swift run -c release LayoutBenchmark validate --corpus Benchmarks/layout-corpus/v1`: 200 trials validated across 2 splits (`Benchmarks/results/logs/03.3-LayoutBenchmark-validate.log`) + +### Item 03.4 +- **Checks**: + - `swift build`: Passed (`Benchmarks/results/logs/03.4-swift-build.log`) + - `swift run -c release TestRunner`: 313 passed, 0 failed (`Benchmarks/results/logs/03.4-TestRunner.log`) + - `swift run -c release InputPipelineTestRunner`: 966 passed, 1 failed (`Benchmarks/results/logs/03.4-InputPipelineTestRunner.log`) + - `swift run -c release LayoutBenchmark self-check`: 36 passed, 0 failed (`Benchmarks/results/logs/03.4-LayoutBenchmark-self-check.log`) + - `swift run -c release LayoutBenchmark validate --corpus Benchmarks/layout-corpus/v1`: 200 trials validated across 2 splits (`Benchmarks/results/logs/03.4-LayoutBenchmark-validate.log`) + - `swift run -c release LayoutBenchmark record-baseline`: Saved frozen baseline artifacts in `Benchmarks/baselines/v1/baseline.{json,md}` (`Benchmarks/results/logs/03.4-LayoutBenchmark-record-baseline.log`) +- **Frozen Baseline v1 Results**: + - Combined (200 trials): Recall 36/40 (90.0% [76.9%, 96.0%]), Precision 36/40 (90.0% [76.9%, 96.0%]), Text-Damage FP 0/160 (0.00% [0.0%, 2.3%]), Layout-Only FP 0/160 (0.00% [0.0%, 2.3%]), TN 160/160 (100% preservation). + - Development (100 trials): Recall 18/20 (90.0%), Text-Damage FP 0/80 (0.00% [0.0%, 4.6%]), TN 80/80. + - Holdout (100 trials): Recall 18/20 (90.0%), Text-Damage FP 0/80 (0.00% [0.0%, 4.6%]), TN 80/80. + - Direction: UK -> EN recall 100% (20/20), EN -> UK recall 80% (16/20, all 4 misses isolated to static variant fallbacks). + +### Item 02.1 +- **Checks**: + - `swift build`: Passed (`Benchmarks/results/logs/02.1-swift-build.log`) + - `swift run -c release TestRunner`: 392 passed, 0 failed (added 79 assertions for normalization, validation, key derivation, and persistence) (`Benchmarks/results/logs/02.1-TestRunner.log`) + - `swift run -c release InputPipelineTestRunner`: 966 passed, 1 failed (`Benchmarks/results/logs/02.1-InputPipelineTestRunner.log`) + - `swift run -c release LayoutBenchmark self-check`: 36 passed, 0 failed (`Benchmarks/results/logs/02.1-LayoutBenchmark-self-check.log`) + - `swift run -c release LayoutBenchmark validate --corpus Benchmarks/layout-corpus/v1`: 200 trials validated across 2 splits (`Benchmarks/results/logs/02.1-LayoutBenchmark-validate.log`) + +### Item 02.2 +- **Checks**: + - `swift build`: Passed (`Benchmarks/results/logs/02.2-swift-build.log`) + - `swift run -c release TestRunner`: 405 passed, 0 failed (added 13 assertions for detector barriers, layout-only suppression, and trigger bypass) (`Benchmarks/results/logs/02.2-TestRunner.log`) + - `swift run -c release InputPipelineTestRunner`: 972 passed, 1 failed (added 6 assertions for pipeline exception blocking and invalidateCorrectionConfiguration epoch invalidation) (`Benchmarks/results/logs/02.2-InputPipelineTestRunner.log`) + - `swift run -c release LayoutBenchmark self-check`: 36 passed, 0 failed (`Benchmarks/results/logs/02.2-LayoutBenchmark-self-check.log`) + - `swift run -c release LayoutBenchmark validate --corpus Benchmarks/layout-corpus/v1`: 200 trials validated across 2 splits (`Benchmarks/results/logs/02.2-LayoutBenchmark-validate.log`) + +### Item 02.3 +- **Checks**: + - `swift build`: Passed (`Benchmarks/results/logs/02.3-swift-build.log`) + - `swift run -c release TestRunner`: 405 passed, 0 failed (`Benchmarks/results/logs/02.3-TestRunner.log`) + - `swift run -c release InputPipelineTestRunner`: 975 passed, 1 failed (added mid-flight exception update invalidation test) (`Benchmarks/results/logs/02.3-InputPipelineTestRunner.log`) + - `swift run -c release LayoutBenchmark self-check`: 36 passed, 0 failed (`Benchmarks/results/logs/02.3-LayoutBenchmark-self-check.log`) + - `swift run -c release LayoutBenchmark validate --corpus Benchmarks/layout-corpus/v1`: 200 trials validated across 2 splits (`Benchmarks/results/logs/02.3-LayoutBenchmark-validate.log`) + +### Item R1 +Verification of 02.1–02.3 (2026-10-19) found defects that this item fixes: +- **Typed word logged publicly:** `LayoutDetector.checkBuffer` interpolated the protected word into `SwitchFixLog` (public privacy). It now logs the character count only. +- **Second tokenizer:** `PersonalExceptionsPolicy.deriveMatchingKey` reimplemented the detector's tokenizers with a different boundary set. It now calls `LayoutDetector.splitTrailingBoundary` and `LayoutDetector.splitTokenForValidation` (made `static`, no behavior change). +- **Invalidation on every preference change:** `AppDelegate.preferencesDidUpdate` republished exceptions unconditionally, bumping `correctionEpoch`, invalidating the current word and resetting the detector for any hotkey, mode or toggle change. `PersonalExceptionsPublisher` (Core) now forwards only a genuinely changed set; `removePersonalException` posts only when the set changed. +- **Next word dropped after an exception edit:** `InputEngine.updatePersonalExceptions` called `invalidateUntilBoundary()`, which sets the skip flag even with an empty buffer, so the first word typed after every edit never reached detection. New `InputStateMachine.resetForConfigurationChange()` drops the run and skips only a word already in progress. The 02.2 pipeline test passed only because of this bug (the word never reached the exception check); it now exercises the exception path. +- **Selection provenance:** `TextCorrector.performSelectionCorrection` hardcoded `.hotkey`; selected text converted on a layout switch now carries `.layoutSwitch`. The parameter has no default. +- **Letter rule:** `CharacterSet.letters` includes combining marks, so a lone mark counted as a letter. Validation now uses the general category. +- **Unreadable stored list:** no message was shown. `PersonalExceptionsStore.loadPolicyReportingIssue()` reports `unreadable` or `skippedEntries(n)`; Settings shows a generic recoverable message without stored words. Non-string members are skipped instead of discarding the envelope. +- **Untested cases (mutations survived):** deleting the `display` half of the sweep barrier or the confidence reset left all tests green. New tests cover confidence reset, display barrier, protected word at run start/middle/end, newline boundaries, empty-policy parity, `kj,` trade-off, punctuation kept on screen, true layout-only suppression, byte ceiling, 1,000-entry Add capacity, malformed types, deterministic serialization, defaults cleanup, U+02BC reaching detection whole, hotkey/selection/layout-switch bypass in the pipeline, security winning over explicit triggers, removal in flight, publication before new input and change-only publishing. Both mutations now fail the suite. +- **Checks:** + - `swift build`: Passed (`Benchmarks/results/logs/R1-swift-build.log`) + - `swift run -c release TestRunner`: 455 passed, 0 failed (`Benchmarks/results/logs/R1-TestRunner.log`) + - `swift run -c release InputPipelineTestRunner`: 1012 passed, 1 failed (only the allowed Accessibility test; 5 consecutive runs identical) (`Benchmarks/results/logs/R1-InputPipelineTestRunner.log`) + - `swift run -c release LayoutBenchmark self-check`: 36 passed, 0 failed (`Benchmarks/results/logs/R1-LayoutBenchmark-self-check.log`) + - `swift run -c release LayoutBenchmark validate --corpus Benchmarks/layout-corpus/v1`: 200 trials validated (`Benchmarks/results/logs/R1-LayoutBenchmark-validate.log`) + +### Item R2 +Verification of 03.1–03.4 found the harness measured a configuration the app never uses and could hide failures: +- **Wrong Ukrainian variant (baseline-changing).** `SwitchFixAdapter` hardcoded `.standard` variants while every trial uses `com.apple.keylayout.Ukrainian`, whose frozen table (key 1 = и, key 11 = і) the app classifies `.legacy`. All 4 baseline v1 misses came from this. The adapter now builds a per-trial `TrialSourceEnvironment` from the trial and the frozen tables, classifies each source with the app's own `InputSourceDiscoveryEngine`, and mirrors `AppDelegate.updateDetectionConfiguration` (variants, active-source layouts, preferred sources). +- **Input classification differed from the app.** The adapter fed every key as a character; the app's `KeyboardMonitor` treats `/`, `!`, `%`, `(` and other hard punctuation as boundaries. Keys now go through `KeyboardMonitor.capturedKind(forText:)`. This exposes a real false positive the old harness hid: `/usr/local/bin` on ABC becomes `/гик/local/bin`. +- **Engine parity.** The engine's edit planning (`word-too-long`, `text-identical-no-switch`, zero deletes for layout-only results) moved to Core `planCorrectionEdit`, used by both. Previously a layout-only result was recorded as a `replaceSuffix` and scored as text damage. After its own switch the replay now resets the detector and recomputes configuration as the engine and app do. +- **Hidden failures.** A suffix mismatch silently dropped the action; the editor now deletes by count like `TextCorrector` and records `suffix_mismatch`. A missing table after a switch silently fell back to another keyboard type or the original text; it is now an error (`missing_layout_table`) or unsupported (`rejected_stroke_*`). Characters were only appended on state-machine commands; the editor now applies every physical key, as the target app does. +- **Scorer.** Missing results were skipped and `status` ignored; errors and missing observations now stay in the denominators as failures, unsupported trials leave them, and every report states `complete`, coverage, errors and missing. Checkpoints, allowed actions and the protected initial text are now scored (new outcomes `FN_transient_damage`, `FN_wrong_source`, `FN_unsafe_mutation`). Precision counts every trial with an automatic attempt. Observed checkpoints now use corpus language codes (`en`/`uk`); the old `english`/`ukrainian` values could never match. Reports carry per-trial outcomes, reason-code counts and group counts. +- **Self-checks.** The suffix-mismatch, transient-damage, source-switch and UTF-16 checks tested inline copies or string literals. All checks now drive the real scorer, validator, editor, renderer and adapter, add error, missing and unsupported controls and a baseline-gate check, and cross-check six traces against `InputEngine` with its real detector and injected emission (36 → 106 checks). +- **Validator.** Rejected `review: reviewed` (blocking human-reviewed versions), borrowed another keyboard type's table, never verified manifest hashes, and ignored initial text and deletes when computing typed text. Now accepts `pending-human`/`reviewed`, requires the exact table, verifies every manifest hash, checks caret offsets on character boundaries, and reports contradictory labels and cross-split typed input or base text as warnings that fail `--strict`. v1 validates with 9 warnings. +- **Records.** `record-baseline` hardcoded runner counts, the source commit, the keyboard type and its analysis text. It now measures commit and tree state, Swift version, keyboard types, detector thresholds and corpus/manifest identity, parses runner logs passed with `--runner-log`, lists every non-passing trial, and refuses to overwrite a recorded baseline. `export-layout-tables` defaulted to overwriting the frozen tables and now requires a new output directory. `run --baseline` fails (exit 2) when any trial loses its pass or coverage, or the run is incomplete; `run` reports `comparisonStatus: not_run`; `compare` exits nonzero with `competitor_unavailable`. +- **Generator.** Output depended on dictionary key order; it is now byte-deterministic and versioned (`generate-seed-corpus --version v1|v2`). Regenerating v1 reproduces its trials semantically; the frozen v1 files are unchanged. +- **CI.** `InputPipelineTestRunner || true` ignored every pipeline failure; removed. +- **Checks:** + - `swift build`: Passed (`Benchmarks/results/logs/R2-swift-build.log`) + - `swift run -c release TestRunner`: 455 passed, 0 failed (`Benchmarks/results/logs/R2-TestRunner.log`) + - `swift run -c release InputPipelineTestRunner`: 1012 passed, 1 failed (only the allowed Accessibility test) (`Benchmarks/results/logs/R2-InputPipelineTestRunner.log`) + - `swift run -c release LayoutBenchmark self-check`: 106 passed, 0 failed (`Benchmarks/results/logs/R2-LayoutBenchmark-self-check.log`) + - `swift run -c release LayoutBenchmark validate --corpus Benchmarks/layout-corpus/v1`: passed, 200 trials, 9 warnings (`Benchmarks/results/logs/R2-LayoutBenchmark-validate.log`) + +### Item R3 +- **Why:** v1 had four inputs typed identically with contradictory labels (`і`, `чи` across splits; `от`, `ну` within holdout), a reversed-mapping leak (`apple`: development preserve, holdout repair), and no negative whose intended token is a word in the other layout, so 0/160 false positives said little. +- **What:** `LayoutBenchmark generate-seed-corpus --version v2 --commit 5daf6533deb186b5fa85fbffc042fffcf34a807a` (definitions in `SeedCorpusDefinitions.v2`). Five holdout trials replaced under new IDs, keeping their stratum (`holdout-repair-011-v2` `garden`, `holdout-preserve-058-v2` `ще`, `holdout-abstain-093-v2` `аж`, `holdout-abstain-094-v2` `хоч`, `holdout-abstain-098-v2` `є`). Category `cross-layout-collision` adds 10 preserve trials per split, 5 on ABC (`vs yt nfr uhf hbr` / `lev ns gap ufc dsl`) and 5 on Ukrainian (`руку руки рук кум нут` / `дум дути дув пуд дну`); inflections of one lemma and reversed mappings share a group. All trials are synthetic with `"review": "pending-human"`. +- **Checks:** regenerating into a scratch directory reproduces `development.jsonl`, `holdout.jsonl` and `README.md` byte for byte; `swift run -c release LayoutBenchmark validate --corpus Benchmarks/layout-corpus/v2 --strict`: 220 trials, 0 warnings (`Benchmarks/results/logs/R3-LayoutBenchmark-validate-v2-strict.log`). No code changed in this item. + +### Item R4 +- **Recording:** `git worktree add --detach /wt-r4 c22be93` (clean: no untracked or modified sources), then all runners there, then `record-baseline --corpus Benchmarks/layout-corpus/ --baselines Benchmarks/baselines/v2/corpus- --runner-log TestRunner=… --runner-log InputPipelineTestRunner=… --runner-log LayoutBenchmarkSelfCheck=…`. Both `baseline.json` files record source `c22be931515086a5935237d14b72d7c50ce2d281` (clean), macOS 15.7.9 (24G830), arm64, Swift 6.2.4, keyboard type 91, text-fallback dictionaries, empty exceptions. +- **Baseline v2 / corpus v2 (reference):** development 20/20 TP, 4/90 text-damage FP; holdout 20/20, 4/90; combined recall 40/40 (100%, Wilson 95% [91.2%, 100%]), text-damage FP 8/180 (4.44%, [2.3%, 8.5%]), layout-only FP 0/180, precision 40/48 (83.3%, [70.4%, 91.3%]); complete, no errors, no unsupported. +- **Baseline v2 / corpus v1:** recall 40/40, text-damage FP 1/160 (0.62%, [0.1%, 3.5%]), layout-only FP 0/160, precision 40/41. +- **Changed trials versus superseded baseline v1 (same corpus v1):** `dev-repair-006` (`добрий день`), `dev-repair-010` (`як справи`), `holdout-repair-004` (`новий день`) and `holdout-repair-010` (`де ти`) FN → TP: the correct legacy variant. `dev-preserve-041` (`/usr/local/bin`) TN → FP: `/` is now a boundary as in the app, so `usr` → `гик`. Experiment for the first group: the old harness with only the variant changed to `.legacy` gave 40/40 on 2026-10-19. +- **False positives in baseline v2 (product behavior, not tuned):** `dev-preserve-041`, and the cross-layout collisions `yt`→`не`, `nfr`→`так`, `hbr`→`рік`, `lev`→`дум`, `ns`→`ти`, `ufc`→`гас`, `dsl`→`вид`. +- **Gate:** `run --split development --corpus Benchmarks/layout-corpus/v2 --baseline Benchmarks/baselines/v2/corpus-v2/baseline.json` passes (`Benchmarks/results/logs/R4-gate-dev.log`); against a different corpus it exits 2. +- **Checks (in the worktree at `c22be93`):** + - `swift build`: Passed (`Benchmarks/results/logs/R4-swift-build.log`) + - `swift run -c release TestRunner`: 455 passed, 0 failed (`Benchmarks/results/logs/R4-TestRunner.log`) + - `swift run -c release InputPipelineTestRunner`: 1012 passed, 1 failed (only the allowed Accessibility test) (`Benchmarks/results/logs/R4-InputPipelineTestRunner.log`) + - `swift run -c release LayoutBenchmark self-check`: 106 passed, 0 failed (`Benchmarks/results/logs/R4-LayoutBenchmark-self-check.log`) + - `swift run -c release LayoutBenchmark validate --corpus Benchmarks/layout-corpus/v2 --strict`: 220 trials, 0 warnings (`Benchmarks/results/logs/R4-LayoutBenchmark-validate.log`) + +## Deviations + +Recorded retroactively during verification (02.x) and in remediation: + +1. **02.2, request-scoped policy.** Plan 02 says each request uses the policy associated with its epoch. `InputEngine.runDetection` reads the current `detectionConfiguration` at detection time. Kept: every policy change bumps `correctionEpoch` before publishing, so a request that sees a newer policy is already stale and is cancelled in `prepareCorrection`. Plan 01 mapping snapshots must still be captured per request. +2. **02.2, `exactDetection` sweep safeguard.** For the injected test seam, the engine drops a whole result that spans past the current word when a preceding token is protected, instead of trimming it to the eligible suffix. Test seam only; the real detector trims. +3. **02.1, test helper rewrite.** `forEachDictionaryWord` in `TestRunner` changed from `split` to `enumerateLines` (behavior-equivalent drive-by). +4. **02.3, Settings window height** 750 → 850 to fit the new section. +5. **R1, configuration reset.** Plan 02 says "reset the run/buffer". `InputStateMachine.resetForConfigurationChange()` does that, but keeps skipping a word already in progress, because only its tail would remain buffered. +6. **R1, new seams.** `InputEngine` gains an optional `selectionEmission` test seam; `KeyboardMonitor.capturedKind(forText:)` is extracted (no rule change) for the U+02BC regression; `PersonalExceptionsPublisher` holds the change-only publishing rule. +7. **R2, adapter input sources.** Frozen tables do not record TIS languages, so `TableInputSourceProvider` passes none and discovery classifies by source ID and key probes; for the Apple sources in the tables this gives the same layouts and variants as the live app. Focus after SwitchFix's own switch is assumed resolved before the next key. +8. **R2, contradictory labels are warnings.** Plan 03 forbids accepting both preservation and conversion for one input; v1 has four preserve/abstain conflicts (both negatives, so scores are unaffected). They are reported as warnings so frozen v1 still validates; `--strict`, used for every new version and CI, fails on them. + +## Needs-Human Items + +Nothing below has been performed. Each needs a person at a Mac with the stated permissions. + +1. **Layout tables (03.1).** For `com.apple.keylayout.ABC`, `com.apple.keylayout.Ukrainian` and `com.apple.keylayout.Ukrainian-PC`, select the source, type each main-block key unmodified and with Shift in TextEdit, and compare with `Benchmarks/layout-tables/v1/@91.json` (`keyOutputs..unmodified` / `.shift`). Record mismatches; a mismatch means a new tables version, never an edit. +2. **Corpus review (03.1, R3).** Review every trial in `Benchmarks/layout-corpus/v1` and `v2` for label, intended text and ambiguous collisions. Reviewed trials go into a new corpus version with `"review": "reviewed"`. +3. **Settings (02.3, R1).** Run the app, open Settings → Personal Exceptions: add `Swift` (shown as `swift`), add it again ("Already in the list"), add `two words` (inline error), select and Remove, quit and relaunch (list persists). Quit the app, run `defaults write com.switchfix.app SwitchFix_personalExceptions_v1 -string broken`, relaunch, reopen Settings and confirm the generic message and that the value is unchanged until the next Add. +4. **Accessibility (02.3).** With VoiceOver on, tab through the section: the text field, Add and the minus button ("Remove Selected") must be reachable and announced; Return in the field adds the word. +5. **Protected typing (02.2).** In TextEdit with automatic mode: add `ghbdsn`, type `ghbdsn ` on ABC (stays as typed, no layout switch), press the correction hotkey after `ghbdsn` (converts), type `ghbdsn hj,jnf ` (only the second word converts). +6. **Hard negatives (R3).** Confirm the intent labels of the 20 `cross-layout-collision` trials in `Benchmarks/layout-corpus/v2`: is a user who types `nfr`, `dsl` or `ns` on ABC plausibly writing that token? Record decisions in a reviewed corpus version. +7. **Path false positive (R4).** In TextEdit on ABC with automatic mode, type `/usr/local/bin ` and confirm whether SwitchFix rewrites `usr` (benchmark predicts `/гик/local/bin`). This is an owner decision for detector behavior, outside the plans' scope. diff --git a/docs/plans/README.md b/docs/plans/README.md new file mode 100644 index 0000000..bc77c68 --- /dev/null +++ b/docs/plans/README.md @@ -0,0 +1,50 @@ +# SwitchFix: reliable layout correction implementation plans + +Prepared: 2026-10-18. Revised: 2026-10-19 after review against the baseline. Source baseline: `4b928be253ce06c325150b122e120b77f174d6f3`. + +These documents specify implementation work; they do not claim that the features already exist. Each plan contains its own scope, implementation steps, tests, acceptance criteria, and delivery sequence. + +| Plan | Intended result | +| --- | --- | +| [01 — Dynamic layout mapping](01-dynamic-layout-mapping.md) | Convert using the actual source and target keyboard layouts installed in macOS, including supported non-QWERTY layouts, without guessing missing mappings. | +| [02 — Personal exceptions](02-personal-exceptions.md) | Let users protect exact words from automatic corrections, manage the list, and explicitly remember a word after reverting an automatic correction. | +| [03 — Comparative false-positive benchmark](03-comparative-false-positive-benchmark.md) | Measure unwanted edits and layout switches, successful corrections, and abstentions on a reproducible corpus, with clearly separated SwitchFix and Klavko evaluation layers. | + +## Recommended execution order + +1. Implement the benchmark's corpus format, validation, SwitchFix baseline adapter, and reporting first, with the 200-trial seed corpus and the frozen layout tables. Freeze a baseline before changing detection or mapping behavior. A Klavko application run is not a prerequisite for implementing SwitchFix features. +2. Implement personal exceptions. This adds the shared `invalidateCorrectionConfiguration()` primitive and `CorrectionTrigger`. Run both the empty-exceptions baseline and explicit personalization scenarios. +3. Implement dynamic mapping Phase A (real tables, exact sources, text-level mapping). Complete its deterministic and real-keyboard checks, then rerun the baseline corpus. Keep language support at English, Ukrainian, and Russian; retain the existing prohibition on Ukrainian–Russian correction. +4. Start dynamic mapping Phase B (keystroke provenance, Option capture) only if the Phase A benchmark attributes missed or wrong repairs to text-level mapping. +5. Complete the external Klavko comparison and the manual application conformance runs. Keep separate results for core decisions, SwitchFix pipeline simulation, and actual application behavior. Only comparable runs belong in a winner/loser comparison. Publishing results that name Klavko is a separate owner decision. + +The three plans are separate implementation units. The benchmark foundation can ship independently. Exceptions go before mapping because they are small, user-visible, and fix false positives directly; their overlap with mapping is additive (a trigger field on `DetectionRequest`/`CorrectionPlan` and the invalidation primitive), so mapping builds on them instead of both editing the same contracts. + +## Shared implementation contracts + +- **Language and physical source are different identities.** `Layout` continues to describe dictionary language. Actual mapping and layout switching must bind to concrete input source IDs. A discovered language label alone does not prove that conversion is supported. +- **One stale-correction guard.** Reuse `CaptureStateStore.correctionEpoch` for changes that invalidate correction configuration. Add an explicit configuration invalidation operation; changing enabled state to the same value is not an invalidation mechanism. Mapping snapshots can have a separate generation to identify their contents, but requests must still pass the existing sequence, edit, context, access, and correction-epoch checks. +- **Immutable request configuration.** Detection must use the mapping and exception snapshots captured for that request. Refreshes invalidate pending work before publishing replacement configuration; never mix an old conversion with a newly selected target source. +- **Exceptions protect automatic behavior.** Match the normalized original token exactly. Do not treat an exception as a dictionary entry or as positive language evidence. A protected token stops a backward context sweep. Explicit correction hotkeys, selected-text conversion, and the user-selected On Layout Switch mode retain manual intent. +- **No automatic collection or learning.** Remembering a reverted word requires an explicit user action. Benchmark fixtures are synthetic or explicitly contributed and reviewed; typed text is not silently recorded for evaluation. +- **Abstention is observable.** Unsupported or ambiguous mappings must not fall back to an unrelated QWERTY layout. The benchmark records abstention and recall alongside false positives so disabling corrections cannot appear to improve quality without a cost. +- **One set of layout tables.** Deterministic key rendering for the benchmark and the mapper's fixtures both use the frozen `Benchmarks/layout-tables/v1` files, generated once from real macOS layouts, reviewed, and versioned. Neither depends on the operator's installed input sources. + +## Verification and reporting + +The current package uses standalone assertion-based executables: + +```sh +swift run -c release TestRunner +swift run -c release InputPipelineTestRunner +``` + +Use these existing targets for mandatory regression checks. At the baseline, `TestRunner` passes 313/313. `InputPipelineTestRunner` passes 962/963 in a session without Accessibility: `TextCorrector must apply layout-only plan without deleting text` calls the real `TextCorrector.apply` and needs the permission. Treat that failure as environment-dependent, not a regression; release CI on hosted runners passes it. `Tests/SwitchFixTests` is not currently registered as a SwiftPM test target, and its only file is a placeholder comment; adding a file there does not make it run. New benchmark commands and files shown in the individual plans are proposed deliverables, not existing tools. + +Optional real-input smoke testing requires a suitable macOS session and permissions: + +```sh +swift run -c release InputPipelineTestRunner --integration-smoke +``` + +Record missing permissions, hardware, layouts, dictionaries, or competitor prerequisites as unverified coverage. Keep production app changes out of the planning-only delivery. During implementation, preserve secure-field handling, excluded applications, queue bounds, undo eligibility, clipboard restoration, and rollback protections. diff --git a/docs/releases/v0.0.12.md b/docs/releases/v0.0.12.md new file mode 100644 index 0000000..95e65b7 --- /dev/null +++ b/docs/releases/v0.0.12.md @@ -0,0 +1,30 @@ +# SwitchFix v0.0.12 + +More control over automatic correction, with safer handling of words typed in the wrong layout. + +## New features + +- **Personal exceptions:** add words in Settings → Personal Exceptions to keep them exactly as typed during automatic correction. Hotkey and layout-switch corrections remain available when you explicitly request them. +- **Correct earlier words together:** when a later word confirms the intended layout, SwitchFix can also correct earlier words in the same uninterrupted typing run. It stops at Enter, uncertain text, or a protected exception. + +## Improvements + +- Automatic correction works between English and one selected Cyrillic layout, avoiding unwanted Russian-to-Ukrainian or Ukrainian-to-Russian conversions. +- Apple keyboard layouts are classified by their primary language instead of every language their character set can represent. +- More reliable correction bookkeeping when typing is interrupted, text is deleted, or a correction cannot be emitted. +- Added a reproducible layout benchmark, frozen test corpora, and CI regression gates to catch correction regressions before release. +- Added `vnc` to the English allow list. + +## Install or update + +Download **SwitchFix-arm64.app.zip** for Apple Silicon or **SwitchFix-intel.app.zip** for Intel. macOS 13 or later is required. + +Builds are ad-hoc signed and are not notarized by Apple. macOS may require **Privacy & Security → Open Anyway** on first launch. After updating, you may need to grant Accessibility and keyboard input permissions again. See the [setup guide](https://rundax.github.io/SwitchFix/tutorial/). + +## Known issues + +- [#14](https://github.com/rundax/SwitchFix/issues/14): source builds with newer Swift toolchains can package dictionary binaries in the wrong bundle directory. +- [#15](https://github.com/rundax/SwitchFix/issues/15): switching layouts with Globe can leave correction paused until a mouse click. +- [#16](https://github.com/rundax/SwitchFix/issues/16): modifier-only correction shortcuts are not supported yet. + +**Changes:** [PR #22](https://github.com/rundax/SwitchFix/pull/22) · [Full changelog](https://github.com/rundax/SwitchFix/compare/v0.0.11...v0.0.12)