From 1e7d764c0ea08f277c648ebf78581ce04d20a453 Mon Sep 17 00:00:00 2001 From: Fabien Cazenave Date: Thu, 15 Feb 2024 22:38:52 +0100 Subject: [PATCH 1/2] [dactylo] n-grams (#63) * [dactylo] n-grams * s/wount/count/ * code factorization --- Makefile | 16 ++-- dactylo.html | 18 ++--- {corpus => data/corpus}/chardict.py | 0 {corpus => data/corpus}/en+fr.json | 0 {corpus => data/corpus}/en+fr.txt | 0 {corpus => data/corpus}/en.json | 0 {corpus => data/corpus}/en.txt | 0 {corpus => data/corpus}/fr.json | 0 {corpus => data/corpus}/fr.txt | 0 {corpus => data/corpus}/merge.py | 0 {dicts => data/dicts}/README.md | 0 {dicts => data/dicts}/english_10k.json | 0 {dicts => data/dicts}/english_1k.json | 0 {dicts => data/dicts}/french_10k.json | 0 {dicts => data/dicts}/french_1k.json | 0 {layouts => data/layouts}/ISRT.json | 0 {layouts => data/layouts}/ISRT.yaml | 0 {layouts => data/layouts}/MTGAP.json | 0 {layouts => data/layouts}/MTGAP.yaml | 0 {layouts => data/layouts}/azerty.json | 0 {layouts => data/layouts}/azerty.yaml | 0 {layouts => data/layouts}/bepo.json | 0 {layouts => data/layouts}/bepo.yaml | 0 {layouts => data/layouts}/colemak-dh.json | 0 {layouts => data/layouts}/colemak-dh.yaml | 0 {layouts => data/layouts}/colemak.json | 0 {layouts => data/layouts}/colemak.yaml | 0 {layouts => data/layouts}/dvorak.json | 0 {layouts => data/layouts}/dvorak.yaml | 0 {layouts => data/layouts}/ergol.json | 0 {layouts => data/layouts}/ergol.toml | 0 {layouts => data/layouts}/lafayette.json | 0 {layouts => data/layouts}/lafayette.yaml | 0 {layouts => data/layouts}/qwerty.json | 0 {layouts => data/layouts}/qwerty.yaml | 0 {layouts => data/layouts}/workman.json | 0 {layouts => data/layouts}/workman.yaml | 0 index.html | 2 +- js/dactylo.js | 98 +++++++++++++++-------- js/heatmap.js | 4 +- 40 files changed, 84 insertions(+), 54 deletions(-) rename {corpus => data/corpus}/chardict.py (100%) rename {corpus => data/corpus}/en+fr.json (100%) rename {corpus => data/corpus}/en+fr.txt (100%) rename {corpus => data/corpus}/en.json (100%) rename {corpus => data/corpus}/en.txt (100%) rename {corpus => data/corpus}/fr.json (100%) rename {corpus => data/corpus}/fr.txt (100%) rename {corpus => data/corpus}/merge.py (100%) rename {dicts => data/dicts}/README.md (100%) rename {dicts => data/dicts}/english_10k.json (100%) rename {dicts => data/dicts}/english_1k.json (100%) rename {dicts => data/dicts}/french_10k.json (100%) rename {dicts => data/dicts}/french_1k.json (100%) rename {layouts => data/layouts}/ISRT.json (100%) rename {layouts => data/layouts}/ISRT.yaml (100%) rename {layouts => data/layouts}/MTGAP.json (100%) rename {layouts => data/layouts}/MTGAP.yaml (100%) rename {layouts => data/layouts}/azerty.json (100%) rename {layouts => data/layouts}/azerty.yaml (100%) rename {layouts => data/layouts}/bepo.json (100%) rename {layouts => data/layouts}/bepo.yaml (100%) rename {layouts => data/layouts}/colemak-dh.json (100%) rename {layouts => data/layouts}/colemak-dh.yaml (100%) rename {layouts => data/layouts}/colemak.json (100%) rename {layouts => data/layouts}/colemak.yaml (100%) rename {layouts => data/layouts}/dvorak.json (100%) rename {layouts => data/layouts}/dvorak.yaml (100%) rename {layouts => data/layouts}/ergol.json (100%) rename {layouts => data/layouts}/ergol.toml (100%) rename {layouts => data/layouts}/lafayette.json (100%) rename {layouts => data/layouts}/lafayette.yaml (100%) rename {layouts => data/layouts}/qwerty.json (100%) rename {layouts => data/layouts}/qwerty.yaml (100%) rename {layouts => data/layouts}/workman.json (100%) rename {layouts => data/layouts}/workman.yaml (100%) diff --git a/Makefile b/Makefile index 585e664f..a5a82539 100644 --- a/Makefile +++ b/Makefile @@ -1,16 +1,16 @@ all: - @for file in layouts/*.toml; do \ - kalamine make $$file --out "layouts/$$(basename $${file%.*}).json"; \ + @for file in data/layouts/*.toml; do \ + kalamine make $$file --out "data/layouts/$$(basename $${file%.*}).json"; \ done - @for file in layouts/*.yaml; do \ - kalamine make $$file --out "layouts/$$(basename $${file%.*}).json"; \ + @for file in data/layouts/*.yaml; do \ + kalamine make $$file --out "data/layouts/$$(basename $${file%.*}).json"; \ done watch: - @inotifywait -m layouts -e close_write | while read -r _path _action file; do \ + @inotifywait -m data/layouts -e close_write | while read -r _path _action file; do \ case $$file in \ - *yaml) kalamine make "layouts/$$file" --out "layouts/$$(basename "$${file%.*}").json";; \ - *toml) kalamine make "layouts/$$file" --out "layouts/$$(basename "$${file%.*}").json";; \ + *yaml) kalamine make "data/layouts/$$file" --out "data/layouts/$$(basename "$${file%.*}").json";; \ + *toml) kalamine make "data/layouts/$$file" --out "data/layouts/$$(basename "$${file%.*}").json";; \ esac \ done @@ -25,7 +25,7 @@ clean: install: @echo "Installer script for XKB (GNU/Linux). Requires super-user privileges." @echo - xkalamine install layouts/ergol.yaml + xkalamine install data/layouts/ergol.yaml uninstall: @echo "Unistaller script for XKB (GNU/Linux). Requires super-user privileges." diff --git a/dactylo.html b/dactylo.html index 27b06e29..2baa55dd 100644 --- a/dactylo.html +++ b/dactylo.html @@ -31,18 +31,18 @@

·

diff --git a/corpus/chardict.py b/data/corpus/chardict.py similarity index 100% rename from corpus/chardict.py rename to data/corpus/chardict.py diff --git a/corpus/en+fr.json b/data/corpus/en+fr.json similarity index 100% rename from corpus/en+fr.json rename to data/corpus/en+fr.json diff --git a/corpus/en+fr.txt b/data/corpus/en+fr.txt similarity index 100% rename from corpus/en+fr.txt rename to data/corpus/en+fr.txt diff --git a/corpus/en.json b/data/corpus/en.json similarity index 100% rename from corpus/en.json rename to data/corpus/en.json diff --git a/corpus/en.txt b/data/corpus/en.txt similarity index 100% rename from corpus/en.txt rename to data/corpus/en.txt diff --git a/corpus/fr.json b/data/corpus/fr.json similarity index 100% rename from corpus/fr.json rename to data/corpus/fr.json diff --git a/corpus/fr.txt b/data/corpus/fr.txt similarity index 100% rename from corpus/fr.txt rename to data/corpus/fr.txt diff --git a/corpus/merge.py b/data/corpus/merge.py similarity index 100% rename from corpus/merge.py rename to data/corpus/merge.py diff --git a/dicts/README.md b/data/dicts/README.md similarity index 100% rename from dicts/README.md rename to data/dicts/README.md diff --git a/dicts/english_10k.json b/data/dicts/english_10k.json similarity index 100% rename from dicts/english_10k.json rename to data/dicts/english_10k.json diff --git a/dicts/english_1k.json b/data/dicts/english_1k.json similarity index 100% rename from dicts/english_1k.json rename to data/dicts/english_1k.json diff --git a/dicts/french_10k.json b/data/dicts/french_10k.json similarity index 100% rename from dicts/french_10k.json rename to data/dicts/french_10k.json diff --git a/dicts/french_1k.json b/data/dicts/french_1k.json similarity index 100% rename from dicts/french_1k.json rename to data/dicts/french_1k.json diff --git a/layouts/ISRT.json b/data/layouts/ISRT.json similarity index 100% rename from layouts/ISRT.json rename to data/layouts/ISRT.json diff --git a/layouts/ISRT.yaml b/data/layouts/ISRT.yaml similarity index 100% rename from layouts/ISRT.yaml rename to data/layouts/ISRT.yaml diff --git a/layouts/MTGAP.json b/data/layouts/MTGAP.json similarity index 100% rename from layouts/MTGAP.json rename to data/layouts/MTGAP.json diff --git a/layouts/MTGAP.yaml b/data/layouts/MTGAP.yaml similarity index 100% rename from layouts/MTGAP.yaml rename to data/layouts/MTGAP.yaml diff --git a/layouts/azerty.json b/data/layouts/azerty.json similarity index 100% rename from layouts/azerty.json rename to data/layouts/azerty.json diff --git a/layouts/azerty.yaml b/data/layouts/azerty.yaml similarity index 100% rename from layouts/azerty.yaml rename to data/layouts/azerty.yaml diff --git a/layouts/bepo.json b/data/layouts/bepo.json similarity index 100% rename from layouts/bepo.json rename to data/layouts/bepo.json diff --git a/layouts/bepo.yaml b/data/layouts/bepo.yaml similarity index 100% rename from layouts/bepo.yaml rename to data/layouts/bepo.yaml diff --git a/layouts/colemak-dh.json b/data/layouts/colemak-dh.json similarity index 100% rename from layouts/colemak-dh.json rename to data/layouts/colemak-dh.json diff --git a/layouts/colemak-dh.yaml b/data/layouts/colemak-dh.yaml similarity index 100% rename from layouts/colemak-dh.yaml rename to data/layouts/colemak-dh.yaml diff --git a/layouts/colemak.json b/data/layouts/colemak.json similarity index 100% rename from layouts/colemak.json rename to data/layouts/colemak.json diff --git a/layouts/colemak.yaml b/data/layouts/colemak.yaml similarity index 100% rename from layouts/colemak.yaml rename to data/layouts/colemak.yaml diff --git a/layouts/dvorak.json b/data/layouts/dvorak.json similarity index 100% rename from layouts/dvorak.json rename to data/layouts/dvorak.json diff --git a/layouts/dvorak.yaml b/data/layouts/dvorak.yaml similarity index 100% rename from layouts/dvorak.yaml rename to data/layouts/dvorak.yaml diff --git a/layouts/ergol.json b/data/layouts/ergol.json similarity index 100% rename from layouts/ergol.json rename to data/layouts/ergol.json diff --git a/layouts/ergol.toml b/data/layouts/ergol.toml similarity index 100% rename from layouts/ergol.toml rename to data/layouts/ergol.toml diff --git a/layouts/lafayette.json b/data/layouts/lafayette.json similarity index 100% rename from layouts/lafayette.json rename to data/layouts/lafayette.json diff --git a/layouts/lafayette.yaml b/data/layouts/lafayette.yaml similarity index 100% rename from layouts/lafayette.yaml rename to data/layouts/lafayette.yaml diff --git a/layouts/qwerty.json b/data/layouts/qwerty.json similarity index 100% rename from layouts/qwerty.json rename to data/layouts/qwerty.json diff --git a/layouts/qwerty.yaml b/data/layouts/qwerty.yaml similarity index 100% rename from layouts/qwerty.yaml rename to data/layouts/qwerty.yaml diff --git a/layouts/workman.json b/data/layouts/workman.json similarity index 100% rename from layouts/workman.json rename to data/layouts/workman.json diff --git a/layouts/workman.yaml b/data/layouts/workman.yaml similarity index 100% rename from layouts/workman.yaml rename to data/layouts/workman.yaml diff --git a/index.html b/index.html index f6ee99cf..646f59a6 100644 --- a/index.html +++ b/index.html @@ -40,7 +40,7 @@

Ergo‑L

- +

Objectifs

diff --git a/js/dactylo.js b/js/dactylo.js index b0b0e58f..654f3d8d 100644 --- a/js/dactylo.js +++ b/js/dactylo.js @@ -1,4 +1,6 @@ -const g30Keys = [ +const STARTING_LEVEL = 4; +const MIN_WORD_COUNT = 42; +const ALL_30_KEYS = [ 'KeyF', 'KeyJ', 'KeyD', 'KeyK', 'KeyS', 'KeyL', @@ -30,39 +32,57 @@ window.addEventListener('DOMContentLoaded', () => { const gInput = document; let gKeyLayout = undefined; - let gDictionary = undefined; + let gDictionary = { + words: undefined, + trigrams: undefined, + bigrams: undefined, + }; - let gLessonLevel = 12; - let gLessonWords = []; - let gLessonCurrent = undefined; + let gLessonLevel = STARTING_LEVEL; + let gLessonWords = []; + let gLessonCurrent = undefined; let gLessonStartTime = undefined; - let gPendingError = false; + let gPendingError = false; - const setLayout = () => { - fetch(`layouts/${gLayout.value}.json`) + // fetch a kalamine corpus: symbols, bigrams, trigrams + const fetchNgrams = () => { + const ngrams = gDict.value.split(',')[0]; + return fetch(`data/corpus/${ngrams}.json`) .then(response => response.json()) - .then(layout => { - gKeyboard.setKeyboardLayout(layout.keymap, layout.deadkeys, gGeometry.value); - gKeyboard.theme = 'hints'; - gKeyLayout = layout; - setLessonLevel(); + .then(data => { + gDictionary.trigrams = Object.keys(data.trigrams); + gDictionary.bigrams = Object.keys(data.digrams); }); }; - const setDict = () => { - fetch(`dicts/${gDict.value}.json`) + // fetch MonkeyType words + const fetchWords = () => { + const words = gDict.value.split(',')[1]; + return fetch(`data/dicts/${words}.json`) .then(response => response.json()) .then(data => { - gDictionary = data.words; - setLessonLevel(); + gDictionary.words = data.words; + }); + }; + + // fetch a kalamine keyboard layout + const fetchLayout = () => { + return fetch(`data/layouts/${gLayout.value}.json`) + .then(response => response.json()) + .then(layout => { + gKeyboard.setKeyboardLayout(layout.keymap, layout.deadkeys, gGeometry.value); + gKeyboard.theme = 'hints'; + gKeyLayout = layout; }); }; - gLayout.addEventListener('change', setLayout); - setLayout(); + gLayout.addEventListener('change', () => { + fetchLayout().then(setLessonLevel); + }); - gDict.addEventListener('change', setDict); - setDict(); + gDict.addEventListener('change', () => { + Promise.all([fetchNgrams(), fetchWords()]).then(setLessonLevel); + }); gGeometry.addEventListener('change', event => { gKeyboard.geometry = gGeometry.value; @@ -70,17 +90,15 @@ window.addEventListener('DOMContentLoaded', () => { gKeyList.addEventListener('click', event => { if (event.target.nodeName.toLowerCase() == 'kbd') { - setLessonLevel(event.target.dataset.level); + gLessonLevel = event.target.dataset.level; + setLessonLevel(); } }); - const setLessonLevel = (level = gLessonLevel) => { - if (!gKeyLayout || !gDictionary) { - return; - } - - const keys = g30Keys.slice(0, level); - const rawLetters = keys.flatMap(key => gKeyLayout.keymap[key]); + const setLessonLevel = () => { + const keys = ALL_30_KEYS.slice(0, gLessonLevel); + // const rawLetters = keys.flatMap(key => gKeyLayout.keymap[key]); + const rawLetters = keys.map(key => gKeyLayout.keymap[key][0]); const odk = gKeyLayout.deadkeys['**']; const has1dk = keys.some(key => gKeyLayout.keymap[key].indexOf('**') >= 0); @@ -88,11 +106,18 @@ window.addEventListener('DOMContentLoaded', () => { rawLetters .filter(letter => letter in odk) .map(letter => odk[letter]); - const lessonLetters = rawLetters.concat(deadkeyLetters); - gLessonLevel = level; - gLessonWords = gDictionary.filter(word => - Array.from(word).every(letter => lessonLetters.indexOf(letter) >= 0)); + const lessonLetters = rawLetters.concat(deadkeyLetters); + const lessonFilter = word => + Array.from(word).every(letter => lessonLetters.indexOf(letter) >= 0); + + gLessonWords = []; + for (dict of [gDictionary.words, gDictionary.trigrams, gDictionary.bigrams, rawLetters]) { + gLessonWords = gLessonWords.concat(dict.filter(lessonFilter)); + if (gLessonWords.length > MIN_WORD_COUNT) { + break; + } + } showLesson(); showKeys(); @@ -105,7 +130,7 @@ window.addEventListener('DOMContentLoaded', () => { const state = idx < gLessonLevel ? '' : 'inactive'; return `${char}`; }; - gKeyList.innerHTML = g30Keys.map(serializeKey).join(''); + gKeyList.innerHTML = ALL_30_KEYS.map(serializeKey).join(''); }; const showLesson = () => { @@ -168,6 +193,11 @@ window.addEventListener('DOMContentLoaded', () => { } }; + // startup + Promise.all([fetchNgrams(), fetchWords(), fetchLayout()]) + .then(setLessonLevel); + + /** * Keyboard highlighting & layout emulation */ diff --git a/js/heatmap.js b/js/heatmap.js index b77a2302..e7bb813e 100644 --- a/js/heatmap.js +++ b/js/heatmap.js @@ -455,7 +455,7 @@ window.addEventListener('DOMContentLoaded', () => { const setProp = (key, value) => { if (key === 'layout') { if (value) { - fetch(`layouts/${value}.json`) + fetch(`data/layouts/${value}.json`) .then(response => response.json()) .then(data => { inputField.placeholder = `Zone de saisie ${value}`; @@ -479,7 +479,7 @@ window.addEventListener('DOMContentLoaded', () => { } } else if (key === 'corpus') { if (value && value !== corpusName) { - fetch(`corpus/${value}.json`) + fetch(`data/corpus/${value}.json`) .then(response => response.json()) .then(data => { corpus = data.symbols; From 8f55c9039959730fc152eec0b45d01aa9aebe7b1 Mon Sep 17 00:00:00 2001 From: Fabien Cazenave Date: Fri, 16 Feb 2024 05:58:41 +0100 Subject: [PATCH 2/2] bugfix: JS error in Chromium --- js/dactylo.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/js/dactylo.js b/js/dactylo.js index 654f3d8d..ae068c34 100644 --- a/js/dactylo.js +++ b/js/dactylo.js @@ -112,7 +112,9 @@ window.addEventListener('DOMContentLoaded', () => { Array.from(word).every(letter => lessonLetters.indexOf(letter) >= 0); gLessonWords = []; - for (dict of [gDictionary.words, gDictionary.trigrams, gDictionary.bigrams, rawLetters]) { + for (const dict of [ + gDictionary.words, gDictionary.trigrams, gDictionary.bigrams, rawLetters + ]) { gLessonWords = gLessonWords.concat(dict.filter(lessonFilter)); if (gLessonWords.length > MIN_WORD_COUNT) { break;