diff --git a/HISTORY.md b/HISTORY.md index 185c9d7626f..2ee27cd1989 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,5 +1,52 @@ # Keyman Version History +## 18.0.95 alpha 2024-08-22 + +* chore(common): allow build agents to automatically select emsdk version, and enable support for 3.1.60+ (#12243) + +## 18.0.94 alpha 2024-08-21 + +* fix(core): look for `emcc` instead of `emcc.py` (#12235) +* fix(web): improve tokenization output when wordbreaker breaks spec for span properties in output (#12229) +* chore(android): Use RTL-aware alignment and padding for layouts (#12225) +* fix(web): disable fat-finger data use when mayCorrect = false (#12220) +* fix(android): Auto-mirror back and forward arrows for RTL support (#12227) +* feat(android): Add localization for Arabic (#12228) +* fix(android): Auto-mirror increment and decrement arrows for RTL support (#12230) + +## 18.0.93 alpha 2024-08-20 + +* (#12188) +* fix(web): fix malformed reversion display strings (#12201) +* feat(android): Add menu to specify long-press delay (#12170) +* feat(android): Pass longpress delay to KeymanWeb (#12185) +* (#12223) + +## 18.0.92 alpha 2024-08-19 + +* chore(deps-dev): bump @75lb/deep-merge from 1.1.1 to 1.1.2 (#12118) +* chore(deps): bump semver from 7.5.4 to 7.6.0 (#12119) +* fix(windows): "Keyman" is not localized in UI strings (#12162) +* feat(android): Enhance how ENTER key is handled in apps (#12125) +* refactor(web): move `lm-worker` → `worker-thread` (#12150) +* fix(developer): remove redundant check in LdmlKeyboardCompiler.validate() (#11858) + +## 18.0.91 alpha 2024-08-16 + +* refactor(web): move `predictive-text` → `worker-main` (#12146) +* fix(web): restore flick functionality (#12187) +* refactor(web): move `lm-message-types` → `predictive-text/types` (#12149) +* fix(developer): enforce presence of kps Info.Description field in info compilers (#12204) +* fix(developer): enforce presence of Version field when FollowKeyboardVersion is not set, in package compiler (#12205) + +## 18.0.90 alpha 2024-08-15 + +* refactor(web): move parts of `keyboard-processor` → `js-processor` (#12111) +* fix(web): allow `lm-worker` to build on Linux (#12181) +* refactor(web): move remaining parts of `keyboard-processor` → `keyboard` (#12131) +* docs: update .kmx documentation around bitmaps, modifier state (#12183) +* refactor(web): rename `package-cache` → `keyboard-storage` (#12135) + ## 18.0.89 alpha 2024-08-14 * feat(web): test skipped prediction round handling (#12169) diff --git a/VERSION.md b/VERSION.md index 49d69062262..998f9bf6fd3 100644 --- a/VERSION.md +++ b/VERSION.md @@ -1 +1 @@ -18.0.90 \ No newline at end of file +18.0.96 \ No newline at end of file diff --git a/android/KMAPro/kMAPro/src/main/AndroidManifest.xml b/android/KMAPro/kMAPro/src/main/AndroidManifest.xml index 7196abdb229..3ba7eaf9fca 100644 --- a/android/KMAPro/kMAPro/src/main/AndroidManifest.xml +++ b/android/KMAPro/kMAPro/src/main/AndroidManifest.xml @@ -297,6 +297,11 @@ android:configChanges="orientation" android:label="@string/app_name" android:theme="@style/AppTheme.Base" /> + diff --git a/android/KMAPro/kMAPro/src/main/java/com/keyman/android/SystemKeyboard.java b/android/KMAPro/kMAPro/src/main/java/com/keyman/android/SystemKeyboard.java index c6885b2b564..4fb1c567922 100644 --- a/android/KMAPro/kMAPro/src/main/java/com/keyman/android/SystemKeyboard.java +++ b/android/KMAPro/kMAPro/src/main/java/com/keyman/android/SystemKeyboard.java @@ -4,6 +4,7 @@ package com.keyman.android; +import com.tavultesoft.kmapro.AdjustLongpressDelayActivity; import com.tavultesoft.kmapro.BuildConfig; import com.tavultesoft.kmapro.DefaultLanguageResource; import com.tavultesoft.kmapro.KeymanSettingsActivity; @@ -175,6 +176,9 @@ public void onStartInput(EditorInfo attribute, boolean restarting) { } } + // Determine special handling for ENTER key + KMManager.setEnterMode(attribute.imeOptions, inputType); + InputConnection ic = getCurrentInputConnection(); if (ic != null) { ExtractedText icText = ic.getExtractedText(new ExtractedTextRequest(), 0); @@ -244,6 +248,8 @@ public void onKeyboardLoaded(KeyboardType keyboardType) { if (exText != null) exText = null; } + // Initialize keyboard options + KMManager.sendOptionsToKeyboard(); } @Override diff --git a/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/AdjustLongpressDelayActivity.java b/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/AdjustLongpressDelayActivity.java new file mode 100644 index 00000000000..2ecf62fe099 --- /dev/null +++ b/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/AdjustLongpressDelayActivity.java @@ -0,0 +1,149 @@ +/* + * Keyman is copyright (C) SIL International. MIT License. + * + * (Optional description of this file) + */ +package com.tavultesoft.kmapro; + +import android.content.Context; +import android.content.SharedPreferences; +import android.graphics.drawable.ColorDrawable; +import android.os.Bundle; +import android.view.View; +import android.widget.SeekBar; +import android.widget.TextView; + +import androidx.appcompat.app.ActionBar; +import androidx.appcompat.widget.Toolbar; +import androidx.core.content.ContextCompat; + +import com.keyman.engine.BaseActivity; +import com.keyman.engine.KMManager; + +/** + * Settings menu for adjusting the longpress delay time. The value for the current longpress delay time + * is saved in shared preferences as an integer (milliseconds). + */ +public class AdjustLongpressDelayActivity extends BaseActivity { + private static final String TAG = "AdjustLongpressDelay"; + public static final String adjustLongpressDelayKey = "AdjustLongpressDelay"; + + // Keeps track of the adjusted longpress delay time for saving. + // Internally use milliseconds, but GUI displays seconds + private static int currentDelayTimeMS = KMManager.KMDefault_LongpressDelay; // ms + private static int minLongpressTime = 300; // ms + private static int maxLongpressTime = 1500; // ms + private static int delayTimeIncrement = 200; // ms + + /** + * Convert currentDelayTimeMS to progress + * @return int + */ + private int delayTimeToProgress() { + return (currentDelayTimeMS / delayTimeIncrement) - 1; + } + + /** + * Convert progress to currentDelayTimeMS + * @param progress + * @return int (milliseconds) + */ + private int progressToDelayTime(int progress) { + return (progress + 1) * delayTimeIncrement + 100; + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + final Context context = this; + + setContentView(R.layout.activity_adjust_longpress_delay); + Toolbar toolbar = (Toolbar) findViewById(R.id.titlebar); + setSupportActionBar(toolbar); + ActionBar actionBar = getSupportActionBar(); + if (actionBar != null) { + actionBar.setTitle(null); + actionBar.setDisplayUseLogoEnabled(false); + actionBar.setDisplayHomeAsUpEnabled(true); + actionBar.setDisplayShowHomeEnabled(true); + actionBar.setDisplayShowTitleEnabled(false); + actionBar.setDisplayShowCustomEnabled(true); + actionBar.setBackgroundDrawable(new ColorDrawable(ContextCompat.getColor(this, R.color.keyman_blue))); + } + + TextView adjustLongpressDelayActivityTitle = (TextView) findViewById(R.id.bar_title); + + String titleStr = getString(R.string.adjust_longpress_delay); + adjustLongpressDelayActivityTitle.setTextColor(ContextCompat.getColor(this, R.color.ms_white)); + adjustLongpressDelayActivityTitle.setText(titleStr); + + currentDelayTimeMS = KMManager.getLongpressDelay(); + + TextView adjustLongpressDelayText = (TextView) findViewById(R.id.delayTimeText); + String longpressDelayTextSeconds = String.format(getString(R.string.longpress_delay_time), (float)(currentDelayTimeMS/1000.0)); + adjustLongpressDelayText.setTextAlignment(View.TEXT_ALIGNMENT_CENTER); + adjustLongpressDelayText.setText(longpressDelayTextSeconds); + + final SeekBar seekBar = (SeekBar) findViewById(R.id.seekBar); + seekBar.setProgress(delayTimeToProgress()); + seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { + + @Override + public void onStopTrackingTouch(SeekBar seekBar) { + // Do nothing + } + + @Override + public void onStartTrackingTouch(SeekBar seekBar) { + // Do nothing + } + + @Override + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { + // Update the text field. + // The keyboard options will be saved and sent to KeymanWeb when exiting the menu + currentDelayTimeMS = progressToDelayTime(progress); + String longpressDelayTextSeconds = String.format(getString(R.string.longpress_delay_time), (float)(currentDelayTimeMS/1000.0)); + adjustLongpressDelayText.setText(longpressDelayTextSeconds); + } + }); + + findViewById(R.id.delayTimeDownButton).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (currentDelayTimeMS > minLongpressTime) { + currentDelayTimeMS -= delayTimeIncrement; + seekBar.setProgress(delayTimeToProgress()); + } + } + }); + + findViewById(R.id.delayTimeUpButton).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (currentDelayTimeMS < maxLongpressTime) { + currentDelayTimeMS += delayTimeIncrement; + seekBar.setProgress(delayTimeToProgress()); + } + } + }); + } + + @Override + public void onBackPressed() { + // Store the longpress delay as a reference + // and then update KeymanWeb with the longpress delay + KMManager.setLongpressDelay(currentDelayTimeMS); + KMManager.sendOptionsToKeyboard(); + + super.onBackPressed(); + } + + @Override + public boolean onSupportNavigateUp() { + onBackPressed(); + return true; + } + +} diff --git a/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/KeyboardSettingsActivity.java b/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/KeyboardSettingsActivity.java index 0989bf0d08b..689548a56ec 100644 --- a/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/KeyboardSettingsActivity.java +++ b/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/KeyboardSettingsActivity.java @@ -105,7 +105,7 @@ public void onCreate(Bundle savedInstanceState) { hashMap = new HashMap<>(); final String customHelpLink = kbd.getHelpLink(); // Check if app declared FileProvider - icon = String.valueOf(R.drawable.ic_arrow_forward); + icon = String.valueOf(R.drawable.ic_action_forward); // Don't show help link arrow if File Provider unavailable, or custom help doesn't exist if ( (customHelpLink != null && !FileProviderUtils.exists(context)) || (customHelpLink == null && !packageID.equals(KMManager.KMDefault_UndefinedPackageID)) ) { diff --git a/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/KeymanSettingsFragment.java b/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/KeymanSettingsFragment.java index 72c54aa0d02..95bb7774b0b 100644 --- a/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/KeymanSettingsFragment.java +++ b/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/KeymanSettingsFragment.java @@ -28,7 +28,7 @@ public class KeymanSettingsFragment extends PreferenceFragmentCompat { private static Context context; private Preference languagesPreference, installKeyboardOrDictionary, displayLanguagePreference, - adjustKeyboardHeight; + adjustKeyboardHeight, adjustLongpressDelay; private ListPreference spacebarTextPreference; private CheckBoxPreference setSystemKeyboardPreference; private CheckBoxPreference setDefaultKeyboardPreference; @@ -99,6 +99,7 @@ public boolean onPreferenceClick(Preference preference) { }); setDefaultKeyboardPreference.setOnPreferenceChangeListener(checkBlocker); + adjustKeyboardHeight = new Preference(context); adjustKeyboardHeight.setKey(AdjustKeyboardHeightActivity.adjustKeyboardHeightKey); adjustKeyboardHeight.setTitle(getString(R.string.adjust_keyboard_height)); @@ -106,6 +107,13 @@ public boolean onPreferenceClick(Preference preference) { Intent adjustKeyboardHeightIntent = new Intent(context, AdjustKeyboardHeightActivity.class); adjustKeyboardHeight.setIntent(adjustKeyboardHeightIntent); + adjustLongpressDelay = new Preference(context); + adjustLongpressDelay.setKey(AdjustLongpressDelayActivity.adjustLongpressDelayKey); + adjustLongpressDelay.setTitle(getString(R.string.adjust_longpress_delay)); + adjustLongpressDelay.setWidgetLayoutResource(R.layout.preference_duration_icon_layout); + Intent adjustLongpressDelayIntent = new Intent(context, AdjustLongpressDelayActivity.class); + adjustLongpressDelay.setIntent(adjustLongpressDelayIntent); + /* Spacebar Caption Preference */ spacebarTextPreference = new ListPreference(context); @@ -188,8 +196,6 @@ as part of the default onClick() used by SwitchPreference. sendCrashReportPreference.setSummaryOff(getString(R.string.show_send_crash_report_off)); sendCrashReportPreference.setDefaultValue(true); - - screen.addPreference(languagesPreference); screen.addPreference(installKeyboardOrDictionary); screen.addPreference(displayLanguagePreference); @@ -197,6 +203,7 @@ as part of the default onClick() used by SwitchPreference. screen.addPreference(setDefaultKeyboardPreference); screen.addPreference(adjustKeyboardHeight); + screen.addPreference(adjustLongpressDelay); screen.addPreference(spacebarTextPreference); screen.addPreference(hapticFeedbackPreference); diff --git a/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/LanguageSettingsActivity.java b/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/LanguageSettingsActivity.java index faa3e0efbf1..75116bad8cc 100644 --- a/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/LanguageSettingsActivity.java +++ b/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/LanguageSettingsActivity.java @@ -174,7 +174,7 @@ public void onCreate(Bundle savedInstanceState) { updateActiveLexicalModel(); ImageView imageView = (ImageView) layout.findViewById(R.id.image1); - imageView.setImageResource(R.drawable.ic_arrow_forward); + imageView.setImageResource(R.drawable.ic_action_forward); layout.setEnabled(true); layout.setOnClickListener(new View.OnClickListener() { @Override @@ -198,7 +198,7 @@ public void onClick(View v) { * textView = (TextView) layout.findViewById(R.id.text1); * textView.setText(getString(R.string.manage_dictionary)); * imageView = (ImageView) layout.findViewById(R.id.image1); - * imageView.setImageResource(R.drawable.ic_arrow_forward); + * imageView.setImageResource(R.drawable.ic_action_forward); */ listView.setAdapter(adapter); @@ -346,7 +346,7 @@ public View getView(int position, View convertView, ViewGroup parent) { } holder.text.setText(kbd.getResourceName()); - holder.img.setImageResource(R.drawable.ic_arrow_forward); + holder.img.setImageResource(R.drawable.ic_action_forward); return convertView; } diff --git a/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/LanguagesSettingsActivity.java b/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/LanguagesSettingsActivity.java index 7b621da4b7a..6e21d04f7bf 100644 --- a/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/LanguagesSettingsActivity.java +++ b/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/LanguagesSettingsActivity.java @@ -203,7 +203,7 @@ public View getView(int position, View convertView, ViewGroup parent) { } holder.textLang.setText(data.name); - holder.img.setImageResource(R.drawable.ic_arrow_forward); + holder.img.setImageResource(R.drawable.ic_action_forward); holder.textCount.setText(getContext().getResources().getQuantityString(R.plurals.keyboard_count, data.keyboards.size(), data.keyboards.size())); diff --git a/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/MainActivity.java b/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/MainActivity.java index 1e5231f5215..2ae689364c3 100644 --- a/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/MainActivity.java +++ b/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/MainActivity.java @@ -472,7 +472,8 @@ public boolean onKeyUp(int keycode, KeyEvent e) { @Override public void onKeyboardLoaded(KeyboardType keyboardType) { - // Do nothing + // Initialize keyboard options + KMManager.sendOptionsToKeyboard(); } @Override @@ -646,7 +647,7 @@ private void showTextSizeDialog() { final View textSizeController = inflater.inflate(R.layout.text_size_controller, null); final AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(MainActivity.this); dialogBuilder.setIcon(R.drawable.ic_light_action_textsize); - dialogBuilder.setTitle(String.format(getString(R.string.text_size), textSize)); + dialogBuilder.setTitle(getTextSizeString()); dialogBuilder.setView(textSizeController); dialogBuilder.setPositiveButton(getString(R.string.label_ok), new DialogInterface.OnClickListener() { @Override @@ -677,7 +678,7 @@ public void onStartTrackingTouch(SeekBar seekBar) { public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { textSize = progress + minTextSize; textView.setTextSize((float) textSize); - dialog.setTitle(String.format(getString(R.string.text_size), textSize)); + dialog.setTitle(getTextSizeString()); } }); @@ -702,6 +703,16 @@ public void onClick(View v) { }); } + /** + * Combine a localized string for "Text Size" plus Arabic numerals + * @return String + */ + private String getTextSizeString() { + // Instead of formatting the number, will truncate formatting and concat the actual textSize + String label = getString(R.string.text_size).replace("%1$d", ""); + return label + KMString.format(" %d", textSize); + } + private void showClearTextDialog() { AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(MainActivity.this); dialogBuilder.setIcon(R.drawable.ic_light_action_trash); diff --git a/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/SelectPackageActivity.java b/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/SelectPackageActivity.java index 46bc68ca1a5..9b053321824 100644 --- a/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/SelectPackageActivity.java +++ b/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/SelectPackageActivity.java @@ -81,7 +81,7 @@ public void onCreate(Bundle savedInstanceState) { HashMap hashMap = new HashMap<>(); hashMap.put(titleKey, keyboardName); hashMap.put(subtitleKey, pkgID); - String icon = String.valueOf(R.drawable.ic_arrow_forward); + String icon = String.valueOf(R.drawable.ic_action_forward); hashMap.put(iconKey, icon); list.add(hashMap); } diff --git a/android/KMAPro/kMAPro/src/main/res/drawable/ic_action_back.xml b/android/KMAPro/kMAPro/src/main/res/drawable/ic_action_back.xml deleted file mode 100644 index 165d535d3d0..00000000000 --- a/android/KMAPro/kMAPro/src/main/res/drawable/ic_action_back.xml +++ /dev/null @@ -1,4 +0,0 @@ - - diff --git a/android/KMAPro/kMAPro/src/main/res/drawable/ic_action_decrement.xml b/android/KMAPro/kMAPro/src/main/res/drawable/ic_action_decrement.xml new file mode 100644 index 00000000000..c3085bec5e9 --- /dev/null +++ b/android/KMAPro/kMAPro/src/main/res/drawable/ic_action_decrement.xml @@ -0,0 +1,5 @@ + + diff --git a/android/KMAPro/kMAPro/src/main/res/drawable/ic_action_increment.xml b/android/KMAPro/kMAPro/src/main/res/drawable/ic_action_increment.xml new file mode 100644 index 00000000000..ea188f818f1 --- /dev/null +++ b/android/KMAPro/kMAPro/src/main/res/drawable/ic_action_increment.xml @@ -0,0 +1,5 @@ + + diff --git a/android/KMAPro/kMAPro/src/main/res/drawable/ic_action_timelapse.xml b/android/KMAPro/kMAPro/src/main/res/drawable/ic_action_timelapse.xml new file mode 100644 index 00000000000..80dc29e33f9 --- /dev/null +++ b/android/KMAPro/kMAPro/src/main/res/drawable/ic_action_timelapse.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/android/KMAPro/kMAPro/src/main/res/layout/activity_adjust_longpress_delay.xml b/android/KMAPro/kMAPro/src/main/res/layout/activity_adjust_longpress_delay.xml new file mode 100644 index 00000000000..ed0bed54a9b --- /dev/null +++ b/android/KMAPro/kMAPro/src/main/res/layout/activity_adjust_longpress_delay.xml @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + diff --git a/android/KMAPro/kMAPro/src/main/res/layout/activity_info.xml b/android/KMAPro/kMAPro/src/main/res/layout/activity_info.xml index 59de5185356..314a5034129 100644 --- a/android/KMAPro/kMAPro/src/main/res/layout/activity_info.xml +++ b/android/KMAPro/kMAPro/src/main/res/layout/activity_info.xml @@ -1,12 +1,12 @@ + xmlns:tools="http://schemas.android.com/tools" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical" + android:background="@color/ms_white" + tools:context=".InfoActivity" > - - + android:orientation="horizontal"> - + + + + + + android:src="@drawable/ic_action_back" /> + android:src="@drawable/ic_action_forward" /> + diff --git a/android/KMAPro/kMAPro/src/main/res/layout/preference_icon_layout.xml b/android/KMAPro/kMAPro/src/main/res/layout/preference_icon_layout.xml index 6aa84ce384b..7074c696de8 100644 --- a/android/KMAPro/kMAPro/src/main/res/layout/preference_icon_layout.xml +++ b/android/KMAPro/kMAPro/src/main/res/layout/preference_icon_layout.xml @@ -2,4 +2,4 @@ + android:src="@drawable/ic_action_forward" /> diff --git a/android/KMAPro/kMAPro/src/main/res/layout/text_size_controller.xml b/android/KMAPro/kMAPro/src/main/res/layout/text_size_controller.xml index 2245ef557a3..c250b1bf072 100644 --- a/android/KMAPro/kMAPro/src/main/res/layout/text_size_controller.xml +++ b/android/KMAPro/kMAPro/src/main/res/layout/text_size_controller.xml @@ -15,7 +15,7 @@ android:layout_marginStart="5dp" android:layout_marginEnd="5dp" android:contentDescription="@string/ic_text_size_down" - android:src="@drawable/ic_light_dialog_textsize_down" /> + android:src="@drawable/ic_action_decrement" /> + android:src="@drawable/ic_action_increment" /> diff --git a/android/KMAPro/kMAPro/src/main/res/layout/web_browser_bar_layout.xml b/android/KMAPro/kMAPro/src/main/res/layout/web_browser_bar_layout.xml index 2e9b059c55f..749412e0676 100644 --- a/android/KMAPro/kMAPro/src/main/res/layout/web_browser_bar_layout.xml +++ b/android/KMAPro/kMAPro/src/main/res/layout/web_browser_bar_layout.xml @@ -15,7 +15,6 @@ android:layout_height="match_parent" android:layout_margin="3dp" android:padding="4dp" - android:layout_alignParentLeft="true" android:layout_alignParentStart="true" android:layout_above="@+id/progressBar" android:weightSum="2" @@ -76,7 +75,6 @@ style="@style/Base.Widget.AppCompat.ProgressBar.Horizontal" android:layout_width="match_parent" android:layout_height="@dimen/keyman_bar_height" - android:layout_alignParentLeft="true" android:layout_alignParentStart="true" android:layout_alignParentBottom="true" android:visibility="visible" diff --git a/android/KMAPro/kMAPro/src/main/res/values-ar-rSA/strings.xml b/android/KMAPro/kMAPro/src/main/res/values-ar-rSA/strings.xml new file mode 100644 index 00000000000..6ceb657187b --- /dev/null +++ b/android/KMAPro/kMAPro/src/main/res/values-ar-rSA/strings.xml @@ -0,0 +1,168 @@ + + + + + مشاركة + + متصفح ويب + + حجم النص + + المزيد + + محو النص + + معلومات + + الإعدادات + + تنزيل التحديثات + + إصدار: %1$s + + يتطلب Keyman إصدار 57 من Chrome أو أعلى. + + تحديث Chrome + + ابدأ الكتابة هنا… + + + + حجم النص: %1$d + + تكبير حجم النص + + تصغير حجم النص + + Text size slider + + \nسيتم محو كل النص\n + + إبدأ الآن + + اضف لوحة مفاتيح بلغتك + + تمكين Keyman لوحة المفاتيح الأساسية للنظام + + إعداد Keyman لوحة المفاتيح الإفتراضية + + المزيد من المعلومات + + أظهر \"%1$s\" عند البدء + + لتثبيت حزم لوحات المفاتيح، امنح Keyman الإذن لقراءة التخزين الخارجي. + + تم رفض أذونات التخزين. قد يفشل تثبيت حزمة لوحة المفاتيح + Storage permission request failed. Try Keyman Settings - Install from local file + + الإعدادات + + + اللغات المثبتة (%1$d) + اللغات المثبتة (%1$d) + اللغات المثبتة (%1$d) + اللغات المثبتة (%1$d) + اللغات المثبتة (%1$d) + اللغات المثبتة (%1$d) + + + تثبيت لوحة المفاتيح او القاموس + + لغة العرض + + تعديل ارتفاع لوحة المفاتيح + + نص مفتاح المسافة + + لوحة المفاتيح + + اللغة + + اللغة + لوحة المفاتيح + + فارغ + + أظهر اسم لوحة المفاتيح على مفتاح المسافة + + أظهر اسم اللغة على المسافة + + اللغة ولوحة المفاتيح على المسافة + + لا تظهر نص على المسافة + + الاهتزاز عند الكتابة + + إظهار اللافتة دائماً + + سيتم تنفيذها + + عند إيقاف التشغيل، يظهر فقط عند تفعيل النص التنبؤي + + السماح بإرسال تقارير توقف التشغيل المفاجئ عبر الشبك + + عند التشغيل، سيتم إرسال تقارير التوقف المفاجئ + + عند إيقاف التشغيل، لن يتم إرسال التقارير + + تثبيت من keyman.com + + التثبيت من ملف محلي + + التثبيت من جهاز آخر + + اضف لغات للوحة المفاتيح المثبتة + + (من حزمة لوحة المفاتيح) + + اختر حزمة لوحة مفاتيح + + اختر لغة %1$s + + تمت اضافة %1$s إلى %2$s + + كل اللغات مثبتة مسبقا + + اسحب لوحة المفاتيح لتغيير مدى ارتفاعها + + دور الجهاز لضبط الوضع العمودي والوضع الأفقي + + إعادة ضبط الافتراضيات + + ابحث أو اكتب عنوان URL + + علامات مرجعية + + لا تتوفر أي علامات مرجعية + + أضف علامة مرجعية + + العنوان + + عنوان Url + + فشل في تنزيل حزمة %1$s + + يتم الآن تحميل لوحة مفاتيح حزمة \n%1$s… + + فشل في الإستخراج + + تثبيت لوحة المفاتيح + + تثبيت القاموس + + ليس %1$s ملف حزمة صالحة لبرنامج Keyman.\n%2$s\" + + ليس في حزمة لوحة المفاتيح لوحات مفاتيح مكيّفة لشاشات اللمس + + لا يوجد نصوص تنبؤية للتنزيل + + لا لوحات مفاتيح و نصوص تنبؤية للتنزيل + + ليس لحزمة لوحة المفاتيح لغات ذات صلة لتثبيتها + + غير صالح\ناقص البيانات الوصفية في الحزمة + + تتطلب لوحة المفاتيح إصدارًا أحدث من Keyman + + غير قادر على تشغيل متصفح الويب + diff --git a/android/KMAPro/kMAPro/src/main/res/values/dimens.xml b/android/KMAPro/kMAPro/src/main/res/values/dimens.xml index cfcc2f4917f..60e89def04b 100644 --- a/android/KMAPro/kMAPro/src/main/res/values/dimens.xml +++ b/android/KMAPro/kMAPro/src/main/res/values/dimens.xml @@ -23,4 +23,5 @@ 32dp 16dp 0dp + 24sp diff --git a/android/KMAPro/kMAPro/src/main/res/values/strings.xml b/android/KMAPro/kMAPro/src/main/res/values/strings.xml index 3cec4f2558c..a1f9e7240d7 100644 --- a/android/KMAPro/kMAPro/src/main/res/values/strings.xml +++ b/android/KMAPro/kMAPro/src/main/res/values/strings.xml @@ -113,6 +113,9 @@ Adjust keyboard height + + Adjust longpress delay + Spacebar caption @@ -198,6 +201,17 @@ Reset to Defaults + + Delay Time: %1$.1f seconds + + + Delay time longer + + + Delay time shorter + + + Longpress delay time slider Search or type URL diff --git a/android/KMEA/app/src/main/assets/android-host.js b/android/KMEA/app/src/main/assets/android-host.js index 9c786e17770..62d396aae0b 100644 --- a/android/KMEA/app/src/main/assets/android-host.js +++ b/android/KMEA/app/src/main/assets/android-host.js @@ -112,6 +112,17 @@ function notifyHost(event, params) { }, 10); } +// Update the KeymanWeb longpress delay +// delay is in milliseconds +function setLongpressDelay(delay) { + if (keyman.osk) { + keyman.osk.gestureParams.longpress.waitLength = delay; + console.debug('setLongpressDelay('+delay+')'); + } else { + window.console.log('setLongpressDelay error: keyman.osk undefined'); + } +} + // Update the KMW banner height // h is in dpi (different from iOS) function setBannerHeight(h) { diff --git a/android/KMEA/app/src/main/java/com/keyman/engine/DisplayLanguages.java b/android/KMEA/app/src/main/java/com/keyman/engine/DisplayLanguages.java index 6a159523a2e..4783e401b8e 100644 --- a/android/KMEA/app/src/main/java/com/keyman/engine/DisplayLanguages.java +++ b/android/KMEA/app/src/main/java/com/keyman/engine/DisplayLanguages.java @@ -40,6 +40,7 @@ public static final DisplayLanguageType[] getDisplayLanguages(Context context) { DisplayLanguageType[] languages = { new DisplayLanguageType(unspecifiedLocale, context.getString(R.string.default_locale)), new DisplayLanguageType("am-ET", "አማርኛ (Amharic)"), + new DisplayLanguageType("ar-SA", "(Arabic) العربية"), new DisplayLanguageType("az-AZ", "Azərbaycanca (Azəricə)"), new DisplayLanguageType("bwr-NG", "Bura-Pabir"), new DisplayLanguageType("cs-CZ", "čeština (Czech)"), diff --git a/android/KMEA/app/src/main/java/com/keyman/engine/KMKeyboardJSHandler.java b/android/KMEA/app/src/main/java/com/keyman/engine/KMKeyboardJSHandler.java index 926ef094f74..d403a29235b 100644 --- a/android/KMEA/app/src/main/java/com/keyman/engine/KMKeyboardJSHandler.java +++ b/android/KMEA/app/src/main/java/com/keyman/engine/KMKeyboardJSHandler.java @@ -20,6 +20,7 @@ import static android.content.Context.VIBRATOR_SERVICE; +import com.keyman.engine.KMManager.EnterModeType; import com.keyman.engine.KMManager.KeyboardType; import com.keyman.engine.data.Keyboard; import com.keyman.engine.util.CharSequenceUtil; @@ -153,7 +154,53 @@ public void run() { } if (s.length() > 0 && s.charAt(0) == '\n') { - keyDownUp(KeyEvent.KEYCODE_ENTER, 0); + if (k.keyboardType == KeyboardType.KEYBOARD_TYPE_SYSTEM) { + // Special handling of ENTER key + switch (KMManager.enterMode) { + // Go action + case GO : + ic.performEditorAction(EditorInfo.IME_ACTION_GO); + break; + + // Search action + case SEARCH : + ic.performEditorAction(EditorInfo.IME_ACTION_SEARCH); + break; + + // Send action + case SEND : + ic.performEditorAction(EditorInfo.IME_ACTION_SEND); + break; + + // Next action + case NEXT : + ic.performEditorAction(EditorInfo.IME_ACTION_NEXT); + break; + + // Done action + case DONE : + ic.performEditorAction(EditorInfo.IME_ACTION_DONE); + break; + + // Previous action + case PREVIOUS : + ic.performEditorAction(EditorInfo.IME_ACTION_PREVIOUS); + break; + + // Messaging apps + case NEWLINE : + // Send newline and advance cursor + ic.commitText("\n", 1); + break; + + // Default ENTER action + default: + keyDownUp(KeyEvent.KEYCODE_ENTER, 0); + } + } else { + // In-app keyboard uses default ENTER action + keyDownUp(KeyEvent.KEYCODE_ENTER, 0); + } ic.endBatchEdit(); return; } diff --git a/android/KMEA/app/src/main/java/com/keyman/engine/KMManager.java b/android/KMEA/app/src/main/java/com/keyman/engine/KMManager.java index aa1c43626a7..b921ac6c0ad 100644 --- a/android/KMEA/app/src/main/java/com/keyman/engine/KMManager.java +++ b/android/KMEA/app/src/main/java/com/keyman/engine/KMManager.java @@ -25,7 +25,6 @@ import android.content.pm.PackageManager; import android.content.res.AssetManager; import android.content.res.Configuration; -import android.content.res.Resources; import android.graphics.Point; import android.graphics.Typeface; import android.inputmethodservice.InputMethodService; @@ -180,6 +179,19 @@ public String toString() { } } + // Enum for how the System Keyboard ENTER key is handled for the EditorInfo action + // Reference: https://developer.android.com/reference/android/view/inputmethod/EditorInfo#summary + public enum EnterModeType { + GO, // Go action + SEARCH, // Search action + SEND, // Send action + NEXT, // Next action + DONE, // Done action + PREVIOUS, // Previous action + NEWLINE, // Send newline character + DEFAULT, // Default ENTER action + } + protected static InputMethodService IMService; private static boolean debugMode = false; @@ -218,6 +230,9 @@ public String toString() { // regardless what the Settings preference is. private static boolean mayPredictOverride = false; + // Determine how system keyboard handles ENTER key + public static EnterModeType enterMode = EnterModeType.DEFAULT; + // Boolean for whether a keyboard can send embedded KMW crash reports to Sentry // When maySendCrashReport is false, KMW will still attempt to send crash reports, but it // will be blocked. @@ -248,6 +263,8 @@ public String toString() { public static final String KMKey_KeyboardHeightPortrait = "keyboardHeightPortrait"; public static final String KMKey_KeyboardHeightLandscape = "keyboardHeightLandscape"; + public static final String KMKey_LongpressDelay = "longpressDelay"; + public static final String KMKey_CustomHelpLink = "CustomHelpLink"; public static final String KMKey_KMPLink = "kmp"; public static final String KMKey_UserKeyboardIndex = "UserKeyboardIndex"; @@ -293,6 +310,9 @@ public String toString() { public static final String KMDefault_DictionaryVersion = "0.1.4"; public static final String KMDefault_DictionaryKMP = KMDefault_DictionaryPackageID + FileUtils.MODELPACKAGE; + // Default KeymanWeb longpress delay in milliseconds + public static final int KMDefault_LongpressDelay = 500; + // Keyman files protected static final String KMFilename_KeyboardHtml = "keyboard.html"; protected static final String KMFilename_JSEngine = "keymanweb-webview.js"; @@ -1251,6 +1271,61 @@ public boolean accept(File pathname) { } } + /** + * Sets enterMode which specifies how the System keyboard ENTER key is handled + * + * @param imeOptions EditorInfo.imeOptions + * @param inputType InputType + */ + public static void setEnterMode(int imeOptions, int inputType) { + if ((inputType & InputType.TYPE_TEXT_FLAG_MULTI_LINE) != 0) { + enterMode = EnterModeType.NEWLINE; + return; + } + + int imeActions = imeOptions & EditorInfo.IME_MASK_ACTION; + EnterModeType value = EnterModeType.DEFAULT; + + switch (imeActions) { + case EditorInfo.IME_ACTION_GO: + value = EnterModeType.GO; + break; + + case EditorInfo.IME_ACTION_SEARCH: + value = EnterModeType.SEARCH; + break; + + case EditorInfo.IME_ACTION_SEND: + value = EnterModeType.SEND; + break; + + case EditorInfo.IME_ACTION_NEXT: + value = EnterModeType.NEXT; + break; + + case EditorInfo.IME_ACTION_DONE: + value = EnterModeType.DONE; + break; + + case EditorInfo.IME_ACTION_PREVIOUS: + value = EnterModeType.PREVIOUS; + break; + + default: + value = EnterModeType.DEFAULT; + } + + enterMode = value; + } + + /** + * Get the value of enterMode + * @return EnterModeType + */ + public static EnterModeType getEnterMode() { + return enterMode; + } + /** * Sets mayPredictOverride true if the InputType field is a hidden password text field * (either TYPE_TEXT_VARIATION_PASSWORD or TYPE_TEXT_VARIATION_WEB_PASSWORD @@ -2010,6 +2085,45 @@ public static int getOrientation(Context context) { return Configuration.ORIENTATION_UNDEFINED; } + /** + * Get the longpress delay (in milliseconds) from stored preference. Defaults to 500ms + * @return int - longpress delay in milliseconds + */ + public static int getLongpressDelay() { + SharedPreferences prefs = appContext.getSharedPreferences( + appContext.getString(R.string.kma_prefs_name), Context.MODE_PRIVATE); + + return prefs.getInt(KMKey_LongpressDelay, KMDefault_LongpressDelay); + } + + /** + * Set the longpress delay (in milliseconds) as a stored preference. + * @param longpressDelay - int longpress delay in milliseconds + */ + public static void setLongpressDelay(int longpressDelay) { + SharedPreferences prefs = appContext.getSharedPreferences( + appContext.getString(R.string.kma_prefs_name), Context.MODE_PRIVATE); + SharedPreferences.Editor editor = prefs.edit(); + editor.putInt(KMKey_LongpressDelay, longpressDelay); + editor.commit(); + } + + /** + * Sends options to the KeymanWeb keyboard. + * 1. number of milliseconds to trigger a longpress gesture. + * This method requires a keyboard to be loaded for the value to take effect. + */ + public static void sendOptionsToKeyboard() { + int longpressDelay = getLongpressDelay(); + if (isKeyboardLoaded(KeyboardType.KEYBOARD_TYPE_INAPP)) { + InAppKeyboard.loadJavascript(KMString.format("setLongpressDelay(%d)", longpressDelay)); + } + + if (SystemKeyboard != null) { + SystemKeyboard.loadJavascript(KMString.format("setLongpressDelay(%d)", longpressDelay)); + } + } + public static int getBannerHeight(Context context) { int bannerHeight = 0; if (InAppKeyboard != null && InAppKeyboard.getBanner() != BannerType.BLANK) { diff --git a/android/KMEA/app/src/main/java/com/keyman/engine/KeyboardInfoActivity.java b/android/KMEA/app/src/main/java/com/keyman/engine/KeyboardInfoActivity.java index c2daaea3030..1897920b731 100644 --- a/android/KMEA/app/src/main/java/com/keyman/engine/KeyboardInfoActivity.java +++ b/android/KMEA/app/src/main/java/com/keyman/engine/KeyboardInfoActivity.java @@ -88,7 +88,7 @@ public void onCreate(Bundle savedInstanceState) { hashMap = new HashMap(); final String customHelpLink = kbd.getHelpLink(); // Check if app declared FileProvider - String icon = String.valueOf(R.drawable.ic_arrow_forward); + String icon = String.valueOf(R.drawable.ic_action_forward); // Don't show help link arrow if it's a local help file and File Provider unavailable, // or custom help doesn't exist if ( (customHelpLink != null && ! KMManager.isTestMode() && diff --git a/android/KMEA/app/src/main/java/com/keyman/engine/ModelInfoActivity.java b/android/KMEA/app/src/main/java/com/keyman/engine/ModelInfoActivity.java index ef1b180c52f..ce81927699a 100644 --- a/android/KMEA/app/src/main/java/com/keyman/engine/ModelInfoActivity.java +++ b/android/KMEA/app/src/main/java/com/keyman/engine/ModelInfoActivity.java @@ -81,7 +81,7 @@ public void onCreate(Bundle savedInstanceState) { final String customHelpLink = lm.getHelpLink(); // Check if app declared FileProvider // Currently, model help only available if custom link exists - icon = String.valueOf(R.drawable.ic_arrow_forward); + icon = String.valueOf(R.drawable.ic_action_forward); // Don't show help link arrow if both custom help and File Provider don't exist // TODO: Update this when model help available on help.keyman.com if ( (!customHelpLink.equals("") && !FileProviderUtils.exists(context)) || diff --git a/android/KMEA/app/src/main/java/com/keyman/engine/ModelPickerActivity.java b/android/KMEA/app/src/main/java/com/keyman/engine/ModelPickerActivity.java index 8b74e261a37..4595aae1e70 100644 --- a/android/KMEA/app/src/main/java/com/keyman/engine/ModelPickerActivity.java +++ b/android/KMEA/app/src/main/java/com/keyman/engine/ModelPickerActivity.java @@ -321,7 +321,7 @@ public View getView(int position, View convertView, ViewGroup parent) { // once it has its own backing Dataset instance. // Is this an installed model or not? - holder.imgDetails.setImageResource(R.drawable.ic_arrow_forward); + holder.imgDetails.setImageResource(R.drawable.ic_action_forward); if (KeyboardPickerActivity.containsLexicalModel(context, modelKey)) { holder.imgInstalled.setImageResource(R.drawable.ic_check); } else { diff --git a/android/KMEA/app/src/main/res/drawable-hdpi/ic_arrow_back.png b/android/KMEA/app/src/main/res/drawable-hdpi/ic_arrow_back.png new file mode 100644 index 00000000000..7922c650163 Binary files /dev/null and b/android/KMEA/app/src/main/res/drawable-hdpi/ic_arrow_back.png differ diff --git a/android/KMEA/app/src/main/res/drawable-mdpi/ic_arrow_back.png b/android/KMEA/app/src/main/res/drawable-mdpi/ic_arrow_back.png new file mode 100644 index 00000000000..0c1eb7a4174 Binary files /dev/null and b/android/KMEA/app/src/main/res/drawable-mdpi/ic_arrow_back.png differ diff --git a/android/KMEA/app/src/main/res/drawable-xhdpi/ic_arrow_back.png b/android/KMEA/app/src/main/res/drawable-xhdpi/ic_arrow_back.png new file mode 100644 index 00000000000..e8e7047ce78 Binary files /dev/null and b/android/KMEA/app/src/main/res/drawable-xhdpi/ic_arrow_back.png differ diff --git a/android/KMEA/app/src/main/res/drawable-xxhdpi/ic_arrow_back.png b/android/KMEA/app/src/main/res/drawable-xxhdpi/ic_arrow_back.png new file mode 100644 index 00000000000..831448cb178 Binary files /dev/null and b/android/KMEA/app/src/main/res/drawable-xxhdpi/ic_arrow_back.png differ diff --git a/android/KMEA/app/src/main/res/drawable-xxxhdpi/ic_arrow_back.png b/android/KMEA/app/src/main/res/drawable-xxxhdpi/ic_arrow_back.png new file mode 100644 index 00000000000..5794fbcaac0 Binary files /dev/null and b/android/KMEA/app/src/main/res/drawable-xxxhdpi/ic_arrow_back.png differ diff --git a/android/KMEA/app/src/main/res/drawable/ic_action_back.xml b/android/KMEA/app/src/main/res/drawable/ic_action_back.xml new file mode 100644 index 00000000000..cae0c5eed49 --- /dev/null +++ b/android/KMEA/app/src/main/res/drawable/ic_action_back.xml @@ -0,0 +1,5 @@ + + diff --git a/android/KMAPro/kMAPro/src/main/res/drawable/ic_action_forward.xml b/android/KMEA/app/src/main/res/drawable/ic_action_forward.xml similarity index 67% rename from android/KMAPro/kMAPro/src/main/res/drawable/ic_action_forward.xml rename to android/KMEA/app/src/main/res/drawable/ic_action_forward.xml index 15efecf5575..2ffd9c4540e 100644 --- a/android/KMAPro/kMAPro/src/main/res/drawable/ic_action_forward.xml +++ b/android/KMEA/app/src/main/res/drawable/ic_action_forward.xml @@ -1,4 +1,5 @@ + android:tint="@android:color/black" + android:autoMirrored="true"/> diff --git a/android/KMEA/app/src/main/res/layout/help_bubble_layout.xml b/android/KMEA/app/src/main/res/layout/help_bubble_layout.xml index 953c74e6924..16e2c3d47a0 100644 --- a/android/KMEA/app/src/main/res/layout/help_bubble_layout.xml +++ b/android/KMEA/app/src/main/res/layout/help_bubble_layout.xml @@ -15,8 +15,8 @@ android:layout_gravity="center" android:gravity="center" android:paddingBottom="10dp" - android:paddingLeft="2dp" - android:paddingRight="2dp" + android:paddingStart="2dp" + android:paddingEnd="2dp" android:text="@string/help_bubble_text" android:textColor="#404040" android:textSize="12sp" /> diff --git a/android/KMEA/app/src/main/res/layout/models_list_row_layout.xml b/android/KMEA/app/src/main/res/layout/models_list_row_layout.xml index ac07621cdd6..a2116bcb4c5 100644 --- a/android/KMEA/app/src/main/res/layout/models_list_row_layout.xml +++ b/android/KMEA/app/src/main/res/layout/models_list_row_layout.xml @@ -44,7 +44,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@android:color/transparent" - android:src="@drawable/ic_arrow_forward" + android:src="@drawable/ic_action_forward" android:layout_alignParentEnd="true" android:layout_centerVertical="true" android:layout_marginEnd="12dp" diff --git a/android/KMEA/app/src/main/res/values-ar-rSA/strings.xml b/android/KMEA/app/src/main/res/values-ar-rSA/strings.xml new file mode 100644 index 00000000000..cb2e9f0d512 --- /dev/null +++ b/android/KMEA/app/src/main/res/values-ar-rSA/strings.xml @@ -0,0 +1,188 @@ + + + + + + + لوحة مفاتيح + لوحات مفاتيح + لوحتا مفاتيح + لوحات مفاتيح + لوحة مفاتيح + لوحة مفاتيح + + + + طريقة إداخل أخرى + طريقة إدخال أخرى + طريقتا إدخال أخريتان + طرق إدخال + طريقة إدخال أخرى + طريقة إدخال أخرى + + + إضافة لوحة مفاتيح جديدة + + اللغات المثبتة + + إعدادات %1$s + + إضافة + + عودة + + إلغاء + + إغلاق + + إغلاق Keyman + + الأمام + + طريقة الإدخال التالية + + تحميل + + تثبيت + + لاحقًا + + التالي + + موافق + + تحديث + + لا يوجد اتصال بالإنترنت + + لا يمكن الاتصال بخادم Keyman! + + هل ترغب بإزالة لوحة المفاتيح هذه؟ + + هل ترغب بتحميل آخر إصدار من لوحة المفاتيح هذه؟ + + هل ترغب بتحديث لوحات المفاتيح والقواميس الآن؟ + + تحديثات الموارد + + تتوفر تحديثات موارد + + %1$s (تحديث متوفر) + + تحديثات متوفرة للوحة مفاتيح %1$s: %2$s + + تحديثات متوفرة لقاموس %1$s: %2$s + + إصدار لوحة المفاتيح + + رابط المساعدة + + إزالة تثبيت لوحة المفاتيح + + [new] %1$s + + امسح هذا الرمز لتحميل \nلوحة المفاتيح هذه على جهاز آخر + + أهلًا بك في %1$s + + مكتبة FileProvider مطلوبة لعرض ملفات المساعدة: %1$s + + خطأ كبير في لوحة مفاتيح مع %1$s:%2$s لأجل لغة %3$s. سيتم تحميل لوحة المفاتيح الأساسية. + + خطأ في لوحة مفاتيح %1$s:%2$s لأجل لغة %3$s. + + يجري التحقق من قاموس ذي صلة للتحميل + لا يمكن الاتصال بخادم Keyman للتحقق من تنزيل القاموس المرتبط + + هل ترغب بتحملي آخر إصدار من هذا القاموس؟ + + لا يوجد قاموس لتحميله + + كتالوغ المصدر غير متوفر + + بدء تحديث الكتالوغ في الخلفية + + الكتالوغ لا يزال يتم تحميله، يرجى المحاولة مجددًا بعد قليل! + + البحث عن مصدر + + بدء تحميل لوحة المفاتيح في الخلفية + + يجري بالفعل تحميل لوحة المفاتيح المختارة، يرجى المحاولة مجددًا بعد قليل! + + اكتمل تحميل لوحة المفاتيح! + + بدأ تحميل القاموس في الخلفية + + يجري بالفعل تحميل القاموس، يرجى المحاولة مجددًا بعد قليل! + + اكتمل تحميل القاموس. + + فشل التحميل + + فشل استرجاع الملف المحمل + + فشل الوصول إلى الخادم! + + "جميع الموارد محدّثة!" + + فشل تحديث واحد أو أكثر من المصادر! + + تم تحديث الموارد بنجاح! + + إصدار القاموس + + إزالة تثبيت القاموس + + هل ترغب بإزالة هذا القاموس؟ + + تم حذف القاموس + + تم تثبيت لوحة مفاتيح %1$s + + تم حذف لوحة المفاتيح + + تفعيل التصحيحات + + تفعيل التوقعات + + القاموس + + Dictionaries + Dictionary + Dictionaries + Dictionaries + Dictionaries + Dictionaries + + + البحث عن قاموس متوفر + تحقق من وجود قواميس على الإنترنت + + قاموس: %1$s + + قواميس %1$s + + + تم تثبيت القاموس + + + (%1$d اوحات) + (لوحة مفاتيح %1$d) + (لوحتا مفاتيح %1$d) + (%1$d لوحة مفاتيح) + (%1$d لوحة مفاتيح) + (%1$d لوحة مفاتيح) + + + اللغة الأصلية + + + + حذف + + + اضغط هنا لتغيير لوحة المفاتيح + + غير قادر على تشغيل متصفح الويب + diff --git a/common/models/templates/src/tokenization.ts b/common/models/templates/src/tokenization.ts index 46290a455d4..2fb07da357b 100644 --- a/common/models/templates/src/tokenization.ts +++ b/common/models/templates/src/tokenization.ts @@ -61,20 +61,21 @@ export function tokenize( let currentIndex = 0; while(leftSpans.length > 0) { const nextSpan = leftSpans[0]; - if(nextSpan.start != currentIndex) { + if(Math.max(nextSpan.start, currentIndex) != currentIndex) { + const nextIndex = Math.max(currentIndex, nextSpan.start); // Implicit whitespace span! tokenization.left.push({ - text: context.left!.substring(currentIndex, nextSpan.start), + text: context.left!.substring(currentIndex, nextIndex), isWhitespace: true }); - currentIndex = nextSpan.start; + currentIndex = nextIndex; } else { leftSpans.shift(); // Explicit non-whitespace span. tokenization.left.push({ text: nextSpan.text }); - currentIndex = nextSpan.end; + currentIndex = Math.max(currentIndex, nextSpan.end); } } @@ -84,11 +85,12 @@ export function tokenize( // Note: the default wordbreaker won't need this code, as it emits a `''` // after final whitespace. if(context.left != null && currentIndex != context.left.length) { + const nextIndex = Math.max(currentIndex, context.left!.length); tokenization.left.push({ - text: context.left.substring(currentIndex, context.left!.length), + text: context.left.substring(currentIndex, nextIndex), isWhitespace: true }); - currentIndex = context.left!.length; + currentIndex = nextIndex; } // New step 2: handle any rejoins needed. @@ -134,13 +136,14 @@ export function tokenize( // `caretSplitsToken` check is additional. while(rightSpans.length > 0) { const nextSpan = rightSpans[0]; - if(nextSpan.start != currentIndex) { + if(Math.max(nextSpan.start, currentIndex) != currentIndex) { + const nextIndex = Math.max(currentIndex, nextSpan.start); // Implicit whitespace span! tokenization.right.push({ - text: context.right!.substring(currentIndex, nextSpan.start), + text: context.right!.substring(currentIndex, nextIndex), isWhitespace: true }); - currentIndex = nextSpan.start; + currentIndex = nextIndex; } else { const leftTail = tokenization.left[leftTokenCount-1]; if(leftTail) { @@ -159,7 +162,7 @@ export function tokenize( tokenization.right.push({ text: nextSpan.text }); - currentIndex = nextSpan.end; + currentIndex = Math.max(currentIndex, nextSpan.end); } // We've always processed the "first right token" after the first iteration. @@ -176,11 +179,12 @@ export function tokenize( // Also note: is pretty much WET with the similar check after the // leftSpan loop. if(context.right && currentIndex != context.right.length) { + const nextIndex = Math.max(currentIndex, context.right!.length); tokenization.right.push({ - text: context.right.substring(currentIndex, context.right!.length), + text: context.right.substring(currentIndex, nextIndex), isWhitespace: true }); - currentIndex = context.right!.length; + currentIndex = nextIndex; } return tokenization; diff --git a/common/models/templates/test/custom-breakers.def.js b/common/models/templates/test/custom-breakers.def.js new file mode 100644 index 00000000000..c907467ce02 --- /dev/null +++ b/common/models/templates/test/custom-breakers.def.js @@ -0,0 +1,128 @@ +/** + * The custom wordbreaker used by the sil.km.gcc model as of + * https://github.com/keymanapp/lexical-models/pull/265. + * @type {WordBreakingFunction} + * */ +export function customWordBreakerProper(str) { + const whitespaceRegex = /\s|\u200b|\n|\r/; + const tokens = str.split(whitespaceRegex); + + for(let i=0; i < tokens.length; i++) { + const token = tokens[i]; + if(token.length == 0) { + tokens.splice(i, 1); + i--; + continue; + } else if(token.length == 1 && whitespaceRegex.test(token)) { + tokens.splice(i, 1); + i--; + continue; + } + + // Certain punctuation marks should be considered a separate token from the word they're next to. + const punctuationMarks = ['«', '»', '$', '#' /* add extras here */]; + const punctSplitIndices = []; + + // Find if and where each mark exists within the token + for(let i = 0; i < punctuationMarks.length; i++) { + const split = token.indexOf(punctuationMarks[i]); + if(split >= 0) { + punctSplitIndices.push(split); + } + } + + // Sort and pick the earliest mark's location. If none exists, use -1. + punctSplitIndices.sort(); + const splitPoint = punctSplitIndices[0] === undefined ? -1 : punctSplitIndices[0]; + + if(splitPoint > -1) { + const left = token.substring(0, splitPoint); // (0, -1) => '' + const punct = token.substring(splitPoint, splitPoint+1); + const right = token.substring(splitPoint+1); // Starting past the end of the string => '' + + if(left) { + tokens.splice(i++, 0, left); + } + tokens.splice(i++, 1, punct); + if(right) { + tokens.splice(i, 0, right); + } + // Ensure that the next iteration puts `i` immediately after the punctuation token... even if + // there was a `right` portion, as it may have extra marks that also need to be spun off. + i--; + } + } + + let latestIndex = 0; + return tokens.map(function(token) { + const start = str.indexOf(token, latestIndex); + latestIndex = start + token.length; + return { + left: start, + start: start, + right: start + token.length, + end: start + token.length, + length: token.length, + text: token + } + }); +} + +/** + * The version of the custom wordbreaker used by the sil.km.gcc model + * before https://github.com/keymanapp/lexical-models/pull/265, which + * triggered #12200. + * @type {WordBreakingFunction} + * */ +export function customWordBreakerFormer (str) { + const tokens = str.split(/\s|\u200b/); + + for(let i=0; i < tokens.length; i++) { + const token = tokens[i]; + if(token.length == 1) { + continue; + } + + // Certain punctuation marks should be considered a separate token from the word they're next to. + const punctuationMarks = ['«', '»', '$', '#' /* add extras here */]; + const punctSplitIndices = []; + + // Find if and where each mark exists within the token + for(let i = 0; i < punctuationMarks.length; i++) { + const split = token.indexOf(punctuationMarks[i]); + if(split >= 0) { + punctSplitIndices.push(split); + } + } + + // Sort and pick the earliest mark's location. If none exists, use -1. + punctSplitIndices.sort(); + const splitPoint = punctSplitIndices[0] === undefined ? -1 : punctSplitIndices[0]; + + if(splitPoint > -1) { + const left = token.substring(0, splitPoint); // (0, -1) => '' + const punct = token.substring(splitPoint, splitPoint+1); + const right = token.substring(splitPoint+1); // Starting past the end of the string => '' + + if(left) { + tokens.splice(i++, 0, left); + } + tokens.splice(i++, 1, punct); + if(right) { + tokens.splice(i, 0, right); + } + // Ensure that the next iteration puts `i` immediately after the punctuation token... even if + // there was a `right` portion, as it may have extra marks that also need to be spun off. + i--; + } + } + return tokens.map(function(token) { + return { + left: str.indexOf(token), + start: str.indexOf(token), + right: str.indexOf(token) + token.length, + end: str.indexOf(token) + token.length, + text: token + } + }); +} \ No newline at end of file diff --git a/common/models/templates/test/test-tokenization.js b/common/models/templates/test/test-tokenization.js index 2a5ec136f69..d8f27197a27 100644 --- a/common/models/templates/test/test-tokenization.js +++ b/common/models/templates/test/test-tokenization.js @@ -5,6 +5,7 @@ import { assert } from 'chai'; import * as models from "@keymanapp/models-templates"; import * as wordBreakers from "@keymanapp/models-wordbreakers"; +import { customWordBreakerFormer, customWordBreakerProper } from './custom-breakers.def.js'; function asProcessedToken(text) { // default wordbreaker emits these at the end of each context half if ending with whitespace. @@ -490,6 +491,87 @@ describe('Tokenization functions', function() { caretSplitsToken: true }); }); + + it('mitigates effects of previously-distributed malformed wordbreaker output', function () { + const text = 'the quick brown fox jumped over the lazy dog '; + /** @type { Context } */ + const context = { + left: text, + right: '', + startOfBuffer: true, + endOfBuffer: true + } + + const tokenized = models.tokenize(customWordBreakerFormer, context); + + // Mitigation aims to prevent the _worst_ side-effects that can result from invalidating the + // underlying assumption of a monotonically-increasing index within the context - + // assigning repeated or blank entries the text that preceded them! + assert.notExists(tokenized.left.find((token) => token.text == text)); + assert.notExists(tokenized.left.find((token) => token.text.startsWith(text.substring(0, 25)))); + + // 'the' appears twice in the context, which should result in two separate 'the' tokens here. + // This was improperly handled when we didn't check that assumption. + assert.equal(tokenized.left.filter((token) => token.text == 'the').length, 2); + + // Does not address multiple blank-token ('') entries that result from intervening spaces; + // that would add too much extra complexity to the method... and it can already be + // handled decently by the predictive-text engine. + assert.deepEqual( + tokenized.left + .filter((entry) => !entry.isWhitespace) + .filter((entry) => entry.text != '') + .map((entry) => entry.text), + ['the', 'quick', 'brown', 'fox', 'jumped', 'over', 'the', 'lazy', 'dog'] + ); + }); + + it('properly works with well-formed custom wordbreaker output', function () { + const text = 'the quick brown fox jumped over the lazy dog '; + /** @type { Context } */ + const context = { + left: text, + right: '', + startOfBuffer: true, + endOfBuffer: true + } + + const tokenized = models.tokenize(customWordBreakerProper, context); + + // Easier-to-parse version + assert.deepEqual( + tokenized.left + .filter((entry) => !entry.isWhitespace) + .map((entry) => entry.text), + ['the', 'quick', 'brown', 'fox', 'jumped', 'over', 'the', 'lazy', 'dog'] + ); + + // This time, with whitespaces. + assert.deepEqual( + tokenized.left.map((entry) => entry.text), [ + 'the', + ' ', + 'quick', + ' ', + 'brown', + ' ', + 'fox', + ' ', + 'jumped', + ' ', + 'over', + ' ', + 'the', + ' ', + 'lazy', + ' ', + 'dog', + ' ' + ] + ); + }); + + // }); describe('getLastPreCaretToken', function() { diff --git a/common/predictive-text/.build-builder b/common/predictive-text/.build-builder deleted file mode 100644 index 4e15741a629..00000000000 --- a/common/predictive-text/.build-builder +++ /dev/null @@ -1 +0,0 @@ -The presence of this file tells CI to use the new builder_ style parameters for build.sh and unit_tests/test.sh. \ No newline at end of file diff --git a/common/web/build.sh b/common/web/build.sh index 554b1e8ebb3..fbc015613fd 100755 --- a/common/web/build.sh +++ b/common/web/build.sh @@ -10,7 +10,6 @@ THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" # # TODO: future modules may include -# :lm-message-types \ # :sentry-manager \ # diff --git a/common/web/keyboard-processor/build.sh b/common/web/keyboard-processor/build.sh deleted file mode 100755 index 3921063bb93..00000000000 --- a/common/web/keyboard-processor/build.sh +++ /dev/null @@ -1,95 +0,0 @@ -#!/usr/bin/env bash -# -# Compile KeymanWeb's 'keyboard-processor' module, one of the components of Web's 'core' module. -# -## START STANDARD BUILD SCRIPT INCLUDE -# adjust relative paths as necessary -THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../../resources/build/builder.inc.sh" -## END STANDARD BUILD SCRIPT INCLUDE - -. "${KEYMAN_ROOT}/resources/shellHelperFunctions.sh" - -BUNDLE_CMD="node ${KEYMAN_ROOT}/common/web/es-bundling/build/common-bundle.mjs" - -################################ Main script ################################ - -builder_describe \ - "Compiles the web-oriented utility function module." \ - "@/web/src/tools/testing/recorder-core test" \ - "@/common/web/keyman-version" \ - "@/common/web/es-bundling" \ - "@/common/web/types" \ - "@/common/web/utils" \ - configure \ - clean \ - build \ - test \ - "--ci For use with action $(builder_term test) - emits CI-friendly test reports" - -builder_describe_outputs \ - configure /node_modules \ - build /common/web/keyboard-processor/build/lib/index.mjs - -builder_parse "$@" - -function do_configure() { - verify_npm_setup - - # Configure Web browser-engine testing environments. As is, this should only - # make changes when we update the dependency, even on our CI build agents. - playwright install -} - -function do_build() { - tsc --build "${THIS_SCRIPT_PATH}/tsconfig.all.json" - - # Base product - the main keyboard processor - builder_echo "Bundle base product - the main keyboard processor" - ${BUNDLE_CMD} "${KEYMAN_ROOT}/common/web/keyboard-processor/build/obj/index.js" \ - --out "${KEYMAN_ROOT}/common/web/keyboard-processor/build/lib/index.mjs" \ - --format esm - - # The DOM-oriented keyboard loader - builder_echo "Bundle the DOM-oriented keyboard loader" - ${BUNDLE_CMD} "${KEYMAN_ROOT}/common/web/keyboard-processor/build/obj/keyboards/loaders/dom-keyboard-loader.js" \ - --out "${KEYMAN_ROOT}/common/web/keyboard-processor/build/lib/dom-keyboard-loader.mjs" \ - --format esm - - # The Node-oriented keyboard loader - builder_echo "Bundle the Node-oriented keyboard loader" - ${BUNDLE_CMD} "${KEYMAN_ROOT}/common/web/keyboard-processor/build/obj/keyboards/loaders/node-keyboard-loader.js" \ - --out "${KEYMAN_ROOT}/common/web/keyboard-processor/build/lib/node-keyboard-loader.mjs" \ - --format esm \ - --platform node - - # Tests - builder_echo "Bundle tests" - ${BUNDLE_CMD} "${KEYMAN_ROOT}/common/web/keyboard-processor/build/tests/dom/cases/domKeyboardLoader.spec.js" \ - --out "${KEYMAN_ROOT}/common/web/keyboard-processor/build/tests/dom/domKeyboardLoader.spec.mjs" \ - --format esm - - # Declaration bundling. - builder_echo "Declaration bundling" - tsc --emitDeclarationOnly --outFile ./build/lib/index.d.ts - tsc --emitDeclarationOnly --outFile ./build/lib/dom-keyboard-loader.d.ts -p src/keyboards/loaders/tsconfig.dom.json - tsc --emitDeclarationOnly --outFile ./build/lib/node-keyboard-loader.d.ts -p src/keyboards/loaders/tsconfig.node.json -} - -function do_test() { - local MOCHA_FLAGS= - local WTR_CONFIG= - if builder_has_option --ci; then - echo "Replacing user-friendly test reports with CI-friendly versions." - MOCHA_FLAGS="$MOCHA_FLAGS --reporter mocha-teamcity-reporter" - WTR_CONFIG=.CI - fi - - c8 mocha --recursive $MOCHA_FLAGS ./tests/node/ - web-test-runner --config tests/dom/web-test-runner${WTR_CONFIG}.config.mjs -} - -builder_run_action configure do_configure -builder_run_action clean rm -rf ./build -builder_run_action build do_build -builder_run_action test do_test diff --git a/common/web/keyboard-processor/package.json b/common/web/keyboard-processor/package.json deleted file mode 100644 index 3f0a5958970..00000000000 --- a/common/web/keyboard-processor/package.json +++ /dev/null @@ -1,69 +0,0 @@ -{ - "name": "@keymanapp/keyboard-processor", - "description": "Core module for Keyman keyboard support in KeymanWeb.", - "repository": { - "type": "git", - "url": "git+https://github.com/keymanapp/keyman.git" - }, - "keywords": [ - "input", - "languages", - "keyboards" - ], - "author": "SIL International", - "license": "MIT", - "bugs": { - "url": "https://github.com/keymanapp/keyman/issues" - }, - "homepage": "https://github.com/keymanapp/keyman#readme", - "devDependencies": { - "@keymanapp/resources-gosh": "*", - "c8": "^7.12.0", - "mocha": "^10.0.0", - "mocha-teamcity-reporter": "^4.0.0", - "typescript": "^5.4.5" - }, - "scripts": { - "build": "gosh build.sh", - "clean": "gosh build.sh clean", - "test": "gosh build.sh test" - }, - "dependencies": { - "@keymanapp/common-types": "*", - "@keymanapp/models-types": "*", - "@keymanapp/keyman-version": "*", - "@keymanapp/web-utils": "*" - }, - "type": "module", - "main": "./build/obj/index.js", - "types": "./build/obj/index.d.ts", - "exports": { - ".": { - "es6-bundling": "./src/index.ts", - "default": "./build/obj/index.js" - }, - "./node-keyboard-loader": { - "es6-bundling": "./src/keyboards/loaders/node-keyboard-loader.ts", - "types": "./build/obj/keyboards/loaders/node-keyboard-loader.d.ts", - "import": "./build/obj/keyboards/loaders/node-keyboard-loader.js" - }, - "./dom-keyboard-loader": { - "es6-bundling": "./src/keyboards/loaders/dom-keyboard-loader.ts", - "types": "./build/obj/keyboards/loaders/dom-keyboard-loader.d.ts", - "import": "./build/obj/keyboards/loaders/dom-keyboard-loader.js" - }, - "./lib": { - "types": "./build/lib/index.d.ts", - "import": "./build/lib/index.mjs" - }, - "./lib/node-keyboard-loader": { - "types": "./build/lib/node-keyboard-loader.d.ts", - "import": "./build/lib/node-keyboard-loader.mjs" - }, - "./lib/dom-keyboard-loader": { - "types": "./build/lib/dom-keyboard-loader.d.ts", - "import": "./build/lib/dom-keyboard-loader.mjs" - }, - "./obj/*.js": "./build/obj/*.js" - } -} diff --git a/common/web/keyboard-processor/src/keyboards/loaders/tsconfig.dom.json b/common/web/keyboard-processor/src/keyboards/loaders/tsconfig.dom.json deleted file mode 100644 index 7ba2abe026a..00000000000 --- a/common/web/keyboard-processor/src/keyboards/loaders/tsconfig.dom.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "extends": "../../../../tsconfig.kmw-main-base.json", - "compilerOptions": { - "baseUrl": "../../../", - "outDir": "../../../build/obj/keyboards/loaders/", - "tsBuildInfoFile": "../../../build/obj/keyboards/loaders/tsconfig.dom.tsbuildinfo", - "rootDir": "." - }, - "references": [ - { "path": "../../../tsconfig.json" } - ], - "include": ["dom-keyboard-loader.ts", "domKeyboardLoader.ts"], -} diff --git a/common/web/keyboard-processor/src/keyboards/loaders/tsconfig.node.json b/common/web/keyboard-processor/src/keyboards/loaders/tsconfig.node.json deleted file mode 100644 index 0be3f169ad3..00000000000 --- a/common/web/keyboard-processor/src/keyboards/loaders/tsconfig.node.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "extends": "../../../../tsconfig.kmw-main-base.json", - "compilerOptions": { - "types": [ "node" ], - "baseUrl": "../../../", - "outDir": "../../../build/obj/keyboards/loaders/", - "tsBuildInfoFile": "../../../build/obj/keyboards/loaders/tsconfig.node.tsbuildinfo", - "rootDir": "." - }, - "references": [ - { "path": "../../../tsconfig.json" } - ], - "include": ["node-keyboard-loader.ts", "nodeKeyboardLoader.ts"], -} diff --git a/common/web/keyboard-processor/tests/dom/readme.md b/common/web/keyboard-processor/tests/dom/readme.md deleted file mode 100644 index 153b7f4a1e8..00000000000 --- a/common/web/keyboard-processor/tests/dom/readme.md +++ /dev/null @@ -1,5 +0,0 @@ -Automated tests in this subfolder and its children are designed to facilitate simple, browser-independent -unit tests that are DOM-reliant. - -Tests for anything that may reasonably vary depending upon the browser used to run the code should go under -the "integrated" folder instead. \ No newline at end of file diff --git a/common/web/keyboard-processor/tests/dom/web-test-runner.config.mjs b/common/web/keyboard-processor/tests/dom/web-test-runner.config.mjs deleted file mode 100644 index 76651182acb..00000000000 --- a/common/web/keyboard-processor/tests/dom/web-test-runner.config.mjs +++ /dev/null @@ -1,62 +0,0 @@ -// @ts-check -import { devices, playwrightLauncher } from '@web/test-runner-playwright'; -import { esbuildPlugin } from '@web/dev-server-esbuild'; -import { defaultReporter, summaryReporter } from '@web/test-runner'; -import { LauncherWrapper, sessionStabilityReporter } from '@keymanapp/common-test-resources/test-runner-stability-reporter.mjs'; -import { importMapsPlugin } from '@web/dev-server-import-maps'; -import { dirname, resolve } from 'path'; -import { fileURLToPath } from 'url'; - -const dir = dirname(fileURLToPath(import.meta.url)); -const KEYMAN_ROOT = resolve(dir, '../../../../..'); - -/** @type {import('@web/test-runner').TestRunnerConfig} */ -export default { - // debug: true, - browsers: [ - new LauncherWrapper(playwrightLauncher({ product: 'chromium' })), - new LauncherWrapper(playwrightLauncher({ product: 'firefox' })), - new LauncherWrapper(playwrightLauncher({ product: 'webkit', concurrency: 1 })), - ], - concurrency: 10, - nodeResolve: true, - files: [ - 'build/tests/dom/**/*.spec.mjs' - ], - middleware: [ - // Rewrites short-hand paths for test resources, making them fully relative to the repo root. - function rewriteResourcePath(context, next) { - if(context.url.startsWith('/resources/')) { - context.url = '/common/test' + context.url; - } - - return next(); - } - ], - plugins: [ - esbuildPlugin({ts: true, target: 'auto'}), - importMapsPlugin({ - inject: { - importMap: { - // Redirects `eventemitter3` imports to the bundled ESM library. The standard import is an - // ESM wrapper around the CommonJS implementation, and WTR fails when it hits the CommonJS. - imports: { - 'eventemitter3': '/node_modules/eventemitter3/dist/eventemitter3.esm.js' - } - } - } - }) - ], - reporters: [ - summaryReporter({}), /* local-dev mocha-style */ - sessionStabilityReporter({}), - defaultReporter({}) - ], - /* - Un-comment the next two lines for easy interactive debugging; it'll launch the - test page in your preferred browser. - */ - // open: true, - // manual: true, - rootDir: KEYMAN_ROOT -} diff --git a/common/web/keyboard-processor/tests/tsconfig.json b/common/web/keyboard-processor/tests/tsconfig.json deleted file mode 100644 index 0e978545b81..00000000000 --- a/common/web/keyboard-processor/tests/tsconfig.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "extends": "../tsconfig.json", - "compilerOptions": { - "baseUrl": "../", - "outDir": "../build/tests/", - "tsBuildInfoFile": "../build/tests/tsconfig.tsbuildinfo", - "rootDir": "./" - }, - "include": [ "./dom/**/*.ts"], - "exclude": [] -} diff --git a/common/web/keyboard-processor/tsconfig.json b/common/web/keyboard-processor/tsconfig.json deleted file mode 100644 index 728935b74f6..00000000000 --- a/common/web/keyboard-processor/tsconfig.json +++ /dev/null @@ -1,18 +0,0 @@ -// Is used by the keyboard-loader submodules. -{ - "extends": "../tsconfig.kmw-main-base.json", - "compilerOptions": { - "baseUrl": "./", - "outDir": "build/obj/", - "tsBuildInfoFile": "build/obj/tsconfig.tsbuildinfo", - "rootDir": "./src/" - }, - "references": [ - { "path": "../types" }, - { "path": "../../models/types" }, - { "path": "../keyman-version/" }, - { "path": "../utils/" } - ], - "include": [ "./src/**/*.ts"], - "exclude": ["./src/keyboards/loaders/**/*.ts"] -} diff --git a/common/web/lm-message-types/tsconfig.json b/common/web/lm-message-types/tsconfig.json deleted file mode 100644 index 559b764980e..00000000000 --- a/common/web/lm-message-types/tsconfig.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "extends": "../../../tsconfig.base.json", - "compilerOptions": { - "declaration": true, - "outDir": "build/", - "sourceMap": true, - "lib": ["es6"], - "target": "es6" - }, - "include": ["./*.ts"], - "exclude": ["test.ts"] -} diff --git a/common/web/lm-worker/src/test/test-runner/web-test-runner.CI.config.mjs b/common/web/lm-worker/src/test/test-runner/web-test-runner.CI.config.mjs deleted file mode 100644 index d18a8a9cb09..00000000000 --- a/common/web/lm-worker/src/test/test-runner/web-test-runner.CI.config.mjs +++ /dev/null @@ -1,13 +0,0 @@ -// @ts-check -import BASE_CONFIG from './web-test-runner.config.mjs'; -import teamcityReporter from '@keymanapp/common-test-resources/test-runner-TC-reporter.mjs'; -import { sessionStabilityReporter } from '@keymanapp/common-test-resources/test-runner-stability-reporter.mjs'; - -/** @type {import('@web/test-runner').TestRunnerConfig} */ -export default { - ...BASE_CONFIG, - reporters: [ - teamcityReporter(), /* custom-written, for CI-friendly reports */ - sessionStabilityReporter({ciMode: true}) - ] -} \ No newline at end of file diff --git a/common/web/lm-worker/tsconfig.json b/common/web/lm-worker/tsconfig.json deleted file mode 100644 index f439e969409..00000000000 --- a/common/web/lm-worker/tsconfig.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "extends": "../../models/tsconfig.kmw-worker-base.json", - - "compilerOptions": { - "baseUrl": "./", - "outDir": "build/obj", - "tsBuildInfoFile": "build/obj/tsconfig.tsbuildinfo", - "rootDir": "./src/main", - - // As this one is the one that directly interfaces with the worker (from the inside) - "lib": ["webworker", "es6"], - }, - "references": [ - // types - { "path": "../../models/types" }, - { "path": "../lm-message-types" }, - // modules - { "path": "../keyman-version" }, - { "path": "../utils" }, - { "path": "../../models/templates" }, - { "path": "../../models/wordbreakers" }, - ], - "include": [ - "src/main/**/*.ts" - ] -} diff --git a/common/web/types/src/keyman-touch-layout/keyman-touch-layout-file.ts b/common/web/types/src/keyman-touch-layout/keyman-touch-layout-file.ts index 757316a70a9..786d9213e2b 100644 --- a/common/web/types/src/keyman-touch-layout/keyman-touch-layout-file.ts +++ b/common/web/types/src/keyman-touch-layout/keyman-touch-layout-file.ts @@ -58,7 +58,7 @@ export const PRIVATE_USE_IDS = [ ] as const; /* A map of key field names with values matching the `typeof` the corresponding property - * exists in common/web/keyboard-processor, keyboards/activeLayout.ts. + * exists in /web/src/engine/keyboard/src/keyboards/activeLayout.ts. * * Make sure that when one is updated, the other also is. TS types are compile-time only, * so the run-time-accessible mapping in activeLayout.ts cannot be auto-generated by TS. */ diff --git a/core/cross-mac-arm64.build b/core/cross-mac-arm64.build index 720e9f31007..faf3e6e9c15 100644 --- a/core/cross-mac-arm64.build +++ b/core/cross-mac-arm64.build @@ -5,9 +5,9 @@ cpu = 'arm64' endian = 'little' [binaries] -c = ['clang', '-arch', 'arm64'] -objc = ['clang', '-arch', 'arm64'] -cpp = ['clang++', '-arch', 'arm64'] +c = ['clang', '-arch', 'arm64', '-mmacosx-version-min=10.13'] +objc = ['clang', '-arch', 'arm64', '-mmacosx-version-min=10.13'] +cpp = ['clang++', '-arch', 'arm64', '-mmacosx-version-min=10.13'] ar = 'ar' ld = 'ld' strip = 'strip' diff --git a/core/cross-mac-x86_64.build b/core/cross-mac-x86_64.build index 2ab2c62a600..3378fe11a40 100644 --- a/core/cross-mac-x86_64.build +++ b/core/cross-mac-x86_64.build @@ -5,9 +5,9 @@ cpu = 'x86_64' endian = 'little' [binaries] -c = ['clang', '-arch', 'x86_64'] -objc = ['clang', '-arch', 'x86_64'] -cpp = ['clang++', '-arch', 'x86_64'] +c = ['clang', '-arch', 'x86_64', '-mmacosx-version-min=10.13'] +objc = ['clang', '-arch', 'x86_64', '-mmacosx-version-min=10.13'] +cpp = ['clang++', '-arch', 'x86_64', '-mmacosx-version-min=10.13'] ar = 'ar' ld = 'ld' strip = 'strip' diff --git a/core/wasm.build.linux.in b/core/wasm.build.linux.in index d449dcf94a0..27cb7ba68fe 100644 --- a/core/wasm.build.linux.in +++ b/core/wasm.build.linux.in @@ -1,4 +1,4 @@ [binaries] -c = ['$EMSCRIPTEN_BASE/emcc.py'] -cpp = ['$EMSCRIPTEN_BASE/em++.py'] -ar = ['$EMSCRIPTEN_BASE/emar.py'] \ No newline at end of file +c = ['$EMSCRIPTEN_BASE/emcc'] +cpp = ['$EMSCRIPTEN_BASE/em++'] +ar = ['$EMSCRIPTEN_BASE/emar'] diff --git a/core/wasm.build.mac.in b/core/wasm.build.mac.in index d449dcf94a0..27cb7ba68fe 100644 --- a/core/wasm.build.mac.in +++ b/core/wasm.build.mac.in @@ -1,4 +1,4 @@ [binaries] -c = ['$EMSCRIPTEN_BASE/emcc.py'] -cpp = ['$EMSCRIPTEN_BASE/em++.py'] -ar = ['$EMSCRIPTEN_BASE/emar.py'] \ No newline at end of file +c = ['$EMSCRIPTEN_BASE/emcc'] +cpp = ['$EMSCRIPTEN_BASE/em++'] +ar = ['$EMSCRIPTEN_BASE/emar'] diff --git a/core/wasm.defs.build b/core/wasm.defs.build index 4a598a1e1c1..f41af337a38 100644 --- a/core/wasm.defs.build +++ b/core/wasm.defs.build @@ -1,7 +1,7 @@ [binaries] -c = ['emcc.py'] -cpp = ['em++.py'] -ar = ['emar.py'] +c = ['emcc'] +cpp = ['em++'] +ar = ['emar'] exe_wrapper = 'node' [properties] diff --git a/developer/src/common/web/utils/package.json b/developer/src/common/web/utils/package.json index 488051f8f0e..1ab9daddbf8 100644 --- a/developer/src/common/web/utils/package.json +++ b/developer/src/common/web/utils/package.json @@ -13,7 +13,7 @@ "@keymanapp/common-types": "*", "eventemitter3": "^5.0.0", "restructure": "^3.0.1", - "semver": "^7.5.2", + "semver": "^7.5.4", "sax": ">=0.6.0", "xmlbuilder": "~11.0.0" }, diff --git a/developer/src/kmc-keyboard-info/src/keyboard-info-compiler-messages.ts b/developer/src/kmc-keyboard-info/src/keyboard-info-compiler-messages.ts index 285e272da5b..bf5d989718b 100644 --- a/developer/src/kmc-keyboard-info/src/keyboard-info-compiler-messages.ts +++ b/developer/src/kmc-keyboard-info/src/keyboard-info-compiler-messages.ts @@ -52,8 +52,14 @@ export class KeyboardInfoCompilerMessages { static Error_FontFileCannotBeRead = (o:{filename: string}) => m(this.ERROR_FontFileCannotBeRead, `Font ${def(o.filename)} could not be parsed to extract a font family.`); -static ERROR_FontFileMetaDataIsInvalid = SevError | 0x000F; -static Error_FontFileMetaDataIsInvalid = (o:{filename: string,message:string}) => m(this.ERROR_FontFileMetaDataIsInvalid, + static ERROR_FontFileMetaDataIsInvalid = SevError | 0x000F; + static Error_FontFileMetaDataIsInvalid = (o:{filename: string,message:string}) => m( + this.ERROR_FontFileMetaDataIsInvalid, `Font ${def(o.filename)} meta data invalid: ${def(o.message)}.`); + + static ERROR_DescriptionIsMissing = SevError | 0x0010; + static Error_DescriptionIsMissing = (o:{filename:string}) => m( + this.ERROR_DescriptionIsMissing, + `The Info.Description field in the package ${def(o.filename)} is required, but is missing or empty.`); } diff --git a/developer/src/kmc-keyboard-info/src/keyboard-info-compiler.ts b/developer/src/kmc-keyboard-info/src/keyboard-info-compiler.ts index 74699556988..8e89bb81646 100644 --- a/developer/src/kmc-keyboard-info/src/keyboard-info-compiler.ts +++ b/developer/src/kmc-keyboard-info/src/keyboard-info-compiler.ts @@ -251,6 +251,9 @@ export class KeyboardInfoCompiler implements KeymanCompiler { if(kmpJsonData.info.description?.description) { keyboard_info.description = kmpJsonData.info.description.description.trim(); + } else { + this.callbacks.reportMessage(KeyboardInfoCompilerMessages.Error_DescriptionIsMissing({filename:sources.kpsFilename})); + return null; } // extract the language identifiers from the language metadata arrays for diff --git a/developer/src/kmc-keyboard-info/test/test-keyboard-info-compiler.ts b/developer/src/kmc-keyboard-info/test/test-keyboard-info-compiler.ts index 9b61b8e93d9..99c244be2dd 100644 --- a/developer/src/kmc-keyboard-info/test/test-keyboard-info-compiler.ts +++ b/developer/src/kmc-keyboard-info/test/test-keyboard-info-compiler.ts @@ -835,17 +835,4 @@ describe('keyboard-info-compiler', function () { const result = await compiler['fontSourceToKeyboardInfoFont'](KHMER_ANGKOR_KPS, kmpJsonData, fonts); assert.deepEqual(result, KHMER_ANGKOR_DISPLAY_FONT_INFO); }); - - it('handles missing info.version in a package file', async function() { - const sources = { - ...KHMER_ANGKOR_SOURCES, - kpsFilename: makePathToFixture('missing-info-version-in-kps-11856', 'khmer_angkor.kps') - }; - const compiler = new KeyboardInfoCompiler(); - assert.isTrue(await compiler.init(callbacks, {sources})); - const kpjFilename = KHMER_ANGKOR_KPJ; - const result = await compiler.run(kpjFilename); - const actual = JSON.parse(new TextDecoder().decode(result.artifacts.keyboard_info.data)); - assert.equal(actual.version, '1.0'); - }); }); diff --git a/developer/src/kmc-ldml/package.json b/developer/src/kmc-ldml/package.json index e423b9cd001..6f65eaf0e0a 100644 --- a/developer/src/kmc-ldml/package.json +++ b/developer/src/kmc-ldml/package.json @@ -29,7 +29,7 @@ "@keymanapp/developer-utils": "*", "@keymanapp/kmc-kmn": "*", "@keymanapp/ldml-keyboard-constants": "*", - "semver": "^7.5.2" + "semver": "^7.5.4" }, "devDependencies": { "@keymanapp/developer-test-helpers": "*", diff --git a/developer/src/kmc-ldml/src/compiler/compiler.ts b/developer/src/kmc-ldml/src/compiler/compiler.ts index 77516ab564f..950534e69c8 100644 --- a/developer/src/kmc-ldml/src/compiler/compiler.ts +++ b/developer/src/kmc-ldml/src/compiler/compiler.ts @@ -297,10 +297,14 @@ export class LdmlKeyboardCompiler implements KeymanCompiler { } /** - * Runs any linter steps + * Runs any linter steps, adding hints to the callbacks as needed * @internal + * @returns true unless there was a linter failure. */ private async lint(source: LDMLKeyboardXMLSourceFile, kmx: KMXPlus.KMXPlusFile): Promise { + if (!kmx || !source) { + return false; + } // run each of the linters for (const linter of this.buildLinters(source, kmx)) { if (!await linter.lint()) { @@ -325,12 +329,7 @@ export class LdmlKeyboardCompiler implements KeymanCompiler { } // Run the linters - if (!await this.lint(source, kmx)) { - return false; - } - - // We are valid if we have a keyboard file at this point. - return !!kmx; + return (await this.lint(source, kmx)); } /** diff --git a/developer/src/kmc-model-info/src/model-info-compiler-messages.ts b/developer/src/kmc-model-info/src/model-info-compiler-messages.ts index e033d6c021c..cbf6b57eb90 100644 --- a/developer/src/kmc-model-info/src/model-info-compiler-messages.ts +++ b/developer/src/kmc-model-info/src/model-info-compiler-messages.ts @@ -44,5 +44,10 @@ export class ModelInfoCompilerMessages { static ERROR_NoLicenseFound = SevError | 0x0009; static Error_NoLicenseFound = () => m(this.ERROR_NoLicenseFound, `No license for the model was found. MIT license is required for publication to Keyman lexical-models repository.`); + + static ERROR_DescriptionIsMissing = SevError | 0x000A; + static Error_DescriptionIsMissing = (o:{filename:string}) => m( + this.ERROR_DescriptionIsMissing, + `The Info.Description field in the package ${def(o.filename)} is required, but is missing or empty.`); } diff --git a/developer/src/kmc-model-info/src/model-info-compiler.ts b/developer/src/kmc-model-info/src/model-info-compiler.ts index d4f3d10d435..a5fb62cbcda 100644 --- a/developer/src/kmc-model-info/src/model-info-compiler.ts +++ b/developer/src/kmc-model-info/src/model-info-compiler.ts @@ -204,6 +204,9 @@ export class ModelInfoCompiler implements KeymanCompiler { if(sources.kmpJsonData.info.description?.description) { model_info.description = sources.kmpJsonData.info.description.description.trim(); + } else { + this.callbacks.reportMessage(ModelInfoCompilerMessages.Error_DescriptionIsMissing({filename:sources.kpsFilename})); + return null; } // isRTL -- this is a little bit of a heuristic from a compiled .js diff --git a/developer/src/kmc-package/src/compiler/package-compiler-messages.ts b/developer/src/kmc-package/src/compiler/package-compiler-messages.ts index 154cb2143e7..00d8a380b5e 100644 --- a/developer/src/kmc-package/src/compiler/package-compiler-messages.ts +++ b/developer/src/kmc-package/src/compiler/package-compiler-messages.ts @@ -138,5 +138,11 @@ export class PackageCompilerMessages { static ERROR_InvalidAuthorEmail = SevError | 0x0020; static Error_InvalidAuthorEmail = (o:{email:string}) => m(this.ERROR_InvalidAuthorEmail, `Invalid author email: ${def(o.email)}`); + + static ERROR_PackageFileHasEmptyVersion = SevError | 0x0021; + static Error_PackageFileHasEmptyVersion = () => m( + this.ERROR_PackageFileHasEmptyVersion, + `Package version is not following keyboard version, but the package version field is blank.` + ); } diff --git a/developer/src/kmc-package/src/compiler/package-version-validator.ts b/developer/src/kmc-package/src/compiler/package-version-validator.ts index 1aad375d400..b20e086aec7 100644 --- a/developer/src/kmc-package/src/compiler/package-version-validator.ts +++ b/developer/src/kmc-package/src/compiler/package-version-validator.ts @@ -42,6 +42,11 @@ export class PackageVersionValidator { if(!this.checkFollowKeyboardVersion(kmp)) { return false; } + } else { + if(!kmp.info.version) { + this.callbacks.reportMessage(PackageCompilerMessages.Error_PackageFileHasEmptyVersion()); + return false; + } } if(!kmp.keyboards) { diff --git a/developer/src/kmc-package/test/fixtures/absolute_path/source/absolute_path.kps b/developer/src/kmc-package/test/fixtures/absolute_path/source/absolute_path.kps index e7c4a8b8512..b5a74c06ee1 100644 --- a/developer/src/kmc-package/test/fixtures/absolute_path/source/absolute_path.kps +++ b/developer/src/kmc-package/test/fixtures/absolute_path/source/absolute_path.kps @@ -17,6 +17,7 @@ Absolute Path + 1.0 diff --git a/developer/src/kmc-package/test/fixtures/invalid/error_package_file_has_empty_version.kps b/developer/src/kmc-package/test/fixtures/invalid/error_package_file_has_empty_version.kps new file mode 100644 index 00000000000..386b4e2e03e --- /dev/null +++ b/developer/src/kmc-package/test/fixtures/invalid/error_package_file_has_empty_version.kps @@ -0,0 +1,32 @@ + + + + 15.0.266.0 + 7.0 + + + + Invalid Email Address + © 2019 National Research Council Canada + Eddie Antonio Santos + + + + + basic.kmx + Keyboard Basic + 0 + .kmx + + + + + Basic + basic + 1.0 + + Central Khmer (Khmer, Cambodia) + + + + diff --git a/developer/src/kmc-package/test/test-messages.ts b/developer/src/kmc-package/test/test-messages.ts index e8fdfa842a5..2d8df44eb50 100644 --- a/developer/src/kmc-package/test/test-messages.ts +++ b/developer/src/kmc-package/test/test-messages.ts @@ -236,4 +236,8 @@ describe('PackageCompilerMessages', function () { PackageCompilerMessages.ERROR_InvalidAuthorEmail); }); + it('should generate ERROR_PackageFileHasEmptyVersion if FollowKeyboardVersion is not present and Version is empty', async function() { + await testForMessage(this, ['invalid', 'error_package_file_has_empty_version.kps'], + PackageCompilerMessages.ERROR_PackageFileHasEmptyVersion); + }); }); diff --git a/developer/src/kmc-package/test/test-package-compiler.ts b/developer/src/kmc-package/test/test-package-compiler.ts index 3f0e8d28fd2..7952e2145a2 100644 --- a/developer/src/kmc-package/test/test-package-compiler.ts +++ b/developer/src/kmc-package/test/test-package-compiler.ts @@ -209,6 +209,8 @@ describe('KmpCompiler', function () { kmpJson = kmpCompiler.transformKpsToKmpObject(kpsPath); }); + assert.isNotNull(kmpJson); + await assert.isNull(kmpCompiler.buildKmpFile(kpsPath, kmpJson)); if(debug) callbacks.printMessages(); diff --git a/developer/src/kmcmplib/src/CompilerInterfacesWasm.cpp b/developer/src/kmcmplib/src/CompilerInterfacesWasm.cpp index a857b41dce4..1ddd45dd3f8 100644 --- a/developer/src/kmcmplib/src/CompilerInterfacesWasm.cpp +++ b/developer/src/kmcmplib/src/CompilerInterfacesWasm.cpp @@ -75,9 +75,16 @@ struct BindingType> { using ValBinding = BindingType; using WireType = ValBinding::WireType; +#if __EMSCRIPTEN_major__ == 3 && __EMSCRIPTEN_minor__ == 1 && __EMSCRIPTEN_tiny__ >= 60 + // emscripten-core/emscripten#21692 + static WireType toWireType(const std::vector &vec, rvp::default_tag) { + return ValBinding::toWireType(val::array(vec), rvp::default_tag{}); + } +#else static WireType toWireType(const std::vector &vec) { return ValBinding::toWireType(val::array(vec)); } +#endif static std::vector fromWireType(WireType value) { return vecFromJSArray(ValBinding::fromWireType(value)); diff --git a/developer/src/kmcmplib/wasm.build.linux.in b/developer/src/kmcmplib/wasm.build.linux.in index c5e61a9bda9..27cb7ba68fe 100644 --- a/developer/src/kmcmplib/wasm.build.linux.in +++ b/developer/src/kmcmplib/wasm.build.linux.in @@ -1,4 +1,4 @@ [binaries] -c = ['$EMSCRIPTEN_BASE/emcc.py'] -cpp = ['$EMSCRIPTEN_BASE/em++.py'] -ar = ['$EMSCRIPTEN_BASE/emar.py'] +c = ['$EMSCRIPTEN_BASE/emcc'] +cpp = ['$EMSCRIPTEN_BASE/em++'] +ar = ['$EMSCRIPTEN_BASE/emar'] diff --git a/developer/src/kmcmplib/wasm.build.mac.in b/developer/src/kmcmplib/wasm.build.mac.in index c5e61a9bda9..27cb7ba68fe 100644 --- a/developer/src/kmcmplib/wasm.build.mac.in +++ b/developer/src/kmcmplib/wasm.build.mac.in @@ -1,4 +1,4 @@ [binaries] -c = ['$EMSCRIPTEN_BASE/emcc.py'] -cpp = ['$EMSCRIPTEN_BASE/em++.py'] -ar = ['$EMSCRIPTEN_BASE/emar.py'] +c = ['$EMSCRIPTEN_BASE/emcc'] +cpp = ['$EMSCRIPTEN_BASE/em++'] +ar = ['$EMSCRIPTEN_BASE/emar'] diff --git a/developer/src/server/package.json b/developer/src/server/package.json index b0e438ad15e..f3be3425f2a 100644 --- a/developer/src/server/package.json +++ b/developer/src/server/package.json @@ -18,7 +18,7 @@ "open": "^8.4.0", "restructure": "^3.0.1", "sax": ">=0.6.0", - "semver": "^7.5.2", + "semver": "^7.5.4", "ws": "^8.17.1", "xmlbuilder": "~11.0.0" }, diff --git a/docs/CODEOWNERS b/docs/CODEOWNERS index 7902ce7a682..75530e6bde1 100644 --- a/docs/CODEOWNERS +++ b/docs/CODEOWNERS @@ -3,30 +3,30 @@ # # @darcywong00 @mcdurdin @ermshiperete @rc-swag @SabineSIL @sgschantz -/android/ @darcywong00 @sgschantz +/android/ @darcywong00 @sgschantz -/common/ @mcdurdin @rc-swag -/core/ @mcdurdin @rc-swag -/common/lexical-model-types/ @jahorton @mcdurdin -/common/models/ @jahorton @mcdurdin -/common/predictive-text/ @jahorton @mcdurdin -/common/schemas/ @mcdurdin @jahorton -/common/test/ @mcdurdin @ermshiperete -/common/web/ @jahorton @mcdurdin +/common/ @mcdurdin @rc-swag +/core/ @mcdurdin @rc-swag +/common/lexical-model-types/ @jahorton @mcdurdin +/common/models/ @jahorton @mcdurdin +/common/schemas/ @mcdurdin @jahorton +/common/test/ @mcdurdin @ermshiperete +/common/web/ @jahorton @mcdurdin -/developer/ @mcdurdin @darcywong00 -/docs/ @mcdurdin @jahorton -/ios/ @sgschantz @jahorton -/linux/ @ermshiperete @darcywong00 -/mac/ @sgschantz @SabineSIL +/developer/ @mcdurdin @darcywong00 +/docs/ @mcdurdin @jahorton +/ios/ @sgschantz @jahorton +/linux/ @ermshiperete @darcywong00 +/mac/ @sgschantz @SabineSIL -/oem/firstvoices/android/ @darcywong00 @sgschantz -/oem/firstvoices/common/ @mcdurdin @rc-swag -/oem/firstvoices/ios/ @sgschantz @jahorton -/oem/firstvoices/windows/ @rc-swag @ermshiperete -/resources/ @mcdurdin @jahorton +/oem/firstvoices/android/ @darcywong00 @sgschantz +/oem/firstvoices/common/ @mcdurdin @rc-swag +/oem/firstvoices/ios/ @sgschantz @jahorton +/oem/firstvoices/windows/ @rc-swag @ermshiperete +/resources/ @mcdurdin @jahorton # Web is currently shared between Eberhard and Joshua: -/web/ @ermshiperete @jahorton +/web/ @ermshiperete @jahorton +/web/src/engine/predictive-text/ @jahorton @mcdurdin -/windows/ @rc-swag @ermshiperete +/windows/ @rc-swag @ermshiperete diff --git a/docs/file-formats/kmx-file-format.md b/docs/file-formats/kmx-file-format.md index 6de14df56fe..67288bc123c 100644 --- a/docs/file-formats/kmx-file-format.md +++ b/docs/file-formats/kmx-file-format.md @@ -44,12 +44,14 @@ The very first section is a header that describes the remainder of the file. |44 | 32 | `StartGroup_Unicode` | index of starting Unicode `COMP_GROUP`, `0xFFFFFFFF` means unused | |48 | 32 | `dwFlags` | global flags for the keyboard, see description below | |52 | 32 | `dwHotKey` | default hotkey for keyboard, from [`&Hotkey`], see description below | -|56 | 32 | `dpBitmapOffset` | offset of keyboard icon, `0x0` if not present | +|56 | 32 | `dpBitmapOffset` | offset of keyboard icon | |60 | 32 | `dwBitmapSize` | size in bytes of keyboard icon, `0x0` if not present | This structure is present at the start of every .kmx file. The `KXTS` identifier can be used as a 'magic' to determine whether a binary file is a .kmx file. +Note that `dpBitmapOffset` should be ignored if `dwBitmapSize` is `0x0` -- it may be a non-zero value. + ### `dwFileVersion`: Minimum version of Keyman required to read the file The `dwFileVersion` property should be checked before attempting to read any @@ -220,7 +222,7 @@ file and is never written to the binary format. | ∆ | Bits | Name | Description | |----|------|--------------|------------------------------------------------------------------------------------------------| -| 0 | 16 | `Key` | Keyman virtual key for the rule, 0 if unused | +| 0 | 16 | `Key` | [Keyman virtual key], virtual character key, or character, for the rule, 0 if unused | | 2 | 16 | (unused) | padding, reserved | | 4 | 32 | `Line` | Line number for the rule, 0 if not compiled with debug information | | 8 | 32 | `ShiftFlags` | Modifier flags for the key, see description below | @@ -232,6 +234,58 @@ compiler expands `any()` statements found in the key part of a rule into multiple rules -- so the key part of the rule is always only ever matching a single key combination. +`ShiftFlags` contains a set of modifier and state key flags, and some additional +flags that determine how to interpret `Key`. + +### Modifier and state key flags + +The modifier and state key flags are: + +| Bit mask | Name | Description | +|--------------|------------------|-----------------------------------------------------------| +| `0x00000001` | `LCTRLFLAG` | Left Control key | +| `0x00000002` | `RCTRLFLAG` | Right Control key | +| `0x00000004` | `LALTFLAG` | Left Alt key (Option on macOS) | +| `0x00000008` | `RALTFLAG` | Right Alt key | +| `0x00000010` | `K_SHIFTFLAG` | Either Shift key | +| `0x00000020` | `K_CTRLFLAG` | Either Ctrl key | +| `0x00000040` | `K_ALTFLAG` | Either Alt key | +| `0x00000080` | `K_METAFLAG` | Either Meta key. Reserved, not for use in .kmx | +| `0x00000100` | `CAPITALFLAG` | Caps lock on | +| `0x00000200` | `NOTCAPITALFLAG` | Caps lock NOT on | +| `0x00000400` | `NUMLOCKFLAG` | Num lock on | +| `0x00000800` | `NOTNUMLOCKFLAG` | Num lock NOT on | +| `0x00001000` | `SCROLLFLAG` | Scroll lock on | +| `0x00002000` | `NOTSCROLLFLAG` | Scroll lock NOT on | + +* The chiral (left/right) modifier flags should not be used together with the + non-chiral flags. +* If neither `CAPITALFLAG` nor `NOTCAPITALFLAG` are set, then the state of the + Caps Lock key is ignored, and the same principle applies for Num lock and + Scroll lock. +* The Meta key may be the Windows key on Windows, or the + Command key on macOS. This flag is reserved for internal use, and + is not valid in a .kmx file. + +### Additional flags + +The meaning of `Key` is determined by the flags `ISVIRTUALKEY` and +`VIRTUALCHARKEY` in `ShiftFlags` (mask `0x0000C000`): + +* `0`: `Key` is a character on a key cap (US English unless the keyboard is a + mnemonic layout). All other shift flags are ignord +* `ISVIRTUALKEY`: `Key` is a US English virtual key +* `VIRTUALCHARKEY`: `Key` is a character on a key cap, combined with the shift + flags (US English unless the keyboard is a mnemonic layout) + +| Bit mask | Name | Description | +|--------------|------------------|-----------------------------------------------| +| `0x00004000` | `ISVIRTUALKEY` | `Key` is a virtual key | +| `0x00008000` | `VIRTUALCHARKEY` | Keyman 6.0+: `Key` is a Virtual character key | + +Note that the shift flags for key rules do not use the same values as hotkeys +for historical reasons. + ## Debug information for keyboards When debug information is present, it will be found in four places in compiled diff --git a/linux/scripts/dist.sh b/linux/scripts/dist.sh index 0038034d421..6ef0be7d8bb 100755 --- a/linux/scripts/dist.sh +++ b/linux/scripts/dist.sh @@ -30,28 +30,37 @@ cp -a debian ../ cd .. echo "3.0 (native)" > debian/source/format dch keyman --newversion "${VERSION}" --force-bad-version --nomultimaint -dpkg-source --tar-ignore=*~ --tar-ignore=.git --tar-ignore=.gitattributes \ - --tar-ignore=.gitignore --tar-ignore=experiments --tar-ignore=debian \ - --tar-ignore=.github --tar-ignore=.vscode --tar-ignore=android \ +dpkg-source --tar-ignore=*~ \ + --tar-ignore=.git \ + --tar-ignore=.gitattributes \ + --tar-ignore=.gitignore \ + --tar-ignore=experiments \ + --tar-ignore=debian \ + --tar-ignore=.github \ + --tar-ignore=.vscode \ + --tar-ignore=android \ --tar-ignore=.devcontainer \ --tar-ignore=artifacts \ \ --tar-ignore=common/models \ - --tar-ignore=common/predictive-text \ --tar-ignore=common/resources \ --tar-ignore=common/schemas \ --tar-ignore=common/test/keyboards/build.* \ - --tar-ignore=common/test/predictive-text \ --tar-ignore=common/test/resources \ --tar-ignore=common/web \ --tar-ignore=common/windows \ \ --tar-ignore=core/build \ - --tar-ignore=developer --tar-ignore=docs --tar-ignore=ios \ + --tar-ignore=developer \ + --tar-ignore=docs \ + --tar-ignore=ios \ --tar-ignore=linux/keyman-config/keyman_config/version.py \ - --tar-ignore=linux/keyman-config/buildtools/build-langtags.py --tar-ignore=__pycache__ \ + --tar-ignore=linux/keyman-config/buildtools/build-langtags.py \ + --tar-ignore=__pycache__ \ --tar-ignore=linux/help \ - --tar-ignore=mac --tar-ignore=node_modules --tar-ignore=oem \ + --tar-ignore=mac \ + --tar-ignore=node_modules \ + --tar-ignore=oem \ --tar-ignore=linux/build \ --tar-ignore=linux/builddebs \ --tar-ignore=linux/ibus-keyman/build \ @@ -60,11 +69,17 @@ dpkg-source --tar-ignore=*~ --tar-ignore=.git --tar-ignore=.gitattributes \ --tar-ignore=resources/environment.sh \ --tar-ignore=resources/git-hooks \ --tar-ignore=resources/scopes \ - --tar-ignore=resources/build/*.lua --tar-ignore=resources/build/jq* \ + --tar-ignore=resources/build/*.lua \ + --tar-ignore=resources/build/jq* \ --tar-ignore=results \ --tar-ignore=tmp \ - --tar-ignore=web --tar-ignore=windows --tar-ignore=keyman_1* \ - --tar-ignore=dist --tar-ignore=VERSION -Zgzip -b . + --tar-ignore=web \ + --tar-ignore=windows \ + --tar-ignore=keyman_1* \ + --tar-ignore=dist \ + --tar-ignore=VERSION \ + \ + -Zgzip -b . mv ../keyman_"${VERSION}".tar.gz linux/dist/keyman-"${VERSION}".tar.gz echo "3.0 (quilt)" > debian/source/format cd "$BASEDIR" diff --git a/package-lock.json b/package-lock.json index 90947948310..cee7d8528b7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,9 +25,9 @@ "common/test/resources", "common/tools/*", "common/web/*", - "common/predictive-text", "common/tools/hextobin", - "web" + "web", + "web/src/engine/predictive-text/*" ], "dependencies": { "@keymanapp/common-types": "file:common/web/types", @@ -108,25 +108,6 @@ "typescript": "^5.4.5" } }, - "common/predictive-text": { - "name": "@keymanapp/lexical-model-layer", - "license": "MIT", - "dependencies": { - "@keymanapp/keyman-version": "*", - "@keymanapp/models-templates": "*", - "@keymanapp/models-wordbreakers": "*", - "@keymanapp/web-utils": "*", - "es6-shim": "^0.35.5", - "string.prototype.codepointat": "^0.2.1" - }, - "devDependencies": { - "@keymanapp/models-types": "*", - "@keymanapp/resources-gosh": "*", - "mocha": "^10.0.0", - "mocha-teamcity-reporter": "^4.0.0", - "typescript": "^5.4.5" - } - }, "common/test/resources": { "name": "@keymanapp/common-test-resources", "license": "MIT", @@ -191,23 +172,6 @@ "typescript": "^5.4.5" } }, - "common/web/keyboard-processor": { - "name": "@keymanapp/keyboard-processor", - "license": "MIT", - "dependencies": { - "@keymanapp/common-types": "*", - "@keymanapp/keyman-version": "*", - "@keymanapp/models-types": "*", - "@keymanapp/web-utils": "*" - }, - "devDependencies": { - "@keymanapp/resources-gosh": "*", - "c8": "^7.12.0", - "mocha": "^10.0.0", - "mocha-teamcity-reporter": "^4.0.0", - "typescript": "^5.4.5" - } - }, "common/web/keyman-version": { "name": "@keymanapp/keyman-version", "license": "MIT", @@ -215,36 +179,6 @@ "typescript": "^5.4.5" } }, - "common/web/lm-message-types": { - "name": "@keymanapp/lm-message-types", - "license": "MIT", - "devDependencies": { - "typescript": "^5.4.5" - } - }, - "common/web/lm-worker": { - "name": "@keymanapp/lm-worker", - "license": "MIT", - "dependencies": { - "@keymanapp/keyman-version": "*", - "@keymanapp/models-templates": "*", - "@keymanapp/models-wordbreakers": "*", - "@keymanapp/web-utils": "*", - "es6-shim": "^0.35.5", - "string.prototype.codepointat": "^0.2.1", - "string.prototype.startswith": "^0.2.0" - }, - "devDependencies": { - "@keymanapp/common-test-resources": "*", - "@keymanapp/models-types": "*", - "@keymanapp/resources-gosh": "*", - "c8": "^7.12.0", - "combine-source-map": "^0.8.0", - "mocha": "^10.0.0", - "mocha-teamcity-reporter": "^4.0.0", - "typescript": "^5.4.5" - } - }, "common/web/sentry-manager": { "name": "@keymanapp/web-sentry-manager", "license": "MIT", @@ -2067,12 +2001,12 @@ } }, "node_modules/@75lb/deep-merge": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@75lb/deep-merge/-/deep-merge-1.1.1.tgz", - "integrity": "sha512-xvgv6pkMGBA6GwdyJbNAnDmfAIR/DfWhrj9jgWh3TY7gRm3KO46x/GPjRg6wJ0nOepwqrNxFfojebh0Df4h4Tw==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@75lb/deep-merge/-/deep-merge-1.1.2.tgz", + "integrity": "sha512-08K9ou5VNbheZFxM5tDWoqjA3ImC50DiuuJ2tj1yEPRfkp8lLLg6XAaJ4On+a0yAXor/8ay5gHnAIshRM44Kpw==", "dev": true, "dependencies": { - "lodash.assignwith": "^4.2.0", + "lodash": "^4.17.21", "typical": "^7.1.1" }, "engines": { @@ -2868,10 +2802,6 @@ "resolved": "common/tools/hextobin", "link": true }, - "node_modules/@keymanapp/keyboard-processor": { - "resolved": "common/web/keyboard-processor", - "link": true - }, "node_modules/@keymanapp/keyman-version": { "resolved": "common/web/keyman-version", "link": true @@ -2913,15 +2843,15 @@ "link": true }, "node_modules/@keymanapp/lexical-model-layer": { - "resolved": "common/predictive-text", + "resolved": "web/src/engine/predictive-text/worker-main", "link": true }, "node_modules/@keymanapp/lm-message-types": { - "resolved": "common/web/lm-message-types", + "resolved": "web/src/engine/predictive-text/types", "link": true }, "node_modules/@keymanapp/lm-worker": { - "resolved": "common/web/lm-worker", + "resolved": "web/src/engine/predictive-text/worker-thread", "link": true }, "node_modules/@keymanapp/models-templates": { @@ -10654,12 +10584,6 @@ "dev": true, "license": "MIT" }, - "node_modules/lodash.assignwith": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.assignwith/-/lodash.assignwith-4.2.0.tgz", - "integrity": "sha512-ZznplvbvtjK2gMvnQ1BR/zqPFZmS6jbK4p+6Up4xcRYA7yMIwxHCfbTcrYxXKzzqLsQ05eJPVznEW3tuwV7k1g==", - "dev": true - }, "node_modules/lodash.camelcase": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", @@ -14821,7 +14745,6 @@ "name": "keyman", "license": "MIT", "dependencies": { - "@keymanapp/keyboard-processor": "*", "@keymanapp/keyman-version": "*", "@keymanapp/lexical-model-layer": "*", "@keymanapp/models-types": "*", @@ -14841,11 +14764,59 @@ "mocha": "^10.0.0" } }, + "web/src/engine/predictive-text/types": { + "name": "@keymanapp/lm-message-types", + "license": "MIT", + "devDependencies": { + "typescript": "^5.4.5" + } + }, + "web/src/engine/predictive-text/worker-main": { + "name": "@keymanapp/lexical-model-layer", + "license": "MIT", + "dependencies": { + "@keymanapp/keyman-version": "*", + "@keymanapp/models-templates": "*", + "@keymanapp/models-wordbreakers": "*", + "@keymanapp/web-utils": "*", + "es6-shim": "^0.35.5", + "string.prototype.codepointat": "^0.2.1" + }, + "devDependencies": { + "@keymanapp/models-types": "*", + "@keymanapp/resources-gosh": "*", + "mocha": "^10.0.0", + "mocha-teamcity-reporter": "^4.0.0", + "typescript": "^5.4.5" + } + }, + "web/src/engine/predictive-text/worker-thread": { + "name": "@keymanapp/lm-worker", + "license": "MIT", + "dependencies": { + "@keymanapp/keyman-version": "*", + "@keymanapp/models-templates": "*", + "@keymanapp/models-wordbreakers": "*", + "@keymanapp/web-utils": "*", + "es6-shim": "^0.35.5", + "string.prototype.codepointat": "^0.2.1", + "string.prototype.startswith": "^0.2.0" + }, + "devDependencies": { + "@keymanapp/common-test-resources": "*", + "@keymanapp/models-types": "*", + "@keymanapp/resources-gosh": "*", + "c8": "^7.12.0", + "combine-source-map": "^0.8.0", + "mocha": "^10.0.0", + "mocha-teamcity-reporter": "^4.0.0", + "typescript": "^5.4.5" + } + }, "web/src/tools/testing/recorder-core": { "name": "@keymanapp/recorder-core", "license": "MIT", "dependencies": { - "@keymanapp/keyboard-processor": "*", "@keymanapp/keyman-version": "*", "@keymanapp/models-types": "*", "@keymanapp/web-utils": "*" diff --git a/package.json b/package.json index 638f2b2fad1..3f0012db10c 100644 --- a/package.json +++ b/package.json @@ -50,9 +50,9 @@ "common/test/resources", "common/tools/*", "common/web/*", - "common/predictive-text", "common/tools/hextobin", - "web" + "web", + "web/src/engine/predictive-text/*" ], "dependencies": { "@keymanapp/common-types": "file:common/web/types", diff --git a/resources/build/minimum-versions.inc.sh b/resources/build/minimum-versions.inc.sh index c060d31c58f..97f25094067 100644 --- a/resources/build/minimum-versions.inc.sh +++ b/resources/build/minimum-versions.inc.sh @@ -20,8 +20,7 @@ KEYMAN_MIN_TARGET_VERSION_CHROME=95.0 # Final version that runs on Andro # Dependency versions KEYMAN_MIN_VERSION_NODE_MAJOR=20 # node version source of truth is /package.json:/engines/node KEYMAN_MIN_VERSION_NPM=10.5.1 # 10.5.0 has bug, discussed in #10350 -KEYMAN_MIN_VERSION_EMSCRIPTEN=3.1.44 # Warning: 3.1.45 is bad (#9529); newer versions work -KEYMAN_MAX_VERSION_EMSCRIPTEN=3.1.58 # See #9529 +KEYMAN_MIN_VERSION_EMSCRIPTEN=3.1.58 # Use KEYMAN_USE_EMSDK to automatically update to this version KEYMAN_MIN_VERSION_VISUAL_STUDIO=2019 KEYMAN_MIN_VERSION_MESON=1.0.0 diff --git a/resources/build/version/package.json b/resources/build/version/package.json index da551fef289..ef27eb0fa1d 100644 --- a/resources/build/version/package.json +++ b/resources/build/version/package.json @@ -11,7 +11,7 @@ "devDependencies": { "@types/semver": "^7.1.0", "@types/yargs": "^17.0.26", - "semver": "^7.5.2" + "semver": "^7.5.4" }, "files": [ "src" diff --git a/resources/locate_emscripten.inc.sh b/resources/locate_emscripten.inc.sh index be66adfa32f..f678e8449e6 100644 --- a/resources/locate_emscripten.inc.sh +++ b/resources/locate_emscripten.inc.sh @@ -1,31 +1,73 @@ # shellcheck shell=bash # no hashbang for .inc.sh +. "$KEYMAN_ROOT/resources/build/minimum-versions.inc.sh" + # -# We don't want to rely on emcc.py being on the path, because Emscripten puts far +# We don't want to rely on emcc being on the path, because Emscripten puts far # too many things onto the path (in particular for us, node). # -# The following comment suggests that we don't need emcc.py on the path. +# The following comment suggests that we don't need emcc on the path. # https://github.com/emscripten-core/emscripten/issues/4848#issuecomment-1097357775 # -# So we try and locate emcc.py in common locations ourselves. The search pattern +# So we try and locate emcc in common locations ourselves. The search pattern # is: # # 1. Look for $EMSCRIPTEN_BASE (our primary emscripten variable), which should -# point to the folder that emcc.py is located in -# 2. Look for $EMCC which should point to the emcc.py executable -# 3. Look for emcc.py on the path +# point to the folder that emcc is located in +# 2. Look for $EMCC which should point to the emcc executable +# 3. Look for emcc on the path # locate_emscripten() { + local EMCC_EXECUTABLE + if [[ "${BUILDER_OS}" == "win" ]]; then + EMCC_EXECUTABLE="emcc.py" + else + EMCC_EXECUTABLE="emcc" + fi if [[ -z ${EMSCRIPTEN_BASE+x} ]]; then if [[ -z ${EMCC+x} ]]; then - local EMCC=`which emcc.py` - [[ -z $EMCC ]] && builder_die "locate_emscripten: Could not locate emscripten (emcc.py) on the path or with \$EMCC or \$EMSCRIPTEN_BASE" + local EMCC=$(which ${EMCC_EXECUTABLE}) + [[ -z $EMCC ]] && builder_die "locate_emscripten: Could not locate emscripten (${EMCC_EXECUTABLE}) on the path or with \$EMCC or \$EMSCRIPTEN_BASE" fi - [[ -f $EMCC && ! -x $EMCC ]] && builder_die "locate_emscripten: Variable EMCC ($EMCC) points to emcc.py but it is not executable" - [[ -x $EMCC ]] || builder_die "locate_emscripten: Variable EMCC ($EMCC) does not point to a valid executable emcc.py" + [[ -f $EMCC && ! -x $EMCC ]] && builder_die "locate_emscripten: Variable EMCC ($EMCC) points to ${EMCC_EXECUTABLE} but it is not executable" + [[ -x $EMCC ]] || builder_die "locate_emscripten: Variable EMCC ($EMCC) does not point to a valid executable ${EMCC_EXECUTABLE}" EMSCRIPTEN_BASE="$(dirname "$EMCC")" fi - [[ -f ${EMSCRIPTEN_BASE}/emcc.py && ! -x ${EMSCRIPTEN_BASE}/emcc.py ]] && builder_die "locate_emscripten: Variable EMSCRIPTEN_BASE ($EMSCRIPTEN_BASE) contains emcc.py but it is not executable" - [[ -x ${EMSCRIPTEN_BASE}/emcc.py ]] || builder_die "locate_emscripten: Variable EMSCRIPTEN_BASE ($EMSCRIPTEN_BASE) does not point to emcc.py's folder" + [[ -f ${EMSCRIPTEN_BASE}/${EMCC_EXECUTABLE} && ! -x ${EMSCRIPTEN_BASE}/${EMCC_EXECUTABLE} ]] && builder_die "locate_emscripten: Variable EMSCRIPTEN_BASE ($EMSCRIPTEN_BASE) contains ${EMCC_EXECUTABLE} but it is not executable" + [[ -x ${EMSCRIPTEN_BASE}/${EMCC_EXECUTABLE} ]] || builder_die "locate_emscripten: Variable EMSCRIPTEN_BASE ($EMSCRIPTEN_BASE) does not point to ${EMCC_EXECUTABLE}'s folder" + + verify_emscripten_version +} + +# Ensure that we use correct version of emsdk on build agents. +# For developers, define KEYMAN_USE_SDK to do this on your +# build machine. +verify_emscripten_version() { + if [[ "$VERSION_ENVIRONMENT" != local || ! -z "${KEYMAN_USE_EMSDK+x}" ]]; then + _select_emscripten_version_with_emsdk + fi +} + +# Use emsdk to select the appropriate version of Emscripten +# according to minimum-versions.inc.sh +_select_emscripten_version_with_emsdk() { + if [[ -z "${EMSCRIPTEN_BASE+x}" ]]; then + builder_die "Variable EMSCRIPTEN_BASE must be set" + fi + + if [[ -z "${KEYMAN_MIN_VERSION_EMSCRIPTEN+x}" ]]; then + builder_die "Variable KEYMAN_MIN_VERSION_EMSCRIPTEN must be set" + fi + + pushd "${EMSCRIPTEN_BASE}/../.." > /dev/null + if [[ ! -f emsdk ]]; then + builder_die "emsdk[.bat] should be in $(pwd)" + fi + + export EMSDK_KEEP_DOWNLOADS=1 + git pull + ./emsdk install "$KEYMAN_MIN_VERSION_EMSCRIPTEN" + ./emsdk activate "$KEYMAN_MIN_VERSION_EMSCRIPTEN" + popd > /dev/null } diff --git a/tsconfig.base.json b/tsconfig.base.json index e34839a52ce..7f69376372d 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -26,13 +26,12 @@ "paths": { "@keymanapp/common-types": ["./common/web/types/src/main"], - "@keymanapp/keyboard-processor": ["./common/web/keyboard-processor/src"], "@keymanapp/keyman": ["./web" ], "@keymanapp/models-types": ["./common/models/types"], "@keymanapp/models-templates": ["./common/models/templates"], "@keymanapp/models-wordbreakers": ["./common/models/wordbreakers"], "@keymanapp/web-utils": ["./common/web/utils"], - "@keymanapp/lm-message-types": ["./common/web/lm-message-types"], + "@keymanapp/lm-message-types": ["./web/src/engine/predictive-text/types"], "@keymanapp/keyman-version": ["./common/web/keyman-version"], "@keymanapp/ldml-keyboard-constants": [ "./core/include/ldml" ], } diff --git a/tsconfig.json b/tsconfig.json index 093cec46f4a..1a02e20ff15 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,15 +5,11 @@ { "path": "./common/models/templates/tsconfig.json" }, { "path": "./common/models/types/tsconfig.json" }, { "path": "./common/models/wordbreakers/tsconfig.json" }, - { "path": "./common/predictive-text/tsconfig.all.json" }, { "path": "./common/tools/hextobin/" }, { "path": "./common/web/gesture-recognizer/tsconfig.json" }, { "path": "./common/web/gesture-recognizer/src/tools/unit-test-resources/tsconfig.json" }, - { "path": "./common/web/keyboard-processor/tsconfig.json" }, { "path": "./common/web/keyman-version" }, - { "path": "./common/web/lm-message-types/" }, - { "path": "./common/web/lm-worker/" }, { "path": "./common/web/recorder/tsconfig.json" }, { "path": "./common/web/sentry-manager/src/tsconfig.json" }, { "path": "./common/web/types/" }, @@ -44,6 +40,9 @@ { "path": "./resources/build/version/" }, { "path": "./web/src/tsconfig.all.json" }, + { "path": "./web/src/engine/predictive-text/types/" }, + { "path": "./web/src/engine/predictive-text/worker-main/tsconfig.all.json" }, + { "path": "./web/src/engine/predictive-text/worker-thread" }, // { "path": "./web/tools/recorder/tsconfig.json" }, // { "path": "./web/tools/sourcemap-root/tsconfig.json" }, ] diff --git a/web/README.md b/web/README.md index 1249805165e..f09fe519bbb 100644 --- a/web/README.md +++ b/web/README.md @@ -81,17 +81,18 @@ title: Dependency Graph %%{init: {"flowchart": {"htmlLabels": false}} }%% graph TD; OSK["/web/src/engine/osk"]; - KP["@keymanapp/keyboard-processor
(/common/web/keyboard-processor)"]; - OSK-->KP; + KeyboardSpec["/web/src/engine/keyboard"]; + JSProc["/web/src/engine/js-processor"]; + OSK-->KeyboardSpec; WebUtils["@keymanapp/web-utils
(/common/web/utils)"]; - KP---->WebUtils; + KeyboardSpec---->WebUtils; Wordbreakers["@keymanapp/models-wordbreakers
(/common/models/wordbreakers)"]; Models["@keymanapp/models-templates
(/common/models/templates)"]; Models-->WebUtils; - LMWorker["@keymanapp/lm-worker
(/common/web/lm-worker)"]; + LMWorker["@keymanapp/lm-worker
(/web/src/engine/predictive-text/worker-thread)"]; LMWorker-->Models; LMWorker-->Wordbreakers; - LMLayer["@keymanapp/lexical-model-layer
(/common/predictive-text)"]; + LMLayer["@keymanapp/lexical-model-layer
(/web/src/engine/predictive-text/worker-main)"]; LMLayer-->LMWorker; Gestures["@keymanapp/gesture-recognizer
(/common/web/gesture-recognizer)"]; Gestures-->WebUtils; @@ -106,32 +107,32 @@ graph TD; subgraph Headless["`**Headless** Fully headless components`"] direction LR - KP; + KeyboardSpec; + JSProc-->KeyboardSpec; WebUtils; PredText; Gestures; end subgraph ClassicWeb["`**ClassicWeb** - Previously unmodularized components`"] + Intermediate-level engine modules`"] Device["/web/src/engine/device-detect"]; Device----->WebUtils; Elements["/web/src/engine/element-wrappers"]; - Elements-->KP; - KeyboardCache["/web/src/engine/package-cache"]; - KeyboardCache-->Interfaces; + Elements-->JSProc; + KeyboardStorage["/web/src/engine/keyboard-storage"]; + KeyboardStorage-->Interfaces; DomUtils["/web/src/engine/dom-utils"]; DomUtils-->WebUtils; - DomUtils-->KP; + DomUtils-->KeyboardSpec; OSK-->DomUtils; OSK-->Gestures; Interfaces["/web/src/engine/interfaces"]; - Interfaces-->KP; + Interfaces-->KeyboardSpec; OSK-->Interfaces; CommonEngine["/web/src/engine/main"]; - CommonEngine-->Interfaces; CommonEngine-->Device; - CommonEngine-->KeyboardCache; + CommonEngine-->KeyboardStorage; CommonEngine-->OSK; Attachment["/web/src/engine/attachment"]; Attachment-->DomUtils; diff --git a/web/build.sh b/web/build.sh index 7248f5c35af..16d3d112356 100755 --- a/web/build.sh +++ b/web/build.sh @@ -27,10 +27,13 @@ builder_describe "Builds engine modules for Keyman Engine for Web (KMW)." \ ":engine/dom-utils A common subset of function used for DOM calculations, layout, etc" \ ":engine/events Specialized classes utilized to support KMW API events" \ ":engine/element-wrappers Subset used to integrate with website elements" \ + ":engine/interfaces Subset used to configure KMW" \ + ":engine/js-processor Build JS processor for KMW" \ + ":engine/keyboard Builds KMW's keyboard-loading and caching code" \ + ":engine/keyboard-storage Subset used to collate keyboards and request them from the cloud" \ ":engine/main Builds all common code used by KMW's app/-level targets" \ ":engine/osk Builds the Web OSK module" \ - ":engine/package-cache Subset used to collate keyboards and request them from the cloud" \ - ":engine/interfaces Subset used to configure KMW" \ + ":engine/predictive-text=src/engine/predictive-text/worker-main Builds KMW's predictive text module" \ ":samples Builds all needed resources for the KMW sample-page set" \ ":tools Builds engine-related development resources" \ ":test-pages=src/test/manual Builds resources needed for the KMW manual testing pages" \ @@ -57,10 +60,13 @@ builder_describe_outputs \ build:engine/dom-utils "/web/build/engine/dom-utils/obj/index.js" \ build:engine/events "/web/build/engine/events/lib/index.mjs" \ build:engine/element-wrappers "/web/build/engine/element-wrappers/lib/index.mjs" \ + build:engine/interfaces "/web/build/engine/interfaces/lib/index.mjs" \ + build:engine/js-processor "/web/build/engine/js-processor/lib/index.mjs" \ + build:engine/keyboard "/web/build/engine/keyboard/lib/index.mjs" \ + build:engine/keyboard-storage "/web/build/engine/keyboard-storage/lib/index.mjs" \ build:engine/main "/web/build/engine/main/lib/index.mjs" \ build:engine/osk "/web/build/engine/osk/lib/index.mjs" \ - build:engine/package-cache "/web/build/engine/package-cache/lib/index.mjs" \ - build:engine/interfaces "/web/build/engine/interfaces/lib/index.mjs" \ + build:engine/predictive-text "/web/src/engine/predictive-text/worker-main/build/lib/web/index.mjs" \ build:samples "/web/src/samples/simplest/keymanweb.js" \ build:tools "/web/build/tools/building/sourcemap-root/index.js" \ build:test-pages "/web/build/test-resources/sentry-manager.js" @@ -137,6 +143,9 @@ coverage_action() { builder_run_child_actions build:engine/device-detect builder_run_child_actions build:engine/dom-utils + +builder_run_child_actions build:engine/keyboard +builder_run_child_actions build:engine/js-processor builder_run_child_actions build:engine/element-wrappers builder_run_child_actions build:engine/events builder_run_child_actions build:engine/interfaces @@ -148,9 +157,9 @@ builder_run_child_actions build:engine/osk builder_run_child_actions build:engine/attachment # Uses engine/interfaces (due to resource-path config interface) -builder_run_child_actions build:engine/package-cache +builder_run_child_actions build:engine/keyboard-storage -# Uses engine/interfaces, engine/device-detect, engine/package-cache, & engine/osk +# Uses engine/interfaces, engine/device-detect, engine/keyboard-storage, & engine/osk builder_run_child_actions build:engine/main # Uses all but engine/element-wrappers and engine/attachment diff --git a/web/ci.sh b/web/ci.sh index 6e5bc3b06ac..5e26a1e30e2 100755 --- a/web/ci.sh +++ b/web/ci.sh @@ -85,7 +85,7 @@ if builder_start_action test; then # No --reporter option exists yet for the headless modules. - "$KEYMAN_ROOT/common/web/keyboard-processor/build.sh" test $OPTIONS + "$KEYMAN_ROOT/web/src/engine/keyboard/build.sh" test $OPTIONS "$KEYMAN_ROOT/common/web/gesture-recognizer/test.sh" $OPTIONS ./build.sh test $OPTIONS diff --git a/web/package.json b/web/package.json index 458ea85de01..96a11c0cd3e 100644 --- a/web/package.json +++ b/web/package.json @@ -37,21 +37,41 @@ "types": "./build/engine/events/obj/index.d.ts", "import": "./build/engine/events/obj/index.js" }, - "./engine/package-cache": { - "es6-bundling": "./src/engine/package-cache/src/index.ts", - "types": "./build/engine/package-cache/obj/index.d.ts", - "import": "./build/engine/package-cache/obj/index.js", - "require": "./build/engine/package-cache/obj/index.js" - }, - "./engine/package-cache/dom-requester": { - "es6-bundling": "./src/engine/package-cache/src/domCloudRequester.ts", - "types": "./build/engine/package-cache/obj/domCloudRequester.d.ts", - "import": "./build/engine/package-cache/obj/domCloudRequester.js" - }, - "./engine/package-cache/node-requester": { - "es6-bundling": "./src/engine/package-cache/src/nodeCloudRequester.ts", - "types": "./build/engine/package-cache/obj/nodeCloudRequester.d.ts", - "import": "./build/engine/package-cache/obj/nodeCloudRequester.js" + "./engine/js-processor": { + "es6-bundling": "./src/engine/js-processor/src/index.ts", + "types": "./build/engine/js-processor/obj/index.d.ts", + "import": "./build/engine/js-processor/obj/index.js" + }, + "./engine/keyboard": { + "es6-bundling": "./src/engine/keyboard/src/index.ts", + "types": "./build/engine/keyboard/obj/index.d.ts", + "import": "./build/engine/keyboard/obj/index.js" + }, + "./engine/keyboard/node-keyboard-loader": { + "es6-bundling": "./src/engine/keyboard/src/keyboards/loaders/node-keyboard-loader.ts", + "types": "./build/engine/keyboard/obj/keyboards/loaders/node-keyboard-loader.d.ts", + "import": "./build/engine/keyboard/obj/keyboards/loaders/node-keyboard-loader.js" + }, + "./engine/keyboard/dom-keyboard-loader": { + "es6-bundling": "./src/engine/keyboard/src/keyboards/loaders/dom-keyboard-loader.ts", + "types": "./build/engine/keyboard/obj/keyboards/loaders/dom-keyboard-loader.d.ts", + "import": "./build/engine/keyboard/obj/keyboards/loaders/dom-keyboard-loader.js" + }, + "./engine/keyboard-storage": { + "es6-bundling": "./src/engine/keyboard-storage/src/index.ts", + "types": "./build/engine/keyboard-storage/obj/index.d.ts", + "import": "./build/engine/keyboard-storage/obj/index.js", + "require": "./build/engine/keyboard-storage/obj/index.js" + }, + "./engine/keyboard-storage/dom-requester": { + "es6-bundling": "./src/engine/keyboard-storage/src/domCloudRequester.ts", + "types": "./build/engine/keyboard-storage/obj/domCloudRequester.d.ts", + "import": "./build/engine/keyboard-storage/obj/domCloudRequester.js" + }, + "./engine/keyboard-storage/node-requester": { + "es6-bundling": "./src/engine/keyboard-storage/src/nodeCloudRequester.ts", + "types": "./build/engine/keyboard-storage/obj/nodeCloudRequester.d.ts", + "import": "./build/engine/keyboard-storage/obj/nodeCloudRequester.js" }, "./engine/main": { "es6-bundling": "./src/engine/main/src/index.ts", @@ -99,7 +119,6 @@ "test": "gosh ./test.sh" }, "dependencies": { - "@keymanapp/keyboard-processor": "*", "@keymanapp/keyman-version": "*", "@keymanapp/lexical-model-layer": "*", "@keymanapp/models-types": "*", diff --git a/web/src/app/browser/build.sh b/web/src/app/browser/build.sh index 049bc056b5e..1d757e264a2 100755 --- a/web/src/app/browser/build.sh +++ b/web/src/app/browser/build.sh @@ -79,7 +79,7 @@ compile_and_copy() { local PROFILE_DEST="$KEYMAN_ROOT/web/build/profiling/" mkdir -p "$PROFILE_DEST" cp "${BUILD_ROOT}/filesize-profile.log" "$PROFILE_DEST/web-engine-filesize.log" - cp "$KEYMAN_ROOT/common/web/lm-worker/build/filesize-profile.log" "$PROFILE_DEST/lm-worker-filesize.log" + cp "$KEYMAN_ROOT/web/src/engine/predictive-text/worker-thread/build/filesize-profile.log" "$PROFILE_DEST/lm-worker-filesize.log" } builder_run_action configure verify_npm_setup diff --git a/web/src/app/browser/src/beepHandler.ts b/web/src/app/browser/src/beepHandler.ts index 3659f8d7fdb..eb6d6583ee4 100644 --- a/web/src/app/browser/src/beepHandler.ts +++ b/web/src/app/browser/src/beepHandler.ts @@ -1,4 +1,4 @@ -import { type KeyboardInterface } from '@keymanapp/keyboard-processor'; +import { type KeyboardInterface } from 'keyman/engine/js-processor'; import { DesignIFrame, OutputTarget } from 'keyman/engine/element-wrappers'; // Utility object used to handle beep (keyboard error response) operations. diff --git a/web/src/app/browser/src/configuration.ts b/web/src/app/browser/src/configuration.ts index 07be8264e78..df2153df828 100644 --- a/web/src/app/browser/src/configuration.ts +++ b/web/src/app/browser/src/configuration.ts @@ -1,7 +1,7 @@ import { EngineConfiguration, InitOptionSpec, InitOptionDefaults } from "keyman/engine/main"; import { OutputTarget as DOMOutputTarget } from 'keyman/engine/element-wrappers'; -import { isEmptyTransform, OutputTarget, RuleBehavior } from '@keymanapp/keyboard-processor'; +import { isEmptyTransform, OutputTarget, RuleBehavior } from 'keyman/engine/js-processor'; import { AlertHost } from "./utils/alertHost.js"; import { whenDocumentReady } from "./utils/documentReady.js"; diff --git a/web/src/app/browser/src/contextManager.ts b/web/src/app/browser/src/contextManager.ts index 1511269bbe8..1fbc9052033 100644 --- a/web/src/app/browser/src/contextManager.ts +++ b/web/src/app/browser/src/contextManager.ts @@ -1,5 +1,5 @@ -import { type Keyboard, KeyboardScriptError } from '@keymanapp/keyboard-processor'; -import { type KeyboardStub } from 'keyman/engine/package-cache'; +import { type Keyboard, KeyboardScriptError } from 'keyman/engine/keyboard'; +import { type KeyboardStub } from 'keyman/engine/keyboard-storage'; import { CookieSerializer } from 'keyman/engine/dom-utils'; import { eventOutputTarget, outputTargetForElement, PageContextAttachment } from 'keyman/engine/attachment'; import { DomEventTracker, LegacyEventEmitter } from 'keyman/engine/events'; diff --git a/web/src/app/browser/src/defaultBrowserRules.ts b/web/src/app/browser/src/defaultBrowserRules.ts index 1ac48016892..4f0ffbe012e 100644 --- a/web/src/app/browser/src/defaultBrowserRules.ts +++ b/web/src/app/browser/src/defaultBrowserRules.ts @@ -2,9 +2,9 @@ import { ModifierKeyConstants } from '@keymanapp/common-types'; import { Codes, DefaultRules, - type KeyEvent, - type OutputTarget -} from '@keymanapp/keyboard-processor'; + type KeyEvent +} from 'keyman/engine/keyboard'; +import { type OutputTarget } from 'keyman/engine/js-processor'; import ContextManager from './contextManager.js'; diff --git a/web/src/app/browser/src/hardwareEventKeyboard.ts b/web/src/app/browser/src/hardwareEventKeyboard.ts index 29e867aa13c..0bb594e8975 100644 --- a/web/src/app/browser/src/hardwareEventKeyboard.ts +++ b/web/src/app/browser/src/hardwareEventKeyboard.ts @@ -1,4 +1,5 @@ -import { Codes, DeviceSpec, KeyEvent, KeyMapping, Keyboard, KeyboardProcessor } from '@keymanapp/keyboard-processor'; +import { Codes, DeviceSpec, KeyEvent, KeyMapping, Keyboard } from 'keyman/engine/keyboard'; +import { KeyboardProcessor } from 'keyman/engine/js-processor'; import { ModifierKeyConstants } from '@keymanapp/common-types'; import { HardKeyboard, processForMnemonicsAndLegacy } from 'keyman/engine/main'; diff --git a/web/src/app/browser/src/keymanEngine.ts b/web/src/app/browser/src/keymanEngine.ts index d807c415eb3..324394bae22 100644 --- a/web/src/app/browser/src/keymanEngine.ts +++ b/web/src/app/browser/src/keymanEngine.ts @@ -6,8 +6,8 @@ import { TwoStateActivator, VisualKeyboard } from 'keyman/engine/osk'; -import { ErrorStub, KeyboardStub, CloudQueryResult, toPrefixedKeyboardId as prefixed } from 'keyman/engine/package-cache'; -import { DeviceSpec, Keyboard, KeyboardObject } from "@keymanapp/keyboard-processor"; +import { ErrorStub, KeyboardStub, CloudQueryResult, toPrefixedKeyboardId as prefixed } from 'keyman/engine/keyboard-storage'; +import { DeviceSpec, Keyboard, KeyboardObject } from "keyman/engine/keyboard"; import * as views from './viewsAnchorpoint.js'; import { BrowserConfiguration, BrowserInitOptionDefaults, BrowserInitOptionSpec } from './configuration.js'; diff --git a/web/src/app/browser/src/languageMenu.ts b/web/src/app/browser/src/languageMenu.ts index eb9361ccba3..d3144de6e5f 100644 --- a/web/src/app/browser/src/languageMenu.ts +++ b/web/src/app/browser/src/languageMenu.ts @@ -1,6 +1,6 @@ // Manages the language selection UI for touch-form factors, which is triggered by an OSK key. import { getAbsoluteX, landscapeView } from "keyman/engine/dom-utils"; -import { KeyboardStub } from "keyman/engine/package-cache"; +import { KeyboardStub } from "keyman/engine/keyboard-storage"; import KeymanEngine from "./keymanEngine.js"; import * as util from "./utils/index.js"; diff --git a/web/src/app/webview/src/contextManager.ts b/web/src/app/webview/src/contextManager.ts index b0c322ea29d..b0b90b4d96c 100644 --- a/web/src/app/webview/src/contextManager.ts +++ b/web/src/app/webview/src/contextManager.ts @@ -1,5 +1,6 @@ -import { type Keyboard, Mock, OutputTarget, Transcription, findCommonSubstringEndIndex, isEmptyTransform, TextTransform } from '@keymanapp/keyboard-processor'; -import { KeyboardStub } from 'keyman/engine/package-cache'; +import { type Keyboard } from 'keyman/engine/keyboard'; +import { Mock, OutputTarget, Transcription, findCommonSubstringEndIndex, isEmptyTransform, TextTransform } from 'keyman/engine/js-processor'; +import { KeyboardStub } from 'keyman/engine/keyboard-storage'; import { ContextManagerBase } from 'keyman/engine/main'; import { WebviewConfiguration } from './configuration.js'; diff --git a/web/src/app/webview/src/keymanEngine.ts b/web/src/app/webview/src/keymanEngine.ts index ba98f8c4776..d390a6ce1b2 100644 --- a/web/src/app/webview/src/keymanEngine.ts +++ b/web/src/app/webview/src/keymanEngine.ts @@ -1,8 +1,9 @@ -import { DefaultRules, DeviceSpec, RuleBehavior } from '@keymanapp/keyboard-processor' +import { DeviceSpec, DefaultRules } from 'keyman/engine/keyboard'; +import { RuleBehavior } from 'keyman/engine/js-processor'; import { KeymanEngine as KeymanEngineBase, KeyboardInterface } from 'keyman/engine/main'; import { AnchoredOSKView, ViewConfiguration, StaticActivator } from 'keyman/engine/osk'; import { getAbsoluteX, getAbsoluteY } from 'keyman/engine/dom-utils'; -import { toPrefixedKeyboardId, toUnprefixedKeyboardId } from 'keyman/engine/package-cache'; +import { toPrefixedKeyboardId, toUnprefixedKeyboardId } from 'keyman/engine/keyboard-storage'; import { WebviewConfiguration, WebviewInitOptionDefaults, WebviewInitOptionSpec } from './configuration.js'; import ContextManager, { ContextHost } from './contextManager.js'; diff --git a/web/src/app/webview/src/oskConfiguration.ts b/web/src/app/webview/src/oskConfiguration.ts index 26ceeb28721..9250b5a2a1d 100644 --- a/web/src/app/webview/src/oskConfiguration.ts +++ b/web/src/app/webview/src/oskConfiguration.ts @@ -1,5 +1,5 @@ import { OSKView } from "keyman/engine/osk"; -import { DeviceSpec } from "@keymanapp/keyboard-processor"; +import { DeviceSpec } from "keyman/engine/keyboard"; import { type EmbeddedGestureConfig } from "keyman/engine/osk"; import { GlobeHint } from './osk/globeHint.js'; diff --git a/web/src/app/webview/src/passthroughKeyboard.ts b/web/src/app/webview/src/passthroughKeyboard.ts index d74ab7f99a7..25dec7a8e68 100644 --- a/web/src/app/webview/src/passthroughKeyboard.ts +++ b/web/src/app/webview/src/passthroughKeyboard.ts @@ -1,4 +1,4 @@ -import { DeviceSpec, Keyboard, KeyEvent, ManagedPromise } from '@keymanapp/keyboard-processor'; +import { DeviceSpec, Keyboard, KeyEvent, ManagedPromise } from 'keyman/engine/keyboard'; import { HardKeyboard, processForMnemonicsAndLegacy } from 'keyman/engine/main'; diff --git a/web/src/engine/attachment/src/pageContextAttachment.ts b/web/src/engine/attachment/src/pageContextAttachment.ts index d63c113d5e2..4325c88f15d 100644 --- a/web/src/engine/attachment/src/pageContextAttachment.ts +++ b/web/src/engine/attachment/src/pageContextAttachment.ts @@ -1,6 +1,6 @@ import { EventEmitter } from 'eventemitter3'; -import { DeviceSpec, InternalKeyboardFont } from "@keymanapp/keyboard-processor"; +import { DeviceSpec, InternalKeyboardFont } from "keyman/engine/keyboard"; import { Input, nestedInstanceOf, wrapElement } from "keyman/engine/element-wrappers"; import { arrayFromNodeList, diff --git a/web/src/engine/dom-utils/build.sh b/web/src/engine/dom-utils/build.sh index 74b46051c42..d2e424db209 100755 --- a/web/src/engine/dom-utils/build.sh +++ b/web/src/engine/dom-utils/build.sh @@ -16,7 +16,7 @@ SUBPROJECT_NAME=engine/dom-utils builder_describe "Builds DOM-utility modules used by the Keyman Engine for Web (KMW)." \ "@/common/web/utils" \ - "@/common/web/keyboard-processor" \ + "@/web/src/engine/keyboard" \ "clean" \ "configure" \ "build" \ diff --git a/web/src/engine/dom-utils/src/stylesheets.ts b/web/src/engine/dom-utils/src/stylesheets.ts index 7312a62f055..977f253ff79 100644 --- a/web/src/engine/dom-utils/src/stylesheets.ts +++ b/web/src/engine/dom-utils/src/stylesheets.ts @@ -1,5 +1,5 @@ import { DeviceSpec, ManagedPromise } from '@keymanapp/web-utils'; -import { type InternalKeyboardFont as KeyboardFont } from '@keymanapp/keyboard-processor'; +import { type InternalKeyboardFont as KeyboardFont } from 'keyman/engine/keyboard'; type FontFamilyStyleMap = {[family: string]: HTMLStyleElement}; diff --git a/web/src/engine/dom-utils/tsconfig.json b/web/src/engine/dom-utils/tsconfig.json index c0b4a7d79dd..dadf4db5c8c 100644 --- a/web/src/engine/dom-utils/tsconfig.json +++ b/web/src/engine/dom-utils/tsconfig.json @@ -11,7 +11,7 @@ "include": [ "src/**/*.ts" ], "references": [ - { "path": "../../../../common/web/keyboard-processor" }, + { "path": "../keyboard" }, { "path": "../../../../common/web/utils" } ] } diff --git a/web/src/engine/element-wrappers/build.sh b/web/src/engine/element-wrappers/build.sh index cfcc58a44f2..e188c7dd414 100755 --- a/web/src/engine/element-wrappers/build.sh +++ b/web/src/engine/element-wrappers/build.sh @@ -13,7 +13,7 @@ SUBPROJECT_NAME=engine/element-wrappers # ################################ Main script ################################ builder_describe "Builds DOM-based OutputTarget subclasses used by the Keyman Engine for Web (KMW)." \ - "@/common/web/keyboard-processor" \ + "@/web/src/engine/js-processor" \ "clean" \ "configure" \ "build" \ diff --git a/web/src/engine/element-wrappers/readme.md b/web/src/engine/element-wrappers/readme.md index 1237237c7be..2a7685edbcb 100644 --- a/web/src/engine/element-wrappers/readme.md +++ b/web/src/engine/element-wrappers/readme.md @@ -1,4 +1,4 @@ ## engine/element-wrappers This submodule provides a subset of the main engine's Web-oriented code that's used to 'wrap' webpage -elements as part of KMW attachment and interface the element with the `keyboard-processor` submodule. \ No newline at end of file +elements as part of KMW attachment and interface the element with the `keyboard` submodule. \ No newline at end of file diff --git a/web/src/engine/element-wrappers/src/outputTarget.ts b/web/src/engine/element-wrappers/src/outputTarget.ts index 3a288c13471..43623af7812 100644 --- a/web/src/engine/element-wrappers/src/outputTarget.ts +++ b/web/src/engine/element-wrappers/src/outputTarget.ts @@ -1,4 +1,4 @@ -import { OutputTarget as OutputTargetBase } from "@keymanapp/keyboard-processor"; +import { OutputTarget as OutputTargetBase } from "keyman/engine/js-processor"; import { EventEmitter } from 'eventemitter3'; export default abstract class OutputTarget extends OutputTargetBase { diff --git a/web/src/engine/element-wrappers/tsconfig.json b/web/src/engine/element-wrappers/tsconfig.json index ab6aa238a80..15a58bbf2a8 100644 --- a/web/src/engine/element-wrappers/tsconfig.json +++ b/web/src/engine/element-wrappers/tsconfig.json @@ -11,6 +11,6 @@ "include": [ "src/**/*.ts" ], "references": [ - { "path": "../../../../common/web/keyboard-processor" } + { "path": "../keyboard" } ] } diff --git a/web/src/engine/events/build.sh b/web/src/engine/events/build.sh index 438d075c2ce..1b8993f5705 100755 --- a/web/src/engine/events/build.sh +++ b/web/src/engine/events/build.sh @@ -14,7 +14,7 @@ SUBPROJECT_NAME=engine/events builder_describe "Builds specialized event-related modules utilized by Keyman Engine for Web." \ "@/common/web/utils build" \ - "@/common/web/keyboard-processor build" \ + "@/web/src/engine/keyboard build" \ "clean" \ "configure" \ "build" \ diff --git a/web/src/engine/events/src/index.ts b/web/src/engine/events/src/index.ts index 2d95307bc9f..ee5674b66c8 100644 --- a/web/src/engine/events/src/index.ts +++ b/web/src/engine/events/src/index.ts @@ -1,4 +1,3 @@ export { DomEventTracker } from './domEventTracker.js'; export { EmitterListenerSpy } from './emitterListenerSpy.js'; -export * from './keyEventSource.interface.js'; export * from './legacyEventEmitter.js'; \ No newline at end of file diff --git a/web/src/engine/events/tsconfig.json b/web/src/engine/events/tsconfig.json index bb25ac9c99b..5d0a45bbab9 100644 --- a/web/src/engine/events/tsconfig.json +++ b/web/src/engine/events/tsconfig.json @@ -12,6 +12,6 @@ "references": [ { "path": "../../../../common/web/utils" }, - { "path": "../../../../common/web/keyboard-processor" } + { "path": "../keyboard" } ] } diff --git a/web/src/engine/interfaces/build.sh b/web/src/engine/interfaces/build.sh index ca61e648653..568bd7940d6 100755 --- a/web/src/engine/interfaces/build.sh +++ b/web/src/engine/interfaces/build.sh @@ -14,7 +14,8 @@ SUBPROJECT_NAME=engine/interfaces builder_describe "Builds configuration subclasses used by the Keyman Engine for Web (KMW)." \ "@/common/web/es-bundling" \ - "@/common/web/keyboard-processor" \ + "@/web/src/engine/keyboard" \ + "@/web/src/engine/js-processor" \ "clean" \ "configure" \ "build" \ diff --git a/web/src/engine/interfaces/src/prediction/languageProcessor.interface.ts b/web/src/engine/interfaces/src/prediction/languageProcessor.interface.ts index 7265d51f463..84e78519879 100644 --- a/web/src/engine/interfaces/src/prediction/languageProcessor.interface.ts +++ b/web/src/engine/interfaces/src/prediction/languageProcessor.interface.ts @@ -1,7 +1,7 @@ /// import { EventEmitter } from "eventemitter3"; -import { OutputTarget } from "@keymanapp/keyboard-processor"; +import { OutputTarget } from "keyman/engine/keyboard"; export class ReadySuggestions { suggestions: Suggestion[]; diff --git a/web/src/engine/interfaces/src/prediction/predictionContext.ts b/web/src/engine/interfaces/src/prediction/predictionContext.ts index d47bf4b8e2d..cd856153b23 100644 --- a/web/src/engine/interfaces/src/prediction/predictionContext.ts +++ b/web/src/engine/interfaces/src/prediction/predictionContext.ts @@ -1,6 +1,6 @@ import { EventEmitter } from "eventemitter3"; import { type LanguageProcessorSpec , ReadySuggestions, type InvalidateSourceEnum, StateChangeHandler } from './languageProcessor.interface.js'; -import { type KeyboardProcessor, type OutputTarget } from "@keymanapp/keyboard-processor"; +import { type OutputTarget } from "keyman/engine/keyboard"; interface PredictionContextEventMap { update: (suggestions: Suggestion[]) => void; @@ -32,7 +32,7 @@ export default class PredictionContext extends EventEmitter string; /** * Represents the active context used when requesting and applying predictive-text operations. @@ -59,27 +59,18 @@ export default class PredictionContext extends EventEmitter Promise; private readonly suggestionReverter: (reversion: Reversion) => void; - /** - * Handler for post-processing once a suggestion has been applied: calls - * into the active keyboard's `begin postKeystroke` entry point. - * - * Called after the suggestion is applied but _before_ new predictions are - * requested based on the resulting context. - */ - private readonly postApplicationHandler: () => void; - - public constructor(langProcessor: LanguageProcessorSpec, kbdProcessor: KeyboardProcessor) { + public constructor(langProcessor: LanguageProcessorSpec, getLayerId: () => string) { super(); this.langProcessor = langProcessor; - this.kbdProcessor = kbdProcessor; + this.getLayerId = getLayerId; const validSuggestionState: () => boolean = () => this.currentTarget && langProcessor.state == 'configured'; this.suggestionApplier = (suggestion) => { if(validSuggestionState()) { - return langProcessor.applySuggestion(suggestion, this.currentTarget, () => kbdProcessor.layerId); + return langProcessor.applySuggestion(suggestion, this.currentTarget, getLayerId); } else { return null; } @@ -94,19 +85,6 @@ export default class PredictionContext extends EventEmitter { - // Tell the keyboard that the current layer has not changed - kbdProcessor.newLayerStore.set(''); - kbdProcessor.oldLayerStore.set(''); - // Call the keyboard's entry point. - kbdProcessor.processPostKeystroke(kbdProcessor.contextDevice, this.currentTarget) - // If we have a RuleBehavior as a result, run it on the target. This should - // only change system store and variable store values. - ?.finalize(kbdProcessor, this.currentTarget, true); - }; - this.connect(); } @@ -116,8 +94,6 @@ export default class PredictionContext extends EventEmitter void; export type LogMessageHandler = (str: string) => void; -export interface VariableStoreSerializer { - loadStore(keyboardID: string, storeName: string): VariableStore; - saveStore(keyboardID: string, storeName: string, storeMap: VariableStore): void; -} - export interface ProcessorInitOptions { baseLayout?: string; keyboardInterface?: KeyboardInterface; @@ -174,7 +169,7 @@ export default class KeyboardProcessor extends EventEmitter { let isMnemonic = this.activeKeyboard && this.activeKeyboard.isMnemonic; if(!matched) { - if((char = this.defaultRules.forAny(Lkc, isMnemonic)) != null) { + if((char = this.defaultRules.forAny(Lkc, isMnemonic, ruleBehavior)) != null) { special = this.defaultRules.forSpecialEmulation(Lkc) if(special == EmulationKeystrokes.Backspace) { // A browser's default backspace may fail to delete both parts of an SMP character. @@ -277,7 +272,8 @@ export default class KeyboardProcessor extends EventEmitter { const lockNames = ['CAPS', 'NUM_LOCK', 'SCROLL_LOCK'] as const; const lockKeys = ['K_CAPS', 'K_NUMLOCK', 'K_SCROLL'] as const; - const lockModifiers = [ ModifierKeyConstants.CAPITALFLAG, ModifierKeyConstants.NUMLOCKFLAG, ModifierKeyConstants.SCROLLFLAG] as const; + const lockModifiers = [ModifierKeyConstants.CAPITALFLAG, ModifierKeyConstants.NUMLOCKFLAG, ModifierKeyConstants.SCROLLFLAG] as const; + if(!this.activeKeyboard) { return true; @@ -329,6 +325,8 @@ export default class KeyboardProcessor extends EventEmitter { const lockModifiers = [ModifierKeyConstants.CAPITALFLAG, ModifierKeyConstants.NUMLOCKFLAG, ModifierKeyConstants.SCROLLFLAG] as const; const noLockModifers = [ModifierKeyConstants.NOTCAPITALFLAG, ModifierKeyConstants.NOTNUMLOCKFLAG, ModifierKeyConstants.NOTSCROLLFLAG] as const; + + for(let i=0; i < lockKeys.length; i++) { const key = lockKeys[i]; const flag = this.stateKeys[key]; diff --git a/web/src/engine/js-processor/src/mock.ts b/web/src/engine/js-processor/src/mock.ts new file mode 100644 index 00000000000..151d8313caa --- /dev/null +++ b/web/src/engine/js-processor/src/mock.ts @@ -0,0 +1,157 @@ +import OutputTarget from './outputTarget.js'; + +// Due to some interesting requirements on compile ordering in TS, +// this needs to be in the same file as OutputTarget now. +export class Mock extends OutputTarget { + text: string; + + selStart: number; + selEnd: number; + selForward: boolean = true; + + constructor(text?: string, caretPos?: number); + constructor(text?: string, selStart?: number, selEnd?: number); + constructor(text?: string, selStart?: number, selEnd?: number) { + super(); + + this.text = text ? text : ""; + var defaultLength = this.text._kmwLength(); + + // Ensures that `caretPos == 0` is handled correctly. + this.selStart = typeof selStart == "number" ? selStart : defaultLength; + + // If no selection-end is set, selection length is implied to be 0. + this.selEnd = typeof selEnd == "number" ? selEnd : this.selStart; + + this.selForward = this.selEnd >= this.selStart; + } + + // Clones the state of an existing EditableElement, creating a Mock version of its state. + static from(outputTarget: OutputTarget, readonly?: boolean) { + let clone: Mock; + + if (outputTarget instanceof Mock) { + // Avoids the need to run expensive kmwstring.ts / `_kmwLength()` + // calculations when deep-copying Mock instances. + let priorMock = outputTarget as Mock; + clone = new Mock(priorMock.text, priorMock.selStart, priorMock.selEnd); + } else { + const text = outputTarget.getText(); + const textLen = text._kmwLength(); + + // If !hasSelection() + let selectionStart: number = textLen; + let selectionEnd: number = 0; + + if (outputTarget.hasSelection()) { + let beforeText = outputTarget.getTextBeforeCaret(); + let afterText = outputTarget.getTextAfterCaret(); + selectionStart = beforeText._kmwLength(); + selectionEnd = textLen - afterText._kmwLength(); + } + + // readonly group or not, the returned Mock remains the same. + // New-context events should act as if the caret were at the earlier-in-context + // side of the selection, same as standard keyboard rules. + clone = new Mock(text, selectionStart, selectionEnd); + } + + // Also duplicate deadkey state! (Needed for fat-finger ops.) + clone.setDeadkeys(outputTarget.deadkeys()); + + return clone; + } + + clearSelection(): void { + this.text = this.getTextBeforeCaret() + this.getTextAfterCaret(); + this.selEnd = this.selStart; + this.selForward = true; + } + + invalidateSelection(): void { + return; + } + + isSelectionEmpty(): boolean { + return this.selStart == this.selEnd; + } + + hasSelection(): boolean { + return true; + } + + getDeadkeyCaret(): number { + return this.selStart; + } + + setSelection(start: number, end?: number) { + this.selStart = start; + this.selEnd = typeof end == 'number' ? end : start; + + this.selForward = end >= start; + if (!this.selForward) { + let temp = this.selStart; + this.selStart = this.selEnd; + this.selEnd = temp; + } + } + + getTextBeforeCaret(): string { + return this.text.kmwSubstr(0, this.selStart); + } + + getSelectedText(): string { + return this.text.kmwSubstr(this.selStart, this.selEnd - this.selStart); + } + + getTextAfterCaret(): string { + return this.text.kmwSubstr(this.selEnd); + } + + getText(): string { + return this.text; + } + + deleteCharsBeforeCaret(dn: number): void { + if (dn >= 0) { + if (dn > this.selStart) { + dn = this.selStart; + } + this.adjustDeadkeys(-dn); + this.text = this.text.kmwSubstr(0, this.selStart - dn) + this.text.kmwSubstr(this.selStart); + this.selStart -= dn; + this.selEnd -= dn; + } + } + + insertTextBeforeCaret(s: string): void { + this.adjustDeadkeys(s._kmwLength()); + this.text = this.getTextBeforeCaret() + s + this.text.kmwSubstr(this.selStart); + this.selStart += s.kmwLength(); + this.selEnd += s.kmwLength(); + } + + handleNewlineAtCaret(): void { + this.insertTextBeforeCaret('\n'); + } + + protected setTextAfterCaret(s: string): void { + this.text = this.getTextBeforeCaret() + s; + } + + /** + * Indicates if this Mock represents an identical context to that of another Mock. + * @param other + * @returns + */ + isEqual(other: Mock) { + return this.text == other.text + && this.selStart == other.selStart + && this.selEnd == other.selEnd + && this.deadkeys().equal(other.deadkeys()); + } + + doInputEvent() { + // Mock isn't backed by an element, so it won't have any event listeners. + } +} diff --git a/common/web/keyboard-processor/src/text/outputTarget.ts b/web/src/engine/js-processor/src/outputTarget.ts similarity index 69% rename from common/web/keyboard-processor/src/text/outputTarget.ts rename to web/src/engine/js-processor/src/outputTarget.ts index 63c4c8a042f..becd887e01c 100644 --- a/common/web/keyboard-processor/src/text/outputTarget.ts +++ b/web/src/engine/js-processor/src/outputTarget.ts @@ -2,11 +2,13 @@ import { extendString } from "@keymanapp/web-utils"; import { findCommonSubstringEndIndex } from "./stringDivergence.js"; +import { Mock } from "./mock.js"; +import { OutputTarget as OutputTargetInterface } from 'keyman/engine/keyboard'; extendString(); // Defines deadkey management in a manner attachable to each element interface. -import type KeyEvent from "./keyEvent.js"; +import { type KeyEvent } from 'keyman/engine/keyboard'; import { Deadkey, DeadkeyTracker } from "./deadkeys.js"; // Also relies on string-extensions provided by the web-utils package. @@ -65,7 +67,7 @@ export class Transcription { export type Alternate = ProbabilityMass; -export default abstract class OutputTarget { +export default abstract class OutputTarget implements OutputTargetInterface { private _dks: DeadkeyTracker; constructor() { @@ -307,159 +309,3 @@ export default abstract class OutputTarget { */ abstract doInputEvent(): void; } - -// Due to some interesting requirements on compile ordering in TS, -// this needs to be in the same file as OutputTarget now. -export class Mock extends OutputTarget { - text: string; - - selStart: number; - selEnd: number; - selForward: boolean = true; - - constructor(text?: string, caretPos?: number); - constructor(text?: string, selStart?: number, selEnd?: number); - constructor(text?: string, selStart?: number, selEnd?: number) { - super(); - - this.text = text ? text : ""; - var defaultLength = this.text._kmwLength(); - - // Ensures that `caretPos == 0` is handled correctly. - this.selStart = typeof selStart == "number" ? selStart : defaultLength; - - // If no selection-end is set, selection length is implied to be 0. - this.selEnd = typeof selEnd == "number" ? selEnd : this.selStart; - - this.selForward = this.selEnd >= this.selStart; - } - - // Clones the state of an existing EditableElement, creating a Mock version of its state. - static from(outputTarget: OutputTarget, readonly?: boolean) { - let clone: Mock; - - if(outputTarget instanceof Mock) { - // Avoids the need to run expensive kmwstring.ts / `_kmwLength()` - // calculations when deep-copying Mock instances. - let priorMock = outputTarget as Mock; - clone = new Mock(priorMock.text, priorMock.selStart, priorMock.selEnd); - } else { - const text = outputTarget.getText(); - const textLen = text._kmwLength(); - - // If !hasSelection() - let selectionStart: number = textLen; - let selectionEnd: number = 0; - - if(outputTarget.hasSelection()) { - let beforeText = outputTarget.getTextBeforeCaret(); - let afterText = outputTarget.getTextAfterCaret(); - selectionStart = beforeText._kmwLength(); - selectionEnd = textLen - afterText._kmwLength(); - } - - // readonly group or not, the returned Mock remains the same. - // New-context events should act as if the caret were at the earlier-in-context - // side of the selection, same as standard keyboard rules. - clone = new Mock(text, selectionStart, selectionEnd); - } - - // Also duplicate deadkey state! (Needed for fat-finger ops.) - clone.setDeadkeys(outputTarget.deadkeys()); - - return clone; - } - - clearSelection(): void { - this.text = this.getTextBeforeCaret() + this.getTextAfterCaret(); - this.selEnd = this.selStart; - this.selForward = true; - } - - invalidateSelection(): void { - return; - } - - isSelectionEmpty(): boolean { - return this.selStart == this.selEnd; - } - - hasSelection(): boolean { - return true; - } - - getDeadkeyCaret(): number { - return this.selStart; - } - - setSelection(start: number, end?: number) { - this.selStart = start; - this.selEnd = typeof end == 'number' ? end : start; - - this.selForward = end >= start; - if(!this.selForward) { - let temp = this.selStart; - this.selStart = this.selEnd; - this.selEnd = temp; - } - } - - getTextBeforeCaret(): string { - return this.text.kmwSubstr(0, this.selStart); - } - - getSelectedText(): string { - return this.text.kmwSubstr(this.selStart, this.selEnd - this.selStart); - } - - getTextAfterCaret(): string { - return this.text.kmwSubstr(this.selEnd); - } - - getText(): string { - return this.text; - } - - deleteCharsBeforeCaret(dn: number): void { - if(dn >= 0) { - if(dn > this.selStart) { - dn = this.selStart; - } - this.adjustDeadkeys(-dn); - this.text = this.text.kmwSubstr(0, this.selStart - dn) + this.text.kmwSubstr(this.selStart); - this.selStart -= dn; - this.selEnd -= dn; - } - } - - insertTextBeforeCaret(s: string): void { - this.adjustDeadkeys(s._kmwLength()); - this.text = this.getTextBeforeCaret() + s + this.text.kmwSubstr(this.selStart); - this.selStart += s.kmwLength(); - this.selEnd += s.kmwLength(); - } - - handleNewlineAtCaret(): void { - this.insertTextBeforeCaret('\n'); - } - - protected setTextAfterCaret(s: string): void { - this.text = this.getTextBeforeCaret() + s; - } - - /** - * Indicates if this Mock represents an identical context to that of another Mock. - * @param other - * @returns - */ - isEqual(other: Mock) { - return this.text == other.text - && this.selStart == other.selStart - && this.selEnd == other.selEnd - && this.deadkeys().equal(other.deadkeys()); - } - - doInputEvent() { - // Mock isn't backed by an element, so it won't have any event listeners. - } -} \ No newline at end of file diff --git a/common/web/keyboard-processor/src/text/ruleBehavior.ts b/web/src/engine/js-processor/src/ruleBehavior.ts similarity index 95% rename from common/web/keyboard-processor/src/text/ruleBehavior.ts rename to web/src/engine/js-processor/src/ruleBehavior.ts index e5d669e9092..a6b2c9973a1 100644 --- a/common/web/keyboard-processor/src/text/ruleBehavior.ts +++ b/web/src/engine/js-processor/src/ruleBehavior.ts @@ -1,9 +1,10 @@ /// import KeyboardProcessor from "./keyboardProcessor.js"; -import OutputTarget, { Mock, type Transcription } from "./outputTarget.js"; -import { VariableStoreDictionary } from "../keyboards/keyboard.js"; -import type { VariableStore } from "./kbdInterface.js"; +import { VariableStoreDictionary } from "keyman/engine/keyboard"; +import OutputTarget, { type Transcription } from './outputTarget.js'; +import { Mock } from "./mock.js"; +import { type VariableStore } from "./systemStores.js"; /** * Represents the commands and state changes that result from a matched keyboard rule. diff --git a/common/web/keyboard-processor/src/text/stringDivergence.ts b/web/src/engine/js-processor/src/stringDivergence.ts similarity index 100% rename from common/web/keyboard-processor/src/text/stringDivergence.ts rename to web/src/engine/js-processor/src/stringDivergence.ts diff --git a/common/web/keyboard-processor/src/text/systemStores.ts b/web/src/engine/js-processor/src/systemStores.ts similarity index 74% rename from common/web/keyboard-processor/src/text/systemStores.ts rename to web/src/engine/js-processor/src/systemStores.ts index 13184baafec..3288c9feff8 100644 --- a/common/web/keyboard-processor/src/text/systemStores.ts +++ b/web/src/engine/js-processor/src/systemStores.ts @@ -1,5 +1,31 @@ -import type KeyboardInterface from "./kbdInterface.js"; -import { SystemStoreIDs } from "./kbdInterface.js"; +import { type KeyboardHarness } from 'keyman/engine/keyboard'; +import { StoreNonCharEntry } from './kbdInterface.js'; + +export enum SystemStoreIDs { + TSS_LAYER = 33, + TSS_PLATFORM = 31, + TSS_NEWLAYER = 42, + TSS_OLDLAYER = 43 +} + +/* +* Type alias definitions to reflect the parameters of the fullContextMatch() callback (KMW 10+). +* No constructors or methods since keyboards will not utilize the same backing prototype, and +* property names are shorthanded to promote minification. +*/ +type PlainKeyboardStore = string; + +export type KeyboardStoreElement = (string | StoreNonCharEntry); +export type ComplexKeyboardStore = KeyboardStoreElement[]; + +export type KeyboardStore = PlainKeyboardStore | ComplexKeyboardStore; + +export type VariableStore = { [name: string]: string }; + +export interface VariableStoreSerializer { + loadStore(keyboardID: string, storeName: string): VariableStore; + saveStore(keyboardID: string, storeName: string, storeMap: VariableStore): void; +} /** * Defines common behaviors associated with system stores. @@ -61,9 +87,9 @@ export class MutableSystemStore extends SystemStore { * Handles checks against the current platform. */ export class PlatformSystemStore extends SystemStore { - private readonly kbdInterface: KeyboardInterface; + private readonly kbdInterface: KeyboardHarness; - constructor(keyboardInterface: KeyboardInterface) { + constructor(keyboardInterface: KeyboardHarness) { super(SystemStoreIDs.TSS_PLATFORM); this.kbdInterface = keyboardInterface; @@ -131,4 +157,4 @@ export class PlatformSystemStore extends SystemStore { // Everything we checked against was valid and had matches - it's a match! return true; } -} \ No newline at end of file +} diff --git a/web/src/engine/js-processor/tsconfig.json b/web/src/engine/js-processor/tsconfig.json new file mode 100644 index 00000000000..a57467139bd --- /dev/null +++ b/web/src/engine/js-processor/tsconfig.json @@ -0,0 +1,13 @@ +{ + // While the actual references themselves are headless, it compiles against the DOM-reliant OSK module. + "extends": "../../tsconfig.dom.json", + + "compilerOptions": { + "baseUrl": "./", + "outDir": "../../../build/engine/js-processor/obj/", + "tsBuildInfoFile": "../../../build/engine/js-processor/obj/tsconfig.tsbuildinfo", + "rootDir": "./src" + }, + + "include": [ "**/*.ts" ], +} diff --git a/web/src/engine/package-cache/.c8rc.json b/web/src/engine/keyboard-storage/.c8rc.json similarity index 100% rename from web/src/engine/package-cache/.c8rc.json rename to web/src/engine/keyboard-storage/.c8rc.json diff --git a/web/src/engine/package-cache/README.md b/web/src/engine/keyboard-storage/README.md similarity index 100% rename from web/src/engine/package-cache/README.md rename to web/src/engine/keyboard-storage/README.md diff --git a/web/src/engine/package-cache/build.sh b/web/src/engine/keyboard-storage/build.sh similarity index 97% rename from web/src/engine/package-cache/build.sh rename to web/src/engine/keyboard-storage/build.sh index b413dd5be8c..33873355ff1 100755 --- a/web/src/engine/package-cache/build.sh +++ b/web/src/engine/keyboard-storage/build.sh @@ -6,7 +6,7 @@ THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" . "${THIS_SCRIPT%/*}/../../../../resources/build/builder.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE -SUBPROJECT_NAME=engine/package-cache +SUBPROJECT_NAME=engine/keyboard-storage . "$KEYMAN_ROOT/web/common.inc.sh" . "$KEYMAN_ROOT/resources/shellHelperFunctions.sh" diff --git a/web/src/engine/package-cache/src/cloud/index.ts b/web/src/engine/keyboard-storage/src/cloud/index.ts similarity index 100% rename from web/src/engine/package-cache/src/cloud/index.ts rename to web/src/engine/keyboard-storage/src/cloud/index.ts diff --git a/web/src/engine/package-cache/src/cloud/queryEngine.ts b/web/src/engine/keyboard-storage/src/cloud/queryEngine.ts similarity index 99% rename from web/src/engine/package-cache/src/cloud/queryEngine.ts rename to web/src/engine/keyboard-storage/src/cloud/queryEngine.ts index eb989d2f00a..5f38c32aead 100644 --- a/web/src/engine/package-cache/src/cloud/queryEngine.ts +++ b/web/src/engine/keyboard-storage/src/cloud/queryEngine.ts @@ -3,7 +3,7 @@ import { EventEmitter } from 'eventemitter3'; import { PathConfiguration } from 'keyman/engine/interfaces'; import { default as KeyboardStub, ErrorStub, KeyboardAPISpec, mergeAndResolveStubPromises } from '../keyboardStub.js'; -import { LanguageAPIPropertySpec, ManagedPromise, Version } from '@keymanapp/keyboard-processor'; +import { LanguageAPIPropertySpec, ManagedPromise, Version } from 'keyman/engine/keyboard'; import CloudRequesterInterface from './requesterInterface.js'; // For when the API call straight-up times out. diff --git a/web/src/engine/package-cache/src/cloud/requesterInterface.ts b/web/src/engine/keyboard-storage/src/cloud/requesterInterface.ts similarity index 68% rename from web/src/engine/package-cache/src/cloud/requesterInterface.ts rename to web/src/engine/keyboard-storage/src/cloud/requesterInterface.ts index c0f8e6baf14..6c612d9cb20 100644 --- a/web/src/engine/package-cache/src/cloud/requesterInterface.ts +++ b/web/src/engine/keyboard-storage/src/cloud/requesterInterface.ts @@ -1,4 +1,4 @@ -import { ManagedPromise } from '@keymanapp/keyboard-processor'; +import { ManagedPromise } from 'keyman/engine/keyboard'; export default interface CloudRequesterInterface { request(query: string): { diff --git a/web/src/engine/package-cache/src/domCloudRequester.ts b/web/src/engine/keyboard-storage/src/domCloudRequester.ts similarity index 97% rename from web/src/engine/package-cache/src/domCloudRequester.ts rename to web/src/engine/keyboard-storage/src/domCloudRequester.ts index ee80b0f0638..fe671d101ff 100644 --- a/web/src/engine/package-cache/src/domCloudRequester.ts +++ b/web/src/engine/keyboard-storage/src/domCloudRequester.ts @@ -1,4 +1,4 @@ -import { ManagedPromise } from '@keymanapp/keyboard-processor'; +import { ManagedPromise } from 'keyman/engine/keyboard'; import CloudRequesterInterface from './cloud/requesterInterface.js'; import { CLOUD_MALFORMED_OBJECT_ERR, CLOUD_TIMEOUT_ERR, CLOUD_STUB_REGISTRATION_ERR } from './cloud/queryEngine.js'; diff --git a/web/src/engine/package-cache/src/index.ts b/web/src/engine/keyboard-storage/src/index.ts similarity index 100% rename from web/src/engine/package-cache/src/index.ts rename to web/src/engine/keyboard-storage/src/index.ts diff --git a/web/src/engine/package-cache/src/keyboardRequisitioner.ts b/web/src/engine/keyboard-storage/src/keyboardRequisitioner.ts similarity index 99% rename from web/src/engine/package-cache/src/keyboardRequisitioner.ts rename to web/src/engine/keyboard-storage/src/keyboardRequisitioner.ts index 13cc7ecef83..fb2807de6ff 100644 --- a/web/src/engine/package-cache/src/keyboardRequisitioner.ts +++ b/web/src/engine/keyboard-storage/src/keyboardRequisitioner.ts @@ -3,7 +3,7 @@ import { KeyboardLoaderBase as KeyboardLoader, LanguageAPIPropertySpec, RawKeyboardMetadata -} from "@keymanapp/keyboard-processor"; +} from "keyman/engine/keyboard"; import { PathConfiguration } from "keyman/engine/interfaces"; // TODO: is cleanup needed here, to use local paths instead? diff --git a/web/src/engine/package-cache/src/keyboardStub.ts b/web/src/engine/keyboard-storage/src/keyboardStub.ts similarity index 99% rename from web/src/engine/package-cache/src/keyboardStub.ts rename to web/src/engine/keyboard-storage/src/keyboardStub.ts index 474d4cc9eff..a2b1d87d6fa 100644 --- a/web/src/engine/package-cache/src/keyboardStub.ts +++ b/web/src/engine/keyboard-storage/src/keyboardStub.ts @@ -3,7 +3,7 @@ import { type KeyboardAPIPropertyMultilangSpec as APICompoundKeyboard, KeyboardProperties, type LanguageAPIPropertySpec, -} from '@keymanapp/keyboard-processor'; +} from 'keyman/engine/keyboard'; import { toPrefixedKeyboardId as prefixed } from './stubAndKeyboardCache.js'; diff --git a/web/src/engine/package-cache/src/modelCache.ts b/web/src/engine/keyboard-storage/src/modelCache.ts similarity index 100% rename from web/src/engine/package-cache/src/modelCache.ts rename to web/src/engine/keyboard-storage/src/modelCache.ts diff --git a/web/src/engine/package-cache/src/nodeCloudRequester.ts b/web/src/engine/keyboard-storage/src/nodeCloudRequester.ts similarity index 97% rename from web/src/engine/package-cache/src/nodeCloudRequester.ts rename to web/src/engine/keyboard-storage/src/nodeCloudRequester.ts index bd9ff851597..e8461ff20d7 100644 --- a/web/src/engine/package-cache/src/nodeCloudRequester.ts +++ b/web/src/engine/keyboard-storage/src/nodeCloudRequester.ts @@ -1,4 +1,4 @@ -import { ManagedPromise } from '@keymanapp/keyboard-processor'; +import { ManagedPromise } from 'keyman/engine/keyboard'; import CloudRequesterInterface from './cloud/requesterInterface.js'; import { CLOUD_TIMEOUT_ERR, diff --git a/web/src/engine/package-cache/src/stubAndKeyboardCache.ts b/web/src/engine/keyboard-storage/src/stubAndKeyboardCache.ts similarity index 99% rename from web/src/engine/package-cache/src/stubAndKeyboardCache.ts rename to web/src/engine/keyboard-storage/src/stubAndKeyboardCache.ts index 9fd70e74724..67d62446bee 100644 --- a/web/src/engine/package-cache/src/stubAndKeyboardCache.ts +++ b/web/src/engine/keyboard-storage/src/stubAndKeyboardCache.ts @@ -1,4 +1,4 @@ -import { Keyboard, KeyboardLoaderBase as KeyboardLoader } from "@keymanapp/keyboard-processor"; +import { Keyboard, KeyboardLoaderBase as KeyboardLoader } from "keyman/engine/keyboard"; import { EventEmitter } from "eventemitter3"; import KeyboardStub from "./keyboardStub.js"; diff --git a/web/src/engine/package-cache/tsconfig.json b/web/src/engine/keyboard-storage/tsconfig.json similarity index 57% rename from web/src/engine/package-cache/tsconfig.json rename to web/src/engine/keyboard-storage/tsconfig.json index 7a2ab5f235d..f093be2cbc6 100644 --- a/web/src/engine/package-cache/tsconfig.json +++ b/web/src/engine/keyboard-storage/tsconfig.json @@ -3,8 +3,8 @@ "compilerOptions": { "baseUrl": "./", - "outDir": "../../../build/engine/package-cache/obj/", - "tsBuildInfoFile": "../../../build/engine/package-cache/obj/tsconfig.tsbuildinfo", + "outDir": "../../../build/engine/keyboard-storage/obj/", + "tsBuildInfoFile": "../../../build/engine/keyboard-storage/obj/tsconfig.tsbuildinfo", "rootDir": "./src" }, diff --git a/common/web/keyboard-processor/.c8rc.json b/web/src/engine/keyboard/.c8rc.json similarity index 100% rename from common/web/keyboard-processor/.c8rc.json rename to web/src/engine/keyboard/.c8rc.json diff --git a/common/web/keyboard-processor/.gitignore b/web/src/engine/keyboard/.gitignore similarity index 100% rename from common/web/keyboard-processor/.gitignore rename to web/src/engine/keyboard/.gitignore diff --git a/common/web/keyboard-processor/README.md b/web/src/engine/keyboard/README.md similarity index 92% rename from common/web/keyboard-processor/README.md rename to web/src/engine/keyboard/README.md index 8ac9b8f9d05..a2a4eb89944 100644 --- a/common/web/keyboard-processor/README.md +++ b/web/src/engine/keyboard/README.md @@ -1,5 +1,6 @@ # Keyman: Keyboard Processor Module -The Original Code is (C) 2017-2020 SIL International + +The Original Code is (C) 2017-2024 SIL International The Keyboard Processor module is an internal component of KeymanWeb, seen within this repo at /web/. diff --git a/web/src/engine/keyboard/build.sh b/web/src/engine/keyboard/build.sh new file mode 100755 index 00000000000..226a7d9d754 --- /dev/null +++ b/web/src/engine/keyboard/build.sh @@ -0,0 +1,85 @@ +#!/usr/bin/env bash +# +# Compile KeymanWeb's 'keyboard' module, one of the components of Web's 'core' module. +# +## START STANDARD BUILD SCRIPT INCLUDE +# adjust relative paths as necessary +THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" +. "${THIS_SCRIPT%/*}/../../../../resources/build/builder.inc.sh" +## END STANDARD BUILD SCRIPT INCLUDE + +SUBPROJECT_NAME=engine/keyboard + +. "${KEYMAN_ROOT}/web/common.inc.sh" +. "${KEYMAN_ROOT}/resources/shellHelperFunctions.sh" + +################################ Main script ################################ + +builder_describe \ + "Compiles the web-oriented utility function module." \ + "@/web/src/tools/testing/recorder-core test" \ + "@/common/web/keyman-version" \ + "@/common/web/es-bundling" \ + "@/common/web/types" \ + "@/common/web/utils" \ + configure \ + clean \ + build \ + test \ + "--ci For use with action $(builder_term test) - emits CI-friendly test reports" + +builder_describe_outputs \ + configure /node_modules \ + build /web/build/engine/keyboard/lib/index.mjs + +builder_parse "$@" + +function do_configure() { + verify_npm_setup + + # Configure Web browser-engine testing environments. As is, this should only + # make changes when we update the dependency, even on our CI build agents. + playwright install +} + +BUILD_DIR="${KEYMAN_ROOT}/web/build/${SUBPROJECT_NAME}" + +function do_build() { + tsc --build "${THIS_SCRIPT_PATH}/tsconfig.all.json" + + # Base product - the main keyboard processor + builder_echo "Bundle base product - the main keyboard processor" + ${BUNDLE_CMD} "${BUILD_DIR}/obj/index.js" \ + --out "${BUILD_DIR}/lib/index.mjs" \ + --format esm + + # The DOM-oriented keyboard loader + builder_echo "Bundle the DOM-oriented keyboard loader" + ${BUNDLE_CMD} "${BUILD_DIR}/obj/keyboards/loaders/dom-keyboard-loader.js" \ + --out "${BUILD_DIR}/lib/dom-keyboard-loader.mjs" \ + --format esm + + # The Node-oriented keyboard loader + builder_echo "Bundle the Node-oriented keyboard loader" + ${BUNDLE_CMD} "${BUILD_DIR}/obj/keyboards/loaders/node-keyboard-loader.js" \ + --out "${BUILD_DIR}/lib/node-keyboard-loader.mjs" \ + --format esm \ + --platform node + + # # Tests + # builder_echo "Bundle tests" + # ${BUNDLE_CMD} "${BUILD_DIR}/tests/dom/cases/domKeyboardLoader.spec.js" \ + # --out "${BUILD_DIR}/tests/dom/domKeyboardLoader.spec.mjs" \ + # --format esm + + # Declaration bundling. + builder_echo "Declaration bundling" + tsc --emitDeclarationOnly --outFile "${BUILD_DIR}/lib/index.d.ts" + tsc --emitDeclarationOnly --outFile "${BUILD_DIR}/lib/dom-keyboard-loader.d.ts" -p src/keyboards/loaders/tsconfig.dom.json + tsc --emitDeclarationOnly --outFile "${BUILD_DIR}/lib/node-keyboard-loader.d.ts" -p src/keyboards/loaders/tsconfig.node.json +} + +builder_run_action configure do_configure +builder_run_action clean rm -rf "${BUILD_DIR}" +builder_run_action build do_build +builder_run_action test test-headless "${SUBPROJECT_NAME}" "" diff --git a/common/web/keyboard-processor/src/text/codes.ts b/web/src/engine/keyboard/src/codes.ts similarity index 100% rename from common/web/keyboard-processor/src/text/codes.ts rename to web/src/engine/keyboard/src/codes.ts diff --git a/common/web/keyboard-processor/src/text/defaultRules.ts b/web/src/engine/keyboard/src/defaultRules.ts similarity index 81% rename from common/web/keyboard-processor/src/text/defaultRules.ts rename to web/src/engine/keyboard/src/defaultRules.ts index 931eb5200b2..5512114d937 100644 --- a/common/web/keyboard-processor/src/text/defaultRules.ts +++ b/web/src/engine/keyboard/src/defaultRules.ts @@ -1,32 +1,28 @@ -// TODO: Move to separate folder: 'codes' -// We should start splitting off code needed by keyboards even without a KeyboardProcessor active. -// There's an upcoming `/common/web/types` package that 'codes' and 'keyboards' may fit well within. - -import { ModifierKeyConstants} from '@keymanapp/common-types'; -import Codes from "./codes.js"; -import type KeyEvent from "./keyEvent.js"; -import type OutputTarget from "./outputTarget.js"; - -// The only members referenced are to produce warning and error logs. A little abstraction -// via an optional 'logger' interface can maintain it while facilitating a the split alluded -// to above. -// -// Alternatively, we could just... not take in the parameter at all, which'd also facilitate -// the future modularization effort. -import RuleBehavior from "./ruleBehavior.js"; +/* + * Keyman is copyright (C) SIL International. MIT License. + * + * Implementation of default rules + */ + +import { ModifierKeyConstants } from '@keymanapp/common-types'; +import Codes from './codes.js'; +import type KeyEvent from './keyEvent.js'; +import { type OutputTarget } from './outputTarget.interface.js'; export enum EmulationKeystrokes { Enter = '\n', Backspace = '\b' } +export class LogMessages { + errorLog?: string; + warningLog?: string; +} + /** * Defines a collection of static library functions that define KeymanWeb's default (implied) keyboard rule behaviors. */ export default class DefaultRules { - public constructor() { - } - codeForEvent(Lkc: KeyEvent) { return Codes.keyCodes[Lkc.kName] || Lkc.Lcode;; } @@ -35,7 +31,7 @@ export default class DefaultRules { * Serves as a default keycode lookup table. This may be referenced safely by mnemonic handling without fear of side-effects. * Also used by Processor.defaultRuleBehavior to generate output after filtering for special cases. */ - public forAny(Lkc: KeyEvent, isMnemonic: boolean, ruleBehavior?: RuleBehavior) { + public forAny(Lkc: KeyEvent, isMnemonic: boolean, logMessages?: LogMessages): string { var char = ''; // A pretty simple table of lookups, corresponding VERY closely to the original defaultKeyOutput. @@ -43,9 +39,9 @@ export default class DefaultRules { return char; } else if(!isMnemonic && ((char = this.forNumpadKeys(Lkc)) != null)) { return char; - } else if((char = this.forUnicodeKeynames(Lkc, ruleBehavior)) != null) { + } else if((char = this.forUnicodeKeynames(Lkc, logMessages)) != null) { return char; - } else if((char = this.forBaseKeys(Lkc, ruleBehavior)) != null) { + } else if((char = this.forBaseKeys(Lkc, logMessages)) != null) { return char; } else { // // For headless and embeddded, we may well allow '\t'. It's DOM mode that has other uses. @@ -152,7 +148,7 @@ export default class DefaultRules { // Test for fall back to U_xxxxxx key id // For this first test, we ignore the keyCode and use the keyName - public forUnicodeKeynames(Lkc: KeyEvent, ruleBehavior?: RuleBehavior) { + public forUnicodeKeynames(Lkc: KeyEvent, logMessages?: LogMessages) { const keyName = Lkc.kName; // Test for fall back to U_xxxxxx key id @@ -169,8 +165,8 @@ export default class DefaultRules { // Code points [U_0000 - U_001F] and [U_0080 - U_009F] refer to Unicode C0 and C1 control codes. // Check the codePoint number and do not allow output of these codes via U_xxxxxx shortcuts. // Also handles invalid identifiers (e.g. `U_ghij`) for which parseInt returns NaN - if(ruleBehavior) { - ruleBehavior.errorLog = ("Suppressing Unicode control code in " + keyName); + if(logMessages) { + logMessages.errorLog = ("Suppressing Unicode control code in " + keyName); } // We'll attempt to add valid chars continue; @@ -185,17 +181,17 @@ export default class DefaultRules { // Test for otherwise unimplemented keys on the the base default & shift layers. // Those keys must be blocked by keyboard rules if intentionally unimplemented; otherwise, this function will trigger. - public forBaseKeys(Lkc: KeyEvent, ruleBehavior?: RuleBehavior) { + public forBaseKeys(Lkc: KeyEvent, logMessages?: LogMessages) { let n = Lkc.Lcode; let keyShiftState = Lkc.Lmodifiers; // check if exact match to SHIFT's code. Only the 'default' and 'shift' layers should have default key outputs. // TODO: Extend to allow AltGr as well - better mnemonic support. - if(keyShiftState == ModifierKeyConstants.K_SHIFTFLAG) { + if (keyShiftState == ModifierKeyConstants.K_SHIFTFLAG) { keyShiftState = 1; } else if(keyShiftState != 0) { - if(ruleBehavior) { - ruleBehavior.warningLog = "KMW only defines default key output for the 'default' and 'shift' layers!"; + if(logMessages) { + logMessages.warningLog = "KMW only defines default key output for the 'default' and 'shift' layers!"; } return null; } @@ -216,8 +212,8 @@ export default class DefaultRules { return keyShiftState ? '|' : '\\'; } } catch (e) { - if(ruleBehavior) { - ruleBehavior.errorLog = "Error detected with default mapping for key: code = " + n + ", shift state = " + (keyShiftState == 1 ? 'shift' : 'default'); + if(logMessages) { + logMessages.errorLog = "Error detected with default mapping for key: code = " + n + ", shift state = " + (keyShiftState == 1 ? 'shift' : 'default'); } } diff --git a/common/web/keyboard-processor/src/index.ts b/web/src/engine/keyboard/src/index.ts similarity index 57% rename from common/web/keyboard-processor/src/index.ts rename to web/src/engine/keyboard/src/index.ts index f800691cfac..28b6331c219 100644 --- a/common/web/keyboard-processor/src/index.ts +++ b/web/src/engine/keyboard/src/index.ts @@ -24,23 +24,14 @@ export { export { default as SpacebarText } from "./keyboards/spacebarText.js"; export { default as StateKeyMap } from "./keyboards/stateKeyMap.js"; -export { default as Codes } from "./text/codes.js"; -export * from "./text/codes.js"; -export * from "./text/deadkeys.js"; -export { default as DefaultRules } from "./text/defaultRules.js"; -export * from "./text/defaultRules.js"; -export { default as KeyboardInterface } from "./text/kbdInterface.js"; -export * from "./text/kbdInterface.js"; -export { default as KeyboardProcessor } from "./text/keyboardProcessor.js"; -export * from "./text/keyboardProcessor.js"; -export { default as KeyEvent } from "./text/keyEvent.js"; -export * from "./text/keyEvent.js"; -export { default as KeyMapping } from "./text/keyMapping.js"; -export { default as OutputTarget } from "./text/outputTarget.js"; -export * from "./text/outputTarget.js"; -export { default as RuleBehavior } from "./text/ruleBehavior.js"; -export * from "./text/stringDivergence.js"; -export * from "./text/systemStores.js"; +export { default as Codes } from "./codes.js"; +export * from "./codes.js"; +export { default as DefaultRules } from "./defaultRules.js"; +export * from "./defaultRules.js"; +export { default as KeyEvent } from "./keyEvent.js"; +export * from "./keyEvent.js"; +export { default as KeyMapping } from "./keyMapping.js"; +export { OutputTarget } from "./outputTarget.interface.js"; export * from "@keymanapp/web-utils"; @@ -48,4 +39,4 @@ export * from "@keymanapp/web-utils"; // Without the line below... OutputTarget would likely be aliased there, as it's // the last `export { default as _ }` => `export * from` pairing seen above. -export default undefined; \ No newline at end of file +export default undefined; diff --git a/common/web/keyboard-processor/src/text/keyEvent.ts b/web/src/engine/keyboard/src/keyEvent.ts similarity index 96% rename from common/web/keyboard-processor/src/text/keyEvent.ts rename to web/src/engine/keyboard/src/keyEvent.ts index a38992a3a14..c359558b1fe 100644 --- a/common/web/keyboard-processor/src/text/keyEvent.ts +++ b/web/src/engine/keyboard/src/keyEvent.ts @@ -6,16 +6,16 @@ // a key event. The most straightforward way to integrate Web OSK events on other platforms is to have // other platforms recognize and utilize this type. -import type Keyboard from "../keyboards/keyboard.js"; +import type Keyboard from "./keyboards/keyboard.js"; import { type DeviceSpec } from "@keymanapp/web-utils"; import Codes from './codes.js'; -import DefaultRules from './defaultRules.js'; -import { ActiveKeyBase } from "../index.js"; +import DefaultRules from "./defaultRules.js"; +import { ActiveKeyBase } from './keyboards/activeLayout.js'; // Represents a probability distribution over a keyboard's keys. // Defined here to avoid compilation issues. -export type KeyDistribution = {keySpec: ActiveKeyBase, p: number}[]; +export type KeyDistribution = { keySpec: ActiveKeyBase, p: number }[]; /** * A simple instance of the standard 'default rules' for keystroke processing from the @@ -188,4 +188,4 @@ export default class KeyEvent implements KeyEventSpec { } } } -}; \ No newline at end of file +}; diff --git a/common/web/keyboard-processor/src/text/keyMapping.ts b/web/src/engine/keyboard/src/keyMapping.ts similarity index 100% rename from common/web/keyboard-processor/src/text/keyMapping.ts rename to web/src/engine/keyboard/src/keyMapping.ts diff --git a/common/web/keyboard-processor/src/keyboards/activeLayout.ts b/web/src/engine/keyboard/src/keyboards/activeLayout.ts similarity index 99% rename from common/web/keyboard-processor/src/keyboards/activeLayout.ts rename to web/src/engine/keyboard/src/keyboards/activeLayout.ts index 7b293db93c3..c440511f172 100644 --- a/common/web/keyboard-processor/src/keyboards/activeLayout.ts +++ b/web/src/engine/keyboard/src/keyboards/activeLayout.ts @@ -1,6 +1,6 @@ -import Codes from "../text/codes.js"; -import KeyEvent, { KeyEventSpec } from "../text/keyEvent.js"; -import KeyMapping from "../text/keyMapping.js"; +import Codes from "../codes.js"; +import KeyEvent, { KeyEventSpec } from "../keyEvent.js"; +import KeyMapping from "../keyMapping.js"; import { ButtonClasses, Layouts } from "./defaultLayouts.js"; import type { LayoutKey, LayoutSubKey, LayoutRow, LayoutLayer, LayoutFormFactor, ButtonClass } from "./defaultLayouts.js"; import type Keyboard from "./keyboard.js"; @@ -105,7 +105,7 @@ export class ActiveKeyBase { pad: ActiveKeyBase.DEFAULT_PAD }; - /** WARNING - DO NOT USE DIRECTLY outside of @keymanapp/keyboard-processor! */ + /** WARNING - DO NOT USE DIRECTLY outside of keyman/engine/keyboard! */ id: TouchLayout.TouchLayoutKeyId; text: string; hint?: string; diff --git a/common/web/keyboard-processor/src/keyboards/defaultLayouts.ts b/web/src/engine/keyboard/src/keyboards/defaultLayouts.ts similarity index 99% rename from common/web/keyboard-processor/src/keyboards/defaultLayouts.ts rename to web/src/engine/keyboard/src/keyboards/defaultLayouts.ts index 109da53a0e5..3b97659d4bb 100644 --- a/common/web/keyboard-processor/src/keyboards/defaultLayouts.ts +++ b/web/src/engine/keyboard/src/keyboards/defaultLayouts.ts @@ -16,7 +16,7 @@ import ButtonClasses = TouchLayout.TouchLayoutKeySp; export { ButtonClasses }; -import Codes from "../text/codes.js"; +import Codes from "../codes.js"; import type Keyboard from "./keyboard.js"; export interface EncodedVisualKeyboard { diff --git a/common/web/keyboard-processor/src/keyboards/keyboard.ts b/web/src/engine/keyboard/src/keyboards/keyboard.ts similarity index 99% rename from common/web/keyboard-processor/src/keyboards/keyboard.ts rename to web/src/engine/keyboard/src/keyboards/keyboard.ts index 5312d825a65..42463cd6e7f 100644 --- a/common/web/keyboard-processor/src/keyboards/keyboard.ts +++ b/web/src/engine/keyboard/src/keyboards/keyboard.ts @@ -1,16 +1,16 @@ -import Codes from "../text/codes.js"; +import Codes from "../codes.js"; import { EncodedVisualKeyboard, LayoutSpec, Layouts } from "./defaultLayouts.js"; import { ActiveKey, ActiveLayout, ActiveSubKey } from "./activeLayout.js"; -import KeyEvent from "../text/keyEvent.js"; -import type OutputTarget from "../text/outputTarget.js"; +import KeyEvent from "../keyEvent.js"; +import { type OutputTarget } from "../outputTarget.interface.js"; import { ModifierKeyConstants, TouchLayout } from "@keymanapp/common-types"; type TouchLayoutSpec = TouchLayout.TouchLayoutPlatform & { isDefault?: boolean}; -import type { ComplexKeyboardStore } from "../text/kbdInterface.js"; - import { Version, DeviceSpec } from "@keymanapp/web-utils"; import StateKeyMap from "./stateKeyMap.js"; +type ComplexKeyboardStore = ( string | { t: 'd', d: number } | { ['t']: 'b' })[]; + /** * Stores preprocessed properties of a keyboard for quick retrieval later. */ diff --git a/common/web/keyboard-processor/src/keyboards/keyboardHarness.ts b/web/src/engine/keyboard/src/keyboards/keyboardHarness.ts similarity index 97% rename from common/web/keyboard-processor/src/keyboards/keyboardHarness.ts rename to web/src/engine/keyboard/src/keyboards/keyboardHarness.ts index 671c8fefd7c..bb2e2d5ccb1 100644 --- a/common/web/keyboard-processor/src/keyboards/keyboardHarness.ts +++ b/web/src/engine/keyboard/src/keyboards/keyboardHarness.ts @@ -1,5 +1,6 @@ import Keyboard from "./keyboard.js"; -import Codes from "../text/codes.js"; +import Codes from "../codes.js"; +import { DeviceSpec } from '@keymanapp/web-utils'; /** * Defines members of the top-level `keyman` global object necessary to guarantee @@ -40,6 +41,8 @@ export const MinimalKeymanGlobal: KeyboardKeymanGlobal = { export class KeyboardHarness { public readonly _jsGlobal: any; public readonly keymanGlobal: KeyboardKeymanGlobal; + activeDevice: DeviceSpec; + /** * Constructs and configures a harness for receiving dynamically-loaded Keyman keyboards. diff --git a/common/web/keyboard-processor/src/keyboards/keyboardLoaderBase.ts b/web/src/engine/keyboard/src/keyboards/keyboardLoaderBase.ts similarity index 100% rename from common/web/keyboard-processor/src/keyboards/keyboardLoaderBase.ts rename to web/src/engine/keyboard/src/keyboards/keyboardLoaderBase.ts diff --git a/common/web/keyboard-processor/src/keyboards/keyboardProperties.ts b/web/src/engine/keyboard/src/keyboards/keyboardProperties.ts similarity index 100% rename from common/web/keyboard-processor/src/keyboards/keyboardProperties.ts rename to web/src/engine/keyboard/src/keyboards/keyboardProperties.ts diff --git a/common/web/keyboard-processor/src/keyboards/loaders/dom-keyboard-loader.ts b/web/src/engine/keyboard/src/keyboards/loaders/dom-keyboard-loader.ts similarity index 100% rename from common/web/keyboard-processor/src/keyboards/loaders/dom-keyboard-loader.ts rename to web/src/engine/keyboard/src/keyboards/loaders/dom-keyboard-loader.ts diff --git a/common/web/keyboard-processor/src/keyboards/loaders/domKeyboardLoader.ts b/web/src/engine/keyboard/src/keyboards/loaders/domKeyboardLoader.ts similarity index 97% rename from common/web/keyboard-processor/src/keyboards/loaders/domKeyboardLoader.ts rename to web/src/engine/keyboard/src/keyboards/loaders/domKeyboardLoader.ts index ea9cddecd42..fcb2c764cd6 100644 --- a/common/web/keyboard-processor/src/keyboards/loaders/domKeyboardLoader.ts +++ b/web/src/engine/keyboard/src/keyboards/loaders/domKeyboardLoader.ts @@ -2,7 +2,7 @@ /// -import { Keyboard, KeyboardHarness, KeyboardLoaderBase, KeyboardLoadErrorBuilder, MinimalKeymanGlobal } from '@keymanapp/keyboard-processor'; +import { Keyboard, KeyboardHarness, KeyboardLoaderBase, KeyboardLoadErrorBuilder, MinimalKeymanGlobal } from 'keyman/engine/keyboard'; import { ManagedPromise } from '@keymanapp/web-utils'; diff --git a/common/web/keyboard-processor/src/keyboards/loaders/node-keyboard-loader.ts b/web/src/engine/keyboard/src/keyboards/loaders/node-keyboard-loader.ts similarity index 100% rename from common/web/keyboard-processor/src/keyboards/loaders/node-keyboard-loader.ts rename to web/src/engine/keyboard/src/keyboards/loaders/node-keyboard-loader.ts diff --git a/common/web/keyboard-processor/src/keyboards/loaders/nodeKeyboardLoader.ts b/web/src/engine/keyboard/src/keyboards/loaders/nodeKeyboardLoader.ts similarity index 95% rename from common/web/keyboard-processor/src/keyboards/loaders/nodeKeyboardLoader.ts rename to web/src/engine/keyboard/src/keyboards/loaders/nodeKeyboardLoader.ts index 143e1c16c11..b9f6a7cdfef 100644 --- a/common/web/keyboard-processor/src/keyboards/loaders/nodeKeyboardLoader.ts +++ b/web/src/engine/keyboard/src/keyboards/loaders/nodeKeyboardLoader.ts @@ -1,4 +1,4 @@ -import { Keyboard, KeyboardHarness, KeyboardLoaderBase, KeyboardLoadErrorBuilder, MinimalKeymanGlobal } from '@keymanapp/keyboard-processor'; +import { Keyboard, KeyboardHarness, KeyboardLoaderBase, KeyboardLoadErrorBuilder, MinimalKeymanGlobal } from 'keyman/engine/keyboard'; import vm from 'vm'; import fs from 'fs'; diff --git a/web/src/engine/keyboard/src/keyboards/loaders/tsconfig.dom.json b/web/src/engine/keyboard/src/keyboards/loaders/tsconfig.dom.json new file mode 100644 index 00000000000..1dd122f5485 --- /dev/null +++ b/web/src/engine/keyboard/src/keyboards/loaders/tsconfig.dom.json @@ -0,0 +1,13 @@ +{ + "extends": "../../../../../../tsconfig.base.json", + "compilerOptions": { + "baseUrl": "../../../", + "outDir": "../../../../../../build/engine/keyboard/obj/keyboards/loaders/", + "tsBuildInfoFile": "../../../../../../build/engine/keyboard/obj/keyboards/loaders/tsconfig.dom.tsbuildinfo", + "rootDir": "." + }, + "references": [ + { "path": "../../../tsconfig.json" } + ], + "include": ["dom-keyboard-loader.ts", "domKeyboardLoader.ts"], +} diff --git a/web/src/engine/keyboard/src/keyboards/loaders/tsconfig.node.json b/web/src/engine/keyboard/src/keyboards/loaders/tsconfig.node.json new file mode 100644 index 00000000000..4a06decf4c4 --- /dev/null +++ b/web/src/engine/keyboard/src/keyboards/loaders/tsconfig.node.json @@ -0,0 +1,14 @@ +{ + "extends": "../../../../../../tsconfig.base.json", + "compilerOptions": { + "types": [ "node" ], + "baseUrl": "../../../", + "outDir": "../../../../../../build/engine/keyboard/obj/keyboards/loaders/", + "tsBuildInfoFile": "../../../../../../build/engine/keyboard/obj/keyboards/loaders/tsconfig.node.tsbuildinfo", + "rootDir": "." + }, + "references": [ + { "path": "../../../tsconfig.json" } + ], + "include": ["node-keyboard-loader.ts", "nodeKeyboardLoader.ts"], +} diff --git a/common/web/keyboard-processor/src/keyboards/spacebarText.ts b/web/src/engine/keyboard/src/keyboards/spacebarText.ts similarity index 100% rename from common/web/keyboard-processor/src/keyboards/spacebarText.ts rename to web/src/engine/keyboard/src/keyboards/spacebarText.ts diff --git a/common/web/keyboard-processor/src/keyboards/stateKeyMap.ts b/web/src/engine/keyboard/src/keyboards/stateKeyMap.ts similarity index 65% rename from common/web/keyboard-processor/src/keyboards/stateKeyMap.ts rename to web/src/engine/keyboard/src/keyboards/stateKeyMap.ts index c8e9ee8b763..e82ff417c4e 100644 --- a/common/web/keyboard-processor/src/keyboards/stateKeyMap.ts +++ b/web/src/engine/keyboard/src/keyboards/stateKeyMap.ts @@ -1,5 +1,5 @@ /** - * Provided by @keymanapp/keyboard-processor's `KeyboardProcessor` class and utilized by the OSK + * Provided by keyman/engine/keyboard's `KeyboardProcessor` class and utilized by the OSK * to provide state feedback on any corresponding keys visible in the OSK. */ export default interface StateKeyMap { diff --git a/web/src/engine/keyboard/src/outputTarget.interface.ts b/web/src/engine/keyboard/src/outputTarget.interface.ts new file mode 100644 index 00000000000..410094c812c --- /dev/null +++ b/web/src/engine/keyboard/src/outputTarget.interface.ts @@ -0,0 +1,104 @@ +export interface OutputTarget { + /** + * Signifies that this OutputTarget has no default key processing behaviors. This should be false + * for OutputTargets backed by web elements like HTMLInputElement or HTMLTextAreaElement. + */ + get isSynthetic(): boolean; + + resetContext(): void; + + hasDeadkeyMatch(n: number, d: number): boolean; + + insertDeadkeyBeforeCaret(d: number): void; + + /** + * Clears any selected text within the wrapper's element(s). + * Silently does nothing if no such text exists. + */ + clearSelection(): void; + + /** + * Clears any cached selection-related state values. + */ + invalidateSelection(): void; + + /** + * Indicates whether or not the underlying element has its own selection (input, textarea) + * or is part of (or possesses) the DOM's active selection. Don't confuse with isSelectionEmpty(). + * + * TODO: rename to supportsOwnSelection + */ + hasSelection(): boolean; + + /** + * Returns true if there is no current selection -- that is, the selection range is empty + */ + isSelectionEmpty(): boolean; + + /** + * Returns an index corresponding to the caret's position for use with deadkeys. + */ + getDeadkeyCaret(): number; + + /** + * Relative to the caret, gets the current context within the wrapper's element. + */ + getTextBeforeCaret(): string; + + /** + * Gets the element's-currently selected text. + */ + getSelectedText(): string; + + /** + * Relative to the caret (and/or active selection), gets the element's text after the caret, + * excluding any actively selected text that would be immediately replaced upon text entry. + */ + getTextAfterCaret(): string; + + /** + * Gets the element's full text, including any text that is actively selected. + */ + getText(): string; + + /** + * Performs context deletions (from the left of the caret) as needed by the KeymanWeb engine and + * corrects the location of any affected deadkeys. + * + * Does not delete deadkeys (b/c KMW 1 & 2 behavior maintenance). + * @param dn The number of characters to delete. If negative, context will be left unchanged. + */ + deleteCharsBeforeCaret(dn: number): void; + + /** + * Inserts text immediately before the caret's current position, moving the caret after the + * newly inserted text in the process along with any affected deadkeys. + * + * @param s Text to insert before the caret's current position. + */ + insertTextBeforeCaret(s: string): void; + + /** + * Allows element-specific handling for ENTER key inputs. Conceptually, this should usually + * correspond to `insertTextBeforeCaret('\n'), but actual implementation will vary greatly among + * elements. + */ + handleNewlineAtCaret(): void; + + /** + * Saves element-specific state properties prone to mutation, enabling restoration after + * text-output operations. + */ + saveProperties(): void; + + /** + * Restores previously-saved element-specific state properties. Designed for use after text-output + * ops to facilitate more-seamless web-dev and user interactions. + */ + restoreProperties(): void; + + /** + * Generates a synthetic event on the underlying element, signalling that its value has changed. + */ + doInputEvent(): void; +} diff --git a/common/web/keyboard-processor/tsconfig.all.json b/web/src/engine/keyboard/tsconfig.all.json similarity index 66% rename from common/web/keyboard-processor/tsconfig.all.json rename to web/src/engine/keyboard/tsconfig.all.json index dabb262d9cb..1ee44d3e70e 100644 --- a/common/web/keyboard-processor/tsconfig.all.json +++ b/web/src/engine/keyboard/tsconfig.all.json @@ -1,15 +1,14 @@ { - "extends": "../tsconfig.kmw-main-base.json", + "extends": "../../../tsconfig.base.json", "compilerOptions": { "baseUrl": "./", - "outDir": "build/obj/", - "tsBuildInfoFile": "build/obj/tsconfig.all.tsbuildinfo", + "outDir": "../../../build/engine/keyboard/obj/", + "tsBuildInfoFile": "../../../build/engine/keyboard/obj/tsconfig.all.tsbuildinfo", "rootDir": "./src/" }, "references": [ { "path": "./src/keyboards/loaders/tsconfig.dom.json" }, { "path": "./src/keyboards/loaders/tsconfig.node.json" }, - { "path": "./tests/tsconfig.json" }, ], // Actual main-body compilation is in tsconfig.json. This config is just a wrapper // to trigger all three components at once. diff --git a/web/src/engine/keyboard/tsconfig.json b/web/src/engine/keyboard/tsconfig.json new file mode 100644 index 00000000000..84d96c8591f --- /dev/null +++ b/web/src/engine/keyboard/tsconfig.json @@ -0,0 +1,12 @@ +// Is used by the keyboard-loader submodules. +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "baseUrl": "./", + "outDir": "../../../build/engine/keyboard/obj/", + "tsBuildInfoFile": "../../../build/engine/keyboard/obj/tsconfig.tsbuildinfo", + "rootDir": "./src/" + }, + "include": [ "./src/**/*.ts"], + "exclude": ["./src/keyboards/loaders/**/*.ts"] +} diff --git a/web/src/engine/main/build.sh b/web/src/engine/main/build.sh index cceb9b4b8b7..77673626e02 100755 --- a/web/src/engine/main/build.sh +++ b/web/src/engine/main/build.sh @@ -14,12 +14,13 @@ SUBPROJECT_NAME=engine/main builder_describe "Builds the Keyman Engine for Web's common top-level base classes." \ "@/common/web/keyman-version" \ - "@/common/web/keyboard-processor" \ - "@/common/predictive-text" \ + "@/web/src/engine/keyboard" \ "@/web/src/engine/interfaces build" \ "@/web/src/engine/device-detect build" \ - "@/web/src/engine/package-cache build" \ + "@/web/src/engine/js-processor build" \ + "@/web/src/engine/keyboard-storage build" \ "@/web/src/engine/osk build" \ + "@/web/src/engine/predictive-text/worker-main" \ "@/developer/src/kmc-model test" \ "clean" \ "configure" \ diff --git a/web/src/engine/main/src/contextManagerBase.ts b/web/src/engine/main/src/contextManagerBase.ts index c3f95810033..17fed71bd2e 100644 --- a/web/src/engine/main/src/contextManagerBase.ts +++ b/web/src/engine/main/src/contextManagerBase.ts @@ -1,6 +1,7 @@ import { EventEmitter } from 'eventemitter3'; -import { ManagedPromise, type Keyboard, type KeyboardInterface, type OutputTarget } from '@keymanapp/keyboard-processor'; -import { StubAndKeyboardCache, type KeyboardStub } from 'keyman/engine/package-cache'; +import { ManagedPromise, type Keyboard } from 'keyman/engine/keyboard'; +import { type KeyboardInterface, type OutputTarget } from 'keyman/engine/js-processor'; +import { StubAndKeyboardCache, type KeyboardStub } from 'keyman/engine/keyboard-storage'; import { PredictionContext } from 'keyman/engine/interfaces'; import { EngineConfiguration } from './engineConfiguration.js'; diff --git a/web/src/engine/main/src/engineConfiguration.ts b/web/src/engine/main/src/engineConfiguration.ts index c66d3bd805d..aac559acabd 100644 --- a/web/src/engine/main/src/engineConfiguration.ts +++ b/web/src/engine/main/src/engineConfiguration.ts @@ -1,9 +1,10 @@ import { EventEmitter } from "eventemitter3"; -import { DeviceSpec, KeyboardProperties, ManagedPromise, OutputTarget, physicalKeyDeviceAlias, RuleBehavior, SpacebarText } from "@keymanapp/keyboard-processor"; +import { DeviceSpec, KeyboardProperties, ManagedPromise, physicalKeyDeviceAlias, SpacebarText } from "keyman/engine/keyboard"; +import { OutputTarget, RuleBehavior } from 'keyman/engine/js-processor'; import { PathConfiguration, PathOptionDefaults, PathOptionSpec } from "keyman/engine/interfaces"; import { Device } from "keyman/engine/device-detect"; -import { KeyboardStub } from "keyman/engine/package-cache"; +import { KeyboardStub } from "keyman/engine/keyboard-storage"; interface EventMap { 'spacebartext': (mode: SpacebarText) => void; diff --git a/web/src/engine/main/src/hardKeyboard.ts b/web/src/engine/main/src/hardKeyboard.ts index a07904f4a6b..60f2b362004 100644 --- a/web/src/engine/main/src/hardKeyboard.ts +++ b/web/src/engine/main/src/hardKeyboard.ts @@ -1,6 +1,7 @@ import { EventEmitter } from "eventemitter3"; -import { Keyboard, KeyMapping, KeyEvent, type RuleBehavior, Codes } from "@keymanapp/keyboard-processor"; -import { KeyEventSourceInterface } from 'keyman/engine/events'; +import { Keyboard, KeyMapping, KeyEvent, Codes } from "keyman/engine/keyboard"; +import { type RuleBehavior } from 'keyman/engine/js-processor'; +import { KeyEventSourceInterface } from 'keyman/engine/osk'; import { ModifierKeyConstants } from '@keymanapp/common-types'; interface EventMap { diff --git a/web/src/engine/main/src/headless/contextWindow.ts b/web/src/engine/main/src/headless/contextWindow.ts index 2adb4f0b6a6..9fcd1458b2a 100644 --- a/web/src/engine/main/src/headless/contextWindow.ts +++ b/web/src/engine/main/src/headless/contextWindow.ts @@ -1,4 +1,4 @@ -import { Mock } from "@keymanapp/keyboard-processor"; +import { Mock } from "keyman/engine/js-processor"; export default class ContextWindow implements Context { // Used to limit the range of context replicated for use of keyboard rules within diff --git a/web/src/engine/main/src/headless/inputProcessor.ts b/web/src/engine/main/src/headless/inputProcessor.ts index 7b89a0e242a..6d8904c563d 100644 --- a/web/src/engine/main/src/headless/inputProcessor.ts +++ b/web/src/engine/main/src/headless/inputProcessor.ts @@ -7,20 +7,19 @@ import { LanguageProcessor } from "./languageProcessor.js"; import type { ModelSpec } from "keyman/engine/interfaces"; import { globalObject, DeviceSpec } from "@keymanapp/web-utils"; +import { Codes, type Keyboard, type KeyEvent } from "keyman/engine/keyboard"; import { type Alternate, - Codes, isEmptyTransform, - type Keyboard, KeyboardInterface, KeyboardProcessor, - type KeyEvent, Mock, type OutputTarget, - type ProcessorInitOptions, RuleBehavior, - SystemStoreIDs, -} from "@keymanapp/keyboard-processor"; + type ProcessorInitOptions, + SystemStoreIDs +} from 'keyman/engine/js-processor'; + import { TranscriptionCache } from "./transcriptionCache.js"; export class InputProcessor { diff --git a/web/src/engine/main/src/headless/languageProcessor.ts b/web/src/engine/main/src/headless/languageProcessor.ts index 30a676bb6aa..cd39afc672b 100644 --- a/web/src/engine/main/src/headless/languageProcessor.ts +++ b/web/src/engine/main/src/headless/languageProcessor.ts @@ -1,6 +1,6 @@ import { EventEmitter } from "eventemitter3"; import { LMLayer } from "@keymanapp/lexical-model-layer/web"; -import { OutputTarget, Transcription, Mock } from "@keymanapp/keyboard-processor"; +import { OutputTarget, Transcription, Mock } from "keyman/engine/js-processor"; import { LanguageProcessorEventMap, ModelSpec, StateChangeEnum, ReadySuggestions } from 'keyman/engine/interfaces'; import ContextWindow from "./contextWindow.js"; import { TranscriptionCache } from "./transcriptionCache.js"; @@ -336,6 +336,7 @@ export class LanguageProcessor extends EventEmitter { public shutdown() { this.lmEngine.shutdown(); + this.removeAllListeners(); } public get isActive(): boolean { diff --git a/web/src/engine/main/src/headless/transcriptionCache.ts b/web/src/engine/main/src/headless/transcriptionCache.ts index c173009171c..af05853e174 100644 --- a/web/src/engine/main/src/headless/transcriptionCache.ts +++ b/web/src/engine/main/src/headless/transcriptionCache.ts @@ -1,4 +1,4 @@ -import { Transcription } from "@keymanapp/keyboard-processor"; +import { Transcription } from "keyman/engine/js-processor"; const TRANSCRIPTION_BUFFER_SIZE = 10; diff --git a/web/src/engine/main/src/keyboardInterface.ts b/web/src/engine/main/src/keyboardInterface.ts index a18bafecd07..10194785948 100644 --- a/web/src/engine/main/src/keyboardInterface.ts +++ b/web/src/engine/main/src/keyboardInterface.ts @@ -1,7 +1,6 @@ -import { - KeyboardInterface as KeyboardInterfaceBase, KeyboardObject, -} from "@keymanapp/keyboard-processor"; -import { KeyboardStub, RawKeyboardStub, toUnprefixedKeyboardId as unprefixed } from 'keyman/engine/package-cache'; +import { KeyboardObject } from "keyman/engine/keyboard"; +import { KeyboardInterface as KeyboardInterfaceBase } from 'keyman/engine/js-processor'; +import { KeyboardStub, RawKeyboardStub, toUnprefixedKeyboardId as unprefixed } from 'keyman/engine/keyboard-storage'; import { ContextManagerBase } from './contextManagerBase.js'; import { VariableStoreCookieSerializer } from "./variableStoreCookieSerializer.js"; diff --git a/web/src/engine/main/src/keymanEngine.ts b/web/src/engine/main/src/keymanEngine.ts index eaa54e59945..beced96d182 100644 --- a/web/src/engine/main/src/keymanEngine.ts +++ b/web/src/engine/main/src/keymanEngine.ts @@ -1,8 +1,9 @@ -import { type Keyboard, KeyboardKeymanGlobal, ProcessorInitOptions } from "@keymanapp/keyboard-processor"; -import { DOMKeyboardLoader as KeyboardLoader } from "@keymanapp/keyboard-processor/dom-keyboard-loader"; +import { type KeyEvent, type Keyboard, KeyboardKeymanGlobal } from "keyman/engine/keyboard"; +import { OutputTarget, ProcessorInitOptions, RuleBehavior } from 'keyman/engine/js-processor'; +import { DOMKeyboardLoader as KeyboardLoader } from "keyman/engine/keyboard/dom-keyboard-loader"; import { InputProcessor } from './headless/inputProcessor.js'; import { OSKView } from "keyman/engine/osk"; -import { KeyboardRequisitioner, ModelCache, toUnprefixedKeyboardId as unprefixed } from "keyman/engine/package-cache"; +import { KeyboardRequisitioner, ModelCache, toUnprefixedKeyboardId as unprefixed } from "keyman/engine/keyboard-storage"; import { ModelSpec, PredictionContext } from "keyman/engine/interfaces"; import { EngineConfiguration, InitOptionSpec } from "./engineConfiguration.js"; @@ -10,8 +11,8 @@ import KeyboardInterface from "./keyboardInterface.js"; import { ContextManagerBase } from "./contextManagerBase.js"; import HardKeyboardBase from "./hardKeyboard.js"; import { LegacyAPIEvents } from "./legacyAPIEvents.js"; -import { KeyEventHandler, EventNames, EventListener, LegacyEventEmitter } from "keyman/engine/events"; -import DOMCloudRequester from "keyman/engine/package-cache/dom-requester"; +import { EventNames, EventListener, LegacyEventEmitter } from "keyman/engine/events"; +import DOMCloudRequester from "keyman/engine/keyboard-storage/dom-requester"; import KEYMAN_VERSION from "@keymanapp/keyman-version"; // From https://stackoverflow.com/a/69328045 @@ -29,6 +30,9 @@ function determineBaseLayout(): string { } } +export type KeyEventFullResultCallback = (result: RuleBehavior, error?: Error) => void; +export type KeyEventFullHandler = (event: KeyEvent, callback?: KeyEventFullResultCallback) => void; + export default class KeymanEngine< Configuration extends EngineConfiguration, ContextManager extends ContextManagerBase, @@ -47,7 +51,7 @@ export default class KeymanEngine< protected keyEventRefocus?: () => void; - private keyEventListener: KeyEventHandler = (event, callback) => { + private keyEventListener: KeyEventFullHandler = (event, callback) => { const outputTarget = this.contextManager.activeTarget; if(!this.contextManager.activeKeyboard || !outputTarget) { @@ -57,6 +61,10 @@ export default class KeymanEngine< return; } + if(!this.core.languageProcessor.mayCorrect) { + event.keyDistribution = []; + } + if(this.keyEventRefocus) { // Do anything needed to guarantee that the outputTarget stays active (`app/browser`: maintains focus). // (Interaction with the OSK may have de-focused the element providing active context; @@ -237,10 +245,12 @@ export default class KeymanEngine< this.modelCache = new ModelCache(); const kbdCache = this.keyboardRequisitioner.cache; + const keyboardProcessor = this.core.keyboardProcessor; + const predictionContext = new PredictionContext(this.core.languageProcessor, () => keyboardProcessor.layerId); this.contextManager.configure({ resetContext: (target) => { // Could reset the target's deadkeys here, but it's really more of a 'core' task. - // So we delegate that to keyboard-processor. + // So we delegate that to keyboard. if(this.osk) { this.osk.batchLayoutAfter(() => { this.core.resetContext(target); @@ -249,10 +259,27 @@ export default class KeymanEngine< this.core.resetContext(target); } }, - predictionContext: new PredictionContext(this.core.languageProcessor, this.core.keyboardProcessor), + predictionContext: predictionContext, keyboardCache: this.keyboardRequisitioner.cache }); + /* + * Handler for post-processing once a suggestion has been applied. + * + * This is called after the suggestion is applied but _before_ new + * predictions are requested based on the resulting context. + */ + this.core.languageProcessor.on('suggestionapplied', () => { + // Tell the keyboard that the current layer has not changed + keyboardProcessor.newLayerStore.set(''); + keyboardProcessor.oldLayerStore.set(''); + // Call the keyboard's entry point. + keyboardProcessor.processPostKeystroke(keyboardProcessor.contextDevice, predictionContext.currentTarget as OutputTarget) + // If we have a RuleBehavior as a result, run it on the target. This should + // only change system store and variable store values. + ?.finalize(keyboardProcessor, predictionContext.currentTarget as OutputTarget, true); + }); + // #region Event handler wiring this.config.on('spacebartext', () => { // On change of spacebar-text mode, we currently need a layout refresh to update the diff --git a/web/src/engine/main/src/variableStoreCookieSerializer.ts b/web/src/engine/main/src/variableStoreCookieSerializer.ts index 35b51150c0e..62c250329aa 100644 --- a/web/src/engine/main/src/variableStoreCookieSerializer.ts +++ b/web/src/engine/main/src/variableStoreCookieSerializer.ts @@ -1,4 +1,4 @@ -import { VariableStore, VariableStoreSerializer } from "@keymanapp/keyboard-processor"; +import { VariableStore, VariableStoreSerializer } from 'keyman/engine/js-processor'; import { CookieSerializer } from "keyman/engine/dom-utils"; // While there's little reason we couldn't store all of a keyboard's store values within diff --git a/web/src/engine/main/tsconfig.json b/web/src/engine/main/tsconfig.json index 9711d06a777..d2e0580161c 100644 --- a/web/src/engine/main/tsconfig.json +++ b/web/src/engine/main/tsconfig.json @@ -13,7 +13,8 @@ "references": [ { "path": "../device-detect" }, { "path": "../osk" }, - { "path": "../package-cache" }, + { "path": "../keyboard-storage" }, { "path": "../interfaces" }, + { "path": "../js-processor" } ] } diff --git a/web/src/engine/osk/build.sh b/web/src/engine/osk/build.sh index 4538b79d923..0d676a2d3ef 100755 --- a/web/src/engine/osk/build.sh +++ b/web/src/engine/osk/build.sh @@ -13,7 +13,7 @@ SUBPROJECT_NAME=engine/osk # ################################ Main script ################################ builder_describe "Builds the Keyman Engine for Web's On-Screen Keyboard package (OSK)." \ - "@/common/web/keyboard-processor build" \ + "@/web/src/engine/keyboard build" \ "@/common/web/gesture-recognizer build" \ "@/web/src/engine/interfaces build" \ "@/web/src/engine/dom-utils build" \ diff --git a/web/src/engine/osk/src/banner/banner.ts b/web/src/engine/osk/src/banner/banner.ts index 39b3ddc708c..e38b6b6625d 100644 --- a/web/src/engine/osk/src/banner/banner.ts +++ b/web/src/engine/osk/src/banner/banner.ts @@ -1,4 +1,4 @@ -import { Keyboard, KeyboardProperties } from '@keymanapp/keyboard-processor'; +import { Keyboard, KeyboardProperties } from 'keyman/engine/keyboard'; import { createUnselectableElement } from 'keyman/engine/dom-utils'; // Base class for a banner above the keyboard in the OSK diff --git a/web/src/engine/osk/src/banner/bannerController.ts b/web/src/engine/osk/src/banner/bannerController.ts index a53ea2f9e92..7fbc8ceb9bb 100644 --- a/web/src/engine/osk/src/banner/bannerController.ts +++ b/web/src/engine/osk/src/banner/bannerController.ts @@ -6,7 +6,7 @@ import { BannerView } from './bannerView.js'; import { Banner } from './banner.js'; import { BlankBanner } from './blankBanner.js'; import { HTMLBanner } from './htmlBanner.js'; -import { Keyboard, KeyboardProperties } from '@keymanapp/keyboard-processor'; +import { Keyboard, KeyboardProperties } from 'keyman/engine/keyboard'; export class BannerController { private container: BannerView; diff --git a/web/src/engine/osk/src/banner/suggestionBanner.ts b/web/src/engine/osk/src/banner/suggestionBanner.ts index adf6a5ea200..34455fd0619 100644 --- a/web/src/engine/osk/src/banner/suggestionBanner.ts +++ b/web/src/engine/osk/src/banner/suggestionBanner.ts @@ -13,7 +13,7 @@ import { import { BANNER_GESTURE_SET } from './bannerGestureSet.js'; -import { DeviceSpec, Keyboard, KeyboardProperties, timedPromise } from '@keymanapp/keyboard-processor'; +import { DeviceSpec, Keyboard, KeyboardProperties, timedPromise } from 'keyman/engine/keyboard'; import { Banner } from './banner.js'; import { ParsedLengthStyle } from '../lengthStyle.js'; import { getFontSizeStyle } from '../fontSizeUtils.js'; diff --git a/web/src/engine/osk/src/components/helpPageView.ts b/web/src/engine/osk/src/components/helpPageView.ts index 7c66708be9a..af489a2686f 100644 --- a/web/src/engine/osk/src/components/helpPageView.ts +++ b/web/src/engine/osk/src/components/helpPageView.ts @@ -1,4 +1,4 @@ -import { Keyboard } from '@keymanapp/keyboard-processor'; +import { Keyboard } from 'keyman/engine/keyboard'; import KeyboardView from './keyboardView.interface.js'; import { ParsedLengthStyle } from "../lengthStyle.js"; diff --git a/web/src/engine/osk/src/components/titleBar.ts b/web/src/engine/osk/src/components/titleBar.ts index 815b8a1e4e0..dd4eba3df7f 100644 --- a/web/src/engine/osk/src/components/titleBar.ts +++ b/web/src/engine/osk/src/components/titleBar.ts @@ -1,6 +1,6 @@ import { EventEmitter } from 'eventemitter3'; -import { Keyboard } from '@keymanapp/keyboard-processor'; +import { Keyboard } from 'keyman/engine/keyboard'; import OSKViewComponent from './oskViewComponent.interface.js'; import { ParsedLengthStyle } from '../lengthStyle.js'; diff --git a/web/src/engine/osk/src/correctionLayout.ts b/web/src/engine/osk/src/correctionLayout.ts index c3c0ffa8865..ba5f902ff47 100644 --- a/web/src/engine/osk/src/correctionLayout.ts +++ b/web/src/engine/osk/src/correctionLayout.ts @@ -1,4 +1,4 @@ -import { ActiveKey, ActiveKeyBase, ActiveLayer, ActiveRow, Codes } from "@keymanapp/keyboard-processor"; +import { ActiveKey, ActiveKeyBase, ActiveLayer, ActiveRow, Codes } from "keyman/engine/keyboard"; /** * Defines correction-layout mappings for keys to be considered by diff --git a/web/src/engine/osk/src/corrections.ts b/web/src/engine/osk/src/corrections.ts index 39c5ae2ef37..808e1c6dce5 100644 --- a/web/src/engine/osk/src/corrections.ts +++ b/web/src/engine/osk/src/corrections.ts @@ -1,4 +1,4 @@ -import { ActiveKeyBase, KeyDistribution } from "@keymanapp/keyboard-processor"; +import { ActiveKeyBase, KeyDistribution } from "keyman/engine/keyboard"; import { CorrectionLayout } from "./correctionLayout.js"; /** diff --git a/web/src/engine/osk/src/index.ts b/web/src/engine/osk/src/index.ts index 388ea32750b..f010697d966 100644 --- a/web/src/engine/osk/src/index.ts +++ b/web/src/engine/osk/src/index.ts @@ -1,9 +1,10 @@ -export { Codes, DeviceSpec, Keyboard, KeyboardProperties, SpacebarText } from '@keymanapp/keyboard-processor'; +export { Codes, DeviceSpec, Keyboard, KeyboardProperties, SpacebarText } from 'keyman/engine/keyboard'; export { default as OSKView } from './views/oskView.js'; export { default as FloatingOSKView, FloatingOSKViewConfiguration } from './views/floatingOskView.js'; export { default as AnchoredOSKView } from './views/anchoredOskView.js'; export { default as InlinedOSKView } from './views/inlinedOskView.js'; +export { type KeyEventResultCallback, type KeyEventHandler, KeyEventSourceInterface } from './views/keyEventSource.interface.js'; export { BannerController } from './banner/bannerController.js'; // Is referenced by at least one desktop UI module. export { FloatingOSKCookie as FloatingOSKViewCookie } from './views/floatingOskCookie.js'; diff --git a/web/src/engine/osk/src/input/gestures/browser/flick.ts b/web/src/engine/osk/src/input/gestures/browser/flick.ts index c83a85121ed..91d20718714 100644 --- a/web/src/engine/osk/src/input/gestures/browser/flick.ts +++ b/web/src/engine/osk/src/input/gestures/browser/flick.ts @@ -1,7 +1,7 @@ import { type KeyElement } from '../../../keyElement.js'; import VisualKeyboard from '../../../visualKeyboard.js'; -import { ActiveKey, ActiveKeyBase, ActiveSubKey, KeyDistribution, KeyEvent } from '@keymanapp/keyboard-processor'; +import { ActiveKey, ActiveKeyBase, ActiveSubKey, KeyDistribution, KeyEvent } from 'keyman/engine/keyboard'; import { ConfigChangeClosure, CumulativePathStats, GestureRecognizerConfiguration, GestureSequence, GestureSource, GestureSourceSubview, InputSample, RecognitionZoneSource } from '@keymanapp/gesture-recognizer'; import { GestureHandler } from '../gestureHandler.js'; import { distributionFromDistanceMaps } from '../../../corrections.js'; diff --git a/web/src/engine/osk/src/input/gestures/browser/modipress.ts b/web/src/engine/osk/src/input/gestures/browser/modipress.ts index f3670944a46..3ffd4c206c9 100644 --- a/web/src/engine/osk/src/input/gestures/browser/modipress.ts +++ b/web/src/engine/osk/src/input/gestures/browser/modipress.ts @@ -1,7 +1,7 @@ import { type KeyElement } from '../../../keyElement.js'; import VisualKeyboard from '../../../visualKeyboard.js'; -import { KeyDistribution, ActiveKeyBase } from '@keymanapp/keyboard-processor'; +import { KeyDistribution, ActiveKeyBase } from 'keyman/engine/keyboard'; import { GestureSequence } from '@keymanapp/gesture-recognizer'; import { GestureHandler } from '../gestureHandler.js'; diff --git a/web/src/engine/osk/src/input/gestures/browser/multitap.ts b/web/src/engine/osk/src/input/gestures/browser/multitap.ts index 23c4898ff0f..99e50810f9c 100644 --- a/web/src/engine/osk/src/input/gestures/browser/multitap.ts +++ b/web/src/engine/osk/src/input/gestures/browser/multitap.ts @@ -1,7 +1,7 @@ import { type KeyElement } from '../../../keyElement.js'; import VisualKeyboard from '../../../visualKeyboard.js'; -import { ActiveSubKey, ActiveKey, KeyDistribution, ActiveKeyBase } from '@keymanapp/keyboard-processor'; +import { ActiveSubKey, ActiveKey, KeyDistribution, ActiveKeyBase } from 'keyman/engine/keyboard'; import { GestureSequence, GestureStageReport } from '@keymanapp/gesture-recognizer'; import { GestureHandler } from '../gestureHandler.js'; import { distributionFromDistanceMaps } from '../../../corrections.js'; diff --git a/web/src/engine/osk/src/input/gestures/browser/oskSubKey.ts b/web/src/engine/osk/src/input/gestures/browser/oskSubKey.ts index 7640c3b20e7..abf3a9fb801 100644 --- a/web/src/engine/osk/src/input/gestures/browser/oskSubKey.ts +++ b/web/src/engine/osk/src/input/gestures/browser/oskSubKey.ts @@ -1,4 +1,4 @@ -import { ActiveSubKey } from '@keymanapp/keyboard-processor'; +import { ActiveSubKey } from 'keyman/engine/keyboard'; import OSKKey from '../../../keyboard-layout/oskKey.js'; import { KeyData, KeyElement, link } from '../../../keyElement.js'; import VisualKeyboard from '../../../visualKeyboard.js'; diff --git a/web/src/engine/osk/src/input/gestures/browser/subkeyPopup.ts b/web/src/engine/osk/src/input/gestures/browser/subkeyPopup.ts index e9388e96f2a..d33d3192116 100644 --- a/web/src/engine/osk/src/input/gestures/browser/subkeyPopup.ts +++ b/web/src/engine/osk/src/input/gestures/browser/subkeyPopup.ts @@ -3,7 +3,7 @@ import { type KeyElement } from '../../../keyElement.js'; import OSKBaseKey from '../../../keyboard-layout/oskBaseKey.js'; import VisualKeyboard from '../../../visualKeyboard.js'; -import { DeviceSpec, ActiveSubKey, KeyDistribution, ActiveKeyBase } from '@keymanapp/keyboard-processor'; +import { DeviceSpec, ActiveSubKey, KeyDistribution, ActiveKeyBase } from 'keyman/engine/keyboard'; import { ConfigChangeClosure, GestureRecognizerConfiguration, GestureSequence, PaddedZoneSource, RecognitionZoneSource } from '@keymanapp/gesture-recognizer'; import { GestureHandler } from '../gestureHandler.js'; import { CorrectionLayout, CorrectionLayoutEntry } from '../../../correctionLayout.js'; diff --git a/web/src/engine/osk/src/input/gestures/gestureHandler.ts b/web/src/engine/osk/src/input/gestures/gestureHandler.ts index 4c45c39e776..3c585de6465 100644 --- a/web/src/engine/osk/src/input/gestures/gestureHandler.ts +++ b/web/src/engine/osk/src/input/gestures/gestureHandler.ts @@ -1,4 +1,4 @@ -import { ActiveKeyBase, KeyDistribution } from "@keymanapp/keyboard-processor"; +import { ActiveKeyBase, KeyDistribution } from "keyman/engine/keyboard"; export interface GestureHandler { /** diff --git a/web/src/engine/osk/src/input/gestures/heldRepeater.ts b/web/src/engine/osk/src/input/gestures/heldRepeater.ts index 0f74677ddab..025d8989125 100644 --- a/web/src/engine/osk/src/input/gestures/heldRepeater.ts +++ b/web/src/engine/osk/src/input/gestures/heldRepeater.ts @@ -1,5 +1,5 @@ import { GestureSequence } from "@keymanapp/gesture-recognizer"; -import { KeyDistribution } from "@keymanapp/keyboard-processor"; +import { KeyDistribution } from "keyman/engine/keyboard"; import { KeyElement } from "../../keyElement.js"; import { GestureHandler } from './gestureHandler.js'; diff --git a/web/src/engine/osk/src/input/gestures/specsForLayout.ts b/web/src/engine/osk/src/input/gestures/specsForLayout.ts index 34d5576e236..5e53a443f50 100644 --- a/web/src/engine/osk/src/input/gestures/specsForLayout.ts +++ b/web/src/engine/osk/src/input/gestures/specsForLayout.ts @@ -12,7 +12,7 @@ import ButtonClasses = TouchLayout.TouchLayoutKeySp; import { ActiveLayout, deepCopy -} from '@keymanapp/keyboard-processor'; +} from 'keyman/engine/keyboard'; import { type KeyElement } from '../../keyElement.js'; @@ -20,20 +20,8 @@ import { calcLockedDistance, lockedAngleForDir, MAX_TOLERANCE_ANGLE_SKEW, type O import specs = gestures.specs; -export interface GestureParams { +export interface GestureParams { readonly longpress: { - /** - * Allows enabling or disabling the longpress up-flick shortcut for - * keyboards that do not include any defined flick gestures. - * - * Will be ignored (in favor of `false`) for keyboards that do have defined - * flicks. - * - * Note: this is automatically overwritten during keyboard initialization - * to match the keyboard's properties. - */ - permitsFlick: (item?: Item) => boolean, - /** * The minimum _net_ distance traveled before a longpress flick-shortcut will cancel any * conflicting flick models. @@ -98,19 +86,31 @@ export interface GestureParams { * Is currently also used as the max radius for valid flick-reset recentering targets. */ dirLockDist: number + } +} + +export interface FullGestureParams extends GestureParams { + readonly longpress: GestureParams["longpress"] & { + /** + * Allows enabling or disabling the longpress up-flick shortcut for + * keyboards that do not include any defined flick gestures. + * + * Will be ignored (in favor of `false`) for keyboards that do have defined + * flicks. + * + * Note: this is automatically overwritten during keyboard initialization + * to match the keyboard's properties. + */ + permitsFlick: (item?: Item) => boolean }, /** * Indicates whether roaming-touch oriented behaviors should be enabled. - * - * Note that run-time adjustments to this property after initialization will - * not take affect, unlike the other properties of the overall parameter object. */ roamingEnabled?: boolean; } export const DEFAULT_GESTURE_PARAMS: GestureParams = { longpress: { - permitsFlick: () => true, // Note: actual runtime value is determined at runtime based upon row height. // See `VisualKeyboard.refreshLayout`, CTRL-F "Step 3". flickDistStart: 8, @@ -206,17 +206,16 @@ let dummy: ActiveLayout; let dummy2: LayoutGestureSupportFlags = dummy; /** - * Defines the set of gestures appropriate for use with the specified Keyman keyboard. + * Defines the set of gestures appropriate for use with the specified Keyman + * keyboard. * @param layerGroup The active keyboard's layer group - * @param params A set of tweakable gesture parameters. It will be closure-captured - * and referred to by reference; changes to its values will take - * immediate effect during gesture processing. - * - * If params.roamingEnabled is unset, it will be initialized by this - * method based upon layout properties. + * @param paramObj A set of tweakable gesture parameters. It will be + * closure-captured and referred to by reference; changes to + * its values will take immediate effect during gesture + * processing. * @returns */ -export function gestureSetForLayout(flags: LayoutGestureSupportFlags, params: GestureParams): GestureModelDefs { +export function gestureSetForLayout(flags: LayoutGestureSupportFlags, paramObj: GestureParams): GestureModelDefs { // To be used among the `allowsInitialState` contact-model specifications as needed. const gestureKeyFilter = (key: KeyElement, gestureId: string) => { if(!key) { @@ -248,18 +247,33 @@ export function gestureSetForLayout(flags: LayoutGestureSupportFlags, params: Ge } }; - const doRoaming = params.roamingEnabled ||= !flags.hasFlicks; + const params = paramObj as FullGestureParams; + + // Override any prior entries for keyboard-specific configuration. + params.longpress.permitsFlick = (key) => { + const flickSpec = key?.key.spec.flick; + return !flickSpec || !(flickSpec.n || flickSpec.nw || flickSpec.ne); + }; + const doRoaming = params.roamingEnabled = !flags.hasFlicks; const _initialTapModel: GestureModel = deepCopy(!doRoaming ? initialTapModel(params) : initialTapModelWithReset(params)); const _simpleTapModel: GestureModel = deepCopy(!doRoaming ? simpleTapModel(params) : simpleTapModelWithReset(params)); // Ensure all deep-copy operations for longpress modeling occur before the property-redefining block. const _longpressModel: GestureModel = withKeySpecFiltering(deepCopy(longpressModel(params, true, doRoaming)), 0); + const _multitapStartModel: GestureModel = withKeySpecFiltering(multitapStartModel(params), 0); + const _modipressMultitapStartModel: GestureModel = withKeySpecFiltering(modipressMultitapStartModel(params), 0); // `deepCopy` does not preserve property definitions, instead raw-copying its value. // We need to re-instate the longpress delay property here. Object.defineProperty(_longpressModel.contacts[0].model.timer, 'duration', { get: () => params.longpress.waitLength }); + Object.defineProperty(_multitapStartModel.sustainTimer, 'duration', { + get: () => params.multitap.waitLength + }); + Object.defineProperty(_modipressMultitapStartModel.sustainTimer, 'duration', { + get: () => params.multitap.waitLength + }); // #region Functions for implementing and/or extending path initial-state checks function withKeySpecFiltering(model: GestureModel, contactIndices: number | number[]) { @@ -293,7 +307,7 @@ export function gestureSetForLayout(flags: LayoutGestureSupportFlags, params: Ge const _modipressStartModel = modipressStartModel(); const gestureModels: GestureModel[] = [ _longpressModel, - withKeySpecFiltering(multitapStartModel(params), 0), + _multitapStartModel, multitapEndModel(params), _initialTapModel, _simpleTapModel, @@ -304,7 +318,7 @@ export function gestureSetForLayout(flags: LayoutGestureSupportFlags, params: Ge modipressHoldModel(params), modipressEndModel(), modipressMultitapTransitionModel(), - withKeySpecFiltering(modipressMultitapStartModel(params), 0), + _modipressMultitapStartModel, modipressMultitapEndModel(params), modipressMultitapLockModel() ]; @@ -367,7 +381,7 @@ export function instantContactResolutionModel(): ContactModel { }; } -export function flickStartContactModel(params: GestureParams): gestures.specs.ContactModel { +export function flickStartContactModel(params: FullGestureParams): gestures.specs.ContactModel { const flickParams = params.flick; return { @@ -426,7 +440,7 @@ function determineLockFromStats(pathStats: CumulativePathStats, base } } -export function flickMidContactModel(params: GestureParams): gestures.specs.ContactModel { +export function flickMidContactModel(params: FullGestureParams): gestures.specs.ContactModel { return { itemPriority: 1, pathModel: { @@ -461,7 +475,7 @@ export function flickMidContactModel(params: GestureParams): gestures.specs.Cont } -export function flickEndContactModel(params: GestureParams): ContactModel { +export function flickEndContactModel(params: FullGestureParams): ContactModel { return { itemPriority: 1, pathModel: { @@ -485,7 +499,7 @@ export function flickEndContactModel(params: GestureParams): ContactModel { } } -export function longpressContactModel(params: GestureParams, enabledFlicks: boolean, resetForRoaming: boolean): ContactModel { +export function longpressContactModel(params: FullGestureParams, enabledFlicks: boolean, resetForRoaming: boolean): ContactModel { const spec = params.longpress; return { @@ -581,7 +595,7 @@ export function modipressContactEndModel(): ContactModel { }; } -export function simpleTapContactModel(params: GestureParams, isNotInitial?: boolean): ContactModel { +export function simpleTapContactModel(params: FullGestureParams, isNotInitial?: boolean): ContactModel { // Snapshot at model construction; do not update if changed. const roamingEnabled = params?.roamingEnabled ?? true; // ?? true - used by the banner. @@ -650,7 +664,7 @@ export function specialKeyStartModel(): GestureModel { }; } -export function specialKeyEndModel(params: GestureParams): GestureModel { +export function specialKeyEndModel(params: FullGestureParams): GestureModel { return { id: 'special-key-end', resolutionPriority: 0, @@ -682,7 +696,7 @@ export function specialKeyEndModel(params: GestureParams): GestureModel { * - the common gesture configuration permits the shortcut where supported * @param allowRoaming Indicates whether "roaming touch" mode should be supported. */ -export function longpressModel(params: GestureParams, allowShortcut: boolean, allowRoaming: boolean): GestureModel { +export function longpressModel(params: FullGestureParams, allowShortcut: boolean, allowRoaming: boolean): GestureModel { const base: GestureModel = { id: 'longpress', // Needs to beat flick-start priority. @@ -732,7 +746,7 @@ export function longpressModel(params: GestureParams, allowShortcut: boolean, al /** * For use for transitioning out of roaming-touch. */ -export function longpressModelAfterRoaming(params: GestureParams): GestureModel { +export function longpressModelAfterRoaming(params: FullGestureParams): GestureModel { // The longpress-shortcut is always disabled for keys reached by roaming (param 2) // Only used when roaming is permitted; continued roaming should be allowed. (param 3) const base = longpressModel(params, false, true); @@ -782,7 +796,7 @@ export function longpressRoamRestoration(): GestureModel { } } -export function flickStartModel(params: GestureParams): GestureModel { +export function flickStartModel(params: FullGestureParams): GestureModel { return { id: 'flick-start', resolutionPriority: 3, @@ -799,7 +813,7 @@ export function flickStartModel(params: GestureParams): GestureModel { } } -export function flickRestartModel(params: GestureParams): GestureModel { +export function flickRestartModel(params: FullGestureParams): GestureModel { const base = flickStartModel(params); return { ...base, @@ -859,7 +873,7 @@ export function flickRestartModel(params: GestureParams): GestureModel { +export function flickMidModel(params: FullGestureParams): GestureModel { return { id: 'flick-mid', resolutionPriority: 0, @@ -889,7 +903,7 @@ export function flickMidModel(params: GestureParams): GestureModel { } // Clears existing flick-scrolling & primes the flick-reset recentering mechanism. -export function flickResetModel(params: GestureParams): GestureModel { +export function flickResetModel(params: FullGestureParams): GestureModel { return { id: 'flick-reset', resolutionPriority: 1, @@ -909,7 +923,7 @@ export function flickResetModel(params: GestureParams): GestureModel { }; } -export function flickResetCenteringModel(params: GestureParams): GestureModel { +export function flickResetCenteringModel(params: FullGestureParams): GestureModel { return { id: 'flick-reset-centering', resolutionPriority: 1, @@ -963,7 +977,7 @@ export function flickResetEndModel(): GestureModel { }; }; -export function flickEndModel(params: GestureParams): GestureModel { +export function flickEndModel(params: FullGestureParams): GestureModel { return { id: 'flick-end', resolutionPriority: 0, @@ -990,7 +1004,7 @@ export function flickEndModel(params: GestureParams): GestureModel { } } -export function multitapStartModel(params: GestureParams): GestureModel { +export function multitapStartModel(params: FullGestureParams): GestureModel { return { id: 'multitap-start', resolutionPriority: 2, @@ -1019,7 +1033,7 @@ export function multitapStartModel(params: GestureParams): GestureModel { } } -export function multitapEndModel(params: GestureParams): GestureModel { +export function multitapEndModel(params: FullGestureParams): GestureModel { return { id: 'multitap-end', resolutionPriority: 2, @@ -1053,7 +1067,7 @@ export function multitapEndModel(params: GestureParams): GestureModel { } } -export function initialTapModel(params: GestureParams): GestureModel { +export function initialTapModel(params: FullGestureParams): GestureModel { return { id: 'initial-tap', resolutionPriority: 1, @@ -1089,7 +1103,7 @@ export function initialTapModel(params: GestureParams): GestureModel { } } -export function simpleTapModel(params: GestureParams): GestureModel { +export function simpleTapModel(params: FullGestureParams): GestureModel { return { id: 'simple-tap', resolutionPriority: 1, @@ -1113,7 +1127,7 @@ export function simpleTapModel(params: GestureParams): GestureModel { }; } -export function initialTapModelWithReset(params: GestureParams): GestureModel { +export function initialTapModelWithReset(params: FullGestureParams): GestureModel { const base = initialTapModel(params); return { ...base, @@ -1127,7 +1141,7 @@ export function initialTapModelWithReset(params: GestureParams): GestureModel { +export function simpleTapModelWithReset(params: FullGestureParams): GestureModel { const simpleModel = simpleTapModel(params); return { ...simpleModel, @@ -1189,7 +1203,7 @@ export function modipressStartModel(): GestureModel { } } -export function modipressHoldModel(params: GestureParams): GestureModel { +export function modipressHoldModel(params: FullGestureParams): GestureModel { return { id: 'modipress-hold', resolutionPriority: 5, @@ -1281,7 +1295,7 @@ export function modipressEndModel(): GestureModel { } } -export function modipressMultitapStartModel(params: GestureParams): GestureModel { +export function modipressMultitapStartModel(params: FullGestureParams): GestureModel { return { id: 'modipress-multitap-start', resolutionPriority: 6, @@ -1316,7 +1330,7 @@ export function modipressMultitapStartModel(params: GestureParams): GestureModel } } -export function modipressMultitapEndModel(params: GestureParams): GestureModel { +export function modipressMultitapEndModel(params: FullGestureParams): GestureModel { return { id: 'modipress-multitap-end', resolutionPriority: 5, diff --git a/web/src/engine/osk/src/keyElement.ts b/web/src/engine/osk/src/keyElement.ts index 01ae3a0c99d..c913bdfbff5 100644 --- a/web/src/engine/osk/src/keyElement.ts +++ b/web/src/engine/osk/src/keyElement.ts @@ -1,4 +1,4 @@ -import { ActiveSubKey } from '@keymanapp/keyboard-processor'; +import { ActiveSubKey } from 'keyman/engine/keyboard'; import OSKKey from "./keyboard-layout/oskKey.js"; export class KeyData { diff --git a/web/src/engine/osk/src/keyboard-layout/gesturePreviewHost.ts b/web/src/engine/osk/src/keyboard-layout/gesturePreviewHost.ts index 321de328b83..32a30c3f5d7 100644 --- a/web/src/engine/osk/src/keyboard-layout/gesturePreviewHost.ts +++ b/web/src/engine/osk/src/keyboard-layout/gesturePreviewHost.ts @@ -1,4 +1,4 @@ -import { ActiveKeyBase } from "@keymanapp/keyboard-processor"; +import { ActiveKeyBase } from "keyman/engine/keyboard"; import { EventEmitter } from "eventemitter3"; import { KeyElement } from "../keyElement.js"; diff --git a/web/src/engine/osk/src/keyboard-layout/oskBaseKey.ts b/web/src/engine/osk/src/keyboard-layout/oskBaseKey.ts index e48253df60d..3d70ed40100 100644 --- a/web/src/engine/osk/src/keyboard-layout/oskBaseKey.ts +++ b/web/src/engine/osk/src/keyboard-layout/oskBaseKey.ts @@ -1,4 +1,4 @@ -import { ActiveKey, Codes } from '@keymanapp/keyboard-processor'; +import { ActiveKey, Codes } from 'keyman/engine/keyboard'; import OSKKey, { KeyLayoutParams, renameSpecialKey } from './oskKey.js'; import { KeyData, KeyElement, link } from '../keyElement.js'; diff --git a/web/src/engine/osk/src/keyboard-layout/oskKey.ts b/web/src/engine/osk/src/keyboard-layout/oskKey.ts index 6404658ef67..2b9381afe30 100644 --- a/web/src/engine/osk/src/keyboard-layout/oskKey.ts +++ b/web/src/engine/osk/src/keyboard-layout/oskKey.ts @@ -1,4 +1,4 @@ -import { ActiveKey, ActiveSubKey, ButtonClass, ButtonClasses, DeviceSpec } from '@keymanapp/keyboard-processor'; +import { ActiveKey, ActiveSubKey, ButtonClass, ButtonClasses, DeviceSpec } from 'keyman/engine/keyboard'; // At present, we don't use @keymanapp/keyman. Just `keyman`. (Refer to /web/package.json.) import specialChars from '../specialCharacters.js'; diff --git a/web/src/engine/osk/src/keyboard-layout/oskLayer.ts b/web/src/engine/osk/src/keyboard-layout/oskLayer.ts index 61c43f22cf8..a08d2921a55 100644 --- a/web/src/engine/osk/src/keyboard-layout/oskLayer.ts +++ b/web/src/engine/osk/src/keyboard-layout/oskLayer.ts @@ -1,4 +1,4 @@ -import { ActiveLayer, ActiveLayout } from '@keymanapp/keyboard-processor'; +import { ActiveLayer, ActiveLayout } from 'keyman/engine/keyboard'; import OSKRow from './oskRow.js'; import OSKBaseKey from './oskBaseKey.js'; diff --git a/web/src/engine/osk/src/keyboard-layout/oskLayerGroup.ts b/web/src/engine/osk/src/keyboard-layout/oskLayerGroup.ts index 5f4a3f45a3b..4bf4bc01b49 100644 --- a/web/src/engine/osk/src/keyboard-layout/oskLayerGroup.ts +++ b/web/src/engine/osk/src/keyboard-layout/oskLayerGroup.ts @@ -1,4 +1,4 @@ -import { type DeviceSpec, Keyboard, ActiveLayout, ButtonClasses } from '@keymanapp/keyboard-processor'; +import { type DeviceSpec, Keyboard, ActiveLayout, ButtonClasses } from 'keyman/engine/keyboard'; import { InputSample } from '@keymanapp/gesture-recognizer'; diff --git a/web/src/engine/osk/src/keyboard-layout/oskRow.ts b/web/src/engine/osk/src/keyboard-layout/oskRow.ts index 8012e8438a1..91c6409cd77 100644 --- a/web/src/engine/osk/src/keyboard-layout/oskRow.ts +++ b/web/src/engine/osk/src/keyboard-layout/oskRow.ts @@ -1,4 +1,4 @@ -import { ActiveKey, ActiveLayer, ActiveRow } from '@keymanapp/keyboard-processor'; +import { ActiveKey, ActiveLayer, ActiveRow } from 'keyman/engine/keyboard'; import OSKBaseKey from './oskBaseKey.js'; import { ParsedLengthStyle } from '../lengthStyle.js'; diff --git a/web/src/engine/osk/src/views/anchoredOskView.ts b/web/src/engine/osk/src/views/anchoredOskView.ts index fe2670c0b90..25ab819e3cd 100644 --- a/web/src/engine/osk/src/views/anchoredOskView.ts +++ b/web/src/engine/osk/src/views/anchoredOskView.ts @@ -1,4 +1,4 @@ -import { DeviceSpec } from '@keymanapp/keyboard-processor'; +import { DeviceSpec } from 'keyman/engine/keyboard'; import { landscapeView } from 'keyman/engine/dom-utils'; import OSKView, { OSKPos, OSKRect } from './oskView.js'; diff --git a/web/src/engine/osk/src/views/floatingOskView.ts b/web/src/engine/osk/src/views/floatingOskView.ts index c296bd374bf..ba186fc99e2 100644 --- a/web/src/engine/osk/src/views/floatingOskView.ts +++ b/web/src/engine/osk/src/views/floatingOskView.ts @@ -1,4 +1,4 @@ -import { DeviceSpec, ManagedPromise, Version } from '@keymanapp/keyboard-processor'; +import { DeviceSpec, ManagedPromise, Version } from 'keyman/engine/keyboard'; import { getAbsoluteX, getAbsoluteY, landscapeView } from 'keyman/engine/dom-utils'; import { EmitterListenerSpy } from 'keyman/engine/events'; diff --git a/web/src/engine/osk/src/views/inlinedOskView.ts b/web/src/engine/osk/src/views/inlinedOskView.ts index 06f89c0a921..d4dc48fd664 100644 --- a/web/src/engine/osk/src/views/inlinedOskView.ts +++ b/web/src/engine/osk/src/views/inlinedOskView.ts @@ -1,4 +1,4 @@ -import { DeviceSpec } from '@keymanapp/keyboard-processor'; +import { DeviceSpec } from 'keyman/engine/keyboard'; import OSKView, { OSKPos, OSKRect } from './oskView.js'; import VisualKeyboard from '../visualKeyboard.js'; diff --git a/web/src/engine/events/src/keyEventSource.interface.ts b/web/src/engine/osk/src/views/keyEventSource.interface.ts similarity index 79% rename from web/src/engine/events/src/keyEventSource.interface.ts rename to web/src/engine/osk/src/views/keyEventSource.interface.ts index d3c39fa49d7..827a0b9af8f 100644 --- a/web/src/engine/events/src/keyEventSource.interface.ts +++ b/web/src/engine/osk/src/views/keyEventSource.interface.ts @@ -1,5 +1,6 @@ import { EventEmitter } from "eventemitter3"; -import { type KeyEvent, type RuleBehavior } from "@keymanapp/keyboard-processor"; +import { type KeyEvent } from 'keyman/engine/keyboard'; +import { type RuleBehavior } from 'keyman/engine/js-processor'; export type KeyEventResultCallback = (result: RuleBehavior, error?: Error) => void; export type KeyEventHandler = (event: KeyEvent, callback?: KeyEventResultCallback) => void; diff --git a/web/src/engine/osk/src/views/oskView.ts b/web/src/engine/osk/src/views/oskView.ts index d55eb8a8f8d..b51c18fbcd9 100644 --- a/web/src/engine/osk/src/views/oskView.ts +++ b/web/src/engine/osk/src/views/oskView.ts @@ -16,16 +16,16 @@ import { Keyboard, KeyboardProperties, ManagedPromise, - type MinimalCodesInterface, - type MutableSystemStore, - type SystemStoreMutationHandler -} from '@keymanapp/keyboard-processor'; + type MinimalCodesInterface +} from 'keyman/engine/keyboard'; import { createUnselectableElement, getAbsoluteX, getAbsoluteY, StylesheetManager } from 'keyman/engine/dom-utils'; -import { EventListener, KeyEventHandler, KeyEventSourceInterface, LegacyEventEmitter } from 'keyman/engine/events'; +import { EventListener, LegacyEventEmitter } from 'keyman/engine/events'; +import { type MutableSystemStore, type SystemStoreMutationHandler } from 'keyman/engine/js-processor'; import Configuration from '../config/viewConfiguration.js'; import Activator, { StaticActivator } from './activator.js'; import TouchEventPromiseMap from './touchEventPromiseMap.js'; +import { KeyEventHandler, KeyEventSourceInterface } from './keyEventSource.interface.js'; import { DEFAULT_GESTURE_PARAMS, GestureParams } from '../input/gestures/specsForLayout.js'; // These will likely be eliminated from THIS file at some point.\ diff --git a/web/src/engine/osk/src/visualKeyboard.ts b/web/src/engine/osk/src/visualKeyboard.ts index a8152a0db0a..570a82a209d 100644 --- a/web/src/engine/osk/src/visualKeyboard.ts +++ b/web/src/engine/osk/src/visualKeyboard.ts @@ -13,9 +13,9 @@ import { StateKeyMap, ActiveSubKey, timedPromise, - ActiveKeyBase, - isEmptyTransform -} from '@keymanapp/keyboard-processor'; + ActiveKeyBase +} from 'keyman/engine/keyboard'; +import { isEmptyTransform } from 'keyman/engine/js-processor'; import { buildCorrectiveLayout } from './correctionLayout.js'; import { distributionFromDistanceMaps, keyTouchDistances } from './corrections.js'; @@ -31,7 +31,7 @@ import { import { createStyleSheet, StylesheetManager } from 'keyman/engine/dom-utils'; -import { KeyEventHandler, KeyEventResultCallback } from 'keyman/engine/events'; +import { KeyEventHandler, KeyEventResultCallback } from './views/keyEventSource.interface.js'; import GlobeHint from './globehint.interface.js'; import KeyboardView from './components/keyboardView.interface.js'; @@ -151,7 +151,7 @@ export default class VisualKeyboard extends EventEmitter implements Ke /** * Tweakable gesture parameters referenced by supported gestures and the gesture engine. */ - get gestureParams(): GestureParams { + get gestureParams(): GestureParams { return this.config.gestureParams; }; @@ -423,11 +423,6 @@ export default class VisualKeyboard extends EventEmitter implements Ke historyLength: DEBUG_HISTORY_COUNT }; - this.gestureParams.longpress.permitsFlick = (key) => { - const flickSpec = key?.key.spec.flick; - return !flickSpec || !(flickSpec.n || flickSpec.nw || flickSpec.ne); - }; - const recognizer = new GestureRecognizer(gestureSetForLayout(this.kbdLayout, this.gestureParams), config); recognizer.stateToken = this.layerId; @@ -934,13 +929,6 @@ export default class VisualKeyboard extends EventEmitter implements Ke * @returns */ getSimpleTapCorrectionDistances(input: InputSample, keySpec?: ActiveKey): Map { - // TODO: It'd be nice to optimize by keeping these off when unused, but the wiring - // necessary would get in the way of modularization at the moment. - // let keyman = com.keyman.singleton; - // if (!keyman.core.languageProcessor.mayCorrect) { - // return null; - // } - // Note: if subkeys are active, they will still be displayed at this time. let touchKbdPos = this.getTouchCoordinatesOnKeyboard(input); let layerGroup = this.layerGroup.element; // Always has proper dimensions, unlike kbdDiv itself. @@ -1251,6 +1239,7 @@ export default class VisualKeyboard extends EventEmitter implements Ke const groupStyle = getComputedStyle(this.layerGroup.element); const isInDOM = computedStyle.height != '' && computedStyle.height != 'auto'; + const isGroupInDOM = groupStyle.height != '' && groupStyle.height != 'auto'; if (computedStyle.border) { this._borderWidth = new ParsedLengthStyle(computedStyle.borderWidth).val; @@ -1263,10 +1252,11 @@ export default class VisualKeyboard extends EventEmitter implements Ke this._computedHeight = this.height; } else if (isInDOM) { this._computedWidth = parseInt(computedStyle.width, 10); - if (!this._computedWidth) { - this._computedWidth = parseInt(groupStyle.width, 10); - } this._computedHeight = parseInt(computedStyle.height, 10); + } else if (isGroupInDOM) { + // May occur for documentation-keyboards, which are detached from their VisualKeyboard base. + this._computedWidth = parseInt(groupStyle.width, 10); + this._computedHeight = parseInt(groupStyle.height, 10); } else { // Cannot perform layout operations! return; @@ -1609,6 +1599,16 @@ export default class VisualKeyboard extends EventEmitter implements Ke // the page. kbdObj.appendStyleSheet(); + // Unset the width + height we used thus far; this method's consumer may choose to rescale + // the returned element. If so, we don't want to use our outdated value by mistake. + // + // While `kbdObj.setSize()` could be used in theory, it _also_ unsets the element styling. + // We actually wish to _leave_ this styling in place - one of our parameters is `height`, and + // it should remain in place in the styling on the output element as the default in case + // the consumer _doesn't_ add styling afterward. + delete kbdObj._width; + delete kbdObj._height; + return classWrapper; } diff --git a/common/web/lm-message-types/message.d.ts b/web/src/engine/predictive-text/types/message.d.ts similarity index 100% rename from common/web/lm-message-types/message.d.ts rename to web/src/engine/predictive-text/types/message.d.ts diff --git a/common/web/lm-message-types/package.json b/web/src/engine/predictive-text/types/package.json similarity index 100% rename from common/web/lm-message-types/package.json rename to web/src/engine/predictive-text/types/package.json diff --git a/web/src/engine/predictive-text/types/tsconfig.json b/web/src/engine/predictive-text/types/tsconfig.json new file mode 100644 index 00000000000..8b87ca26672 --- /dev/null +++ b/web/src/engine/predictive-text/types/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../../../../tsconfig.base.json", + "compilerOptions": { + "baseUrl": "./", + "outDir": "build/", + "tsBuildInfoFile": "build/tsconfig.tsbuildinfo", + "rootDir": "./src" + }, + "include": ["./*.ts"], + "exclude": ["test.ts"] +} diff --git a/common/predictive-text/.gitignore b/web/src/engine/predictive-text/worker-main/.gitignore similarity index 100% rename from common/predictive-text/.gitignore rename to web/src/engine/predictive-text/worker-main/.gitignore diff --git a/common/predictive-text/README.md b/web/src/engine/predictive-text/worker-main/README.md similarity index 100% rename from common/predictive-text/README.md rename to web/src/engine/predictive-text/worker-main/README.md diff --git a/common/predictive-text/build.sh b/web/src/engine/predictive-text/worker-main/build.sh similarity index 82% rename from common/predictive-text/build.sh rename to web/src/engine/predictive-text/worker-main/build.sh index b9ab6016914..33e5eaebeb6 100755 --- a/common/predictive-text/build.sh +++ b/web/src/engine/predictive-text/worker-main/build.sh @@ -8,7 +8,7 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../resources/build/builder.inc.sh" +. "${THIS_SCRIPT%/*}/../../../../../resources/build/builder.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE . "$KEYMAN_ROOT/resources/shellHelperFunctions.sh" @@ -22,7 +22,7 @@ BUNDLE_CMD="node $KEYMAN_ROOT/common/web/es-bundling/build/common-bundle.mjs" builder_describe "Builds the lm-layer module" \ "@/common/web/keyman-version" \ "@/common/web/es-bundling" \ - "@/common/web/lm-worker" \ + "@/web/src/engine/predictive-text/worker-thread" \ "clean" \ "configure" \ "build" \ @@ -31,7 +31,7 @@ builder_describe "Builds the lm-layer module" \ builder_describe_outputs \ configure /node_modules \ - build /common/predictive-text/build/lib/web/index.mjs # is built by the final step. + build /web/src/engine/predictive-text/worker-main/build/lib/web/index.mjs # is built by the final step. builder_parse "$@" @@ -48,8 +48,8 @@ function do_build() { tsc -b ./tsconfig.all.json # esbuild-bundled products at this level are not intended to be used for anything but testing. - $BUNDLE_CMD "${KEYMAN_ROOT}/common/predictive-text/build/obj/web/index.js" \ - --out "${KEYMAN_ROOT}/common/predictive-text/build/lib/web/index.mjs" \ + $BUNDLE_CMD "${KEYMAN_ROOT}/web/src/engine/predictive-text/worker-main/build/obj/web/index.js" \ + --out "${KEYMAN_ROOT}/web/src/engine/predictive-text/worker-main/build/lib/web/index.mjs" \ --format esm } diff --git a/common/predictive-text/docs/build.sh b/web/src/engine/predictive-text/worker-main/docs/build.sh similarity index 100% rename from common/predictive-text/docs/build.sh rename to web/src/engine/predictive-text/worker-main/docs/build.sh diff --git a/common/predictive-text/docs/lmlayer-states.dot b/web/src/engine/predictive-text/worker-main/docs/lmlayer-states.dot similarity index 100% rename from common/predictive-text/docs/lmlayer-states.dot rename to web/src/engine/predictive-text/worker-main/docs/lmlayer-states.dot diff --git a/common/predictive-text/docs/lmlayer-states.png b/web/src/engine/predictive-text/worker-main/docs/lmlayer-states.png similarity index 100% rename from common/predictive-text/docs/lmlayer-states.png rename to web/src/engine/predictive-text/worker-main/docs/lmlayer-states.png diff --git a/common/predictive-text/docs/predictive-text-sequence.png b/web/src/engine/predictive-text/worker-main/docs/predictive-text-sequence.png similarity index 100% rename from common/predictive-text/docs/predictive-text-sequence.png rename to web/src/engine/predictive-text/worker-main/docs/predictive-text-sequence.png diff --git a/common/predictive-text/docs/worker-communication-protocol.md b/web/src/engine/predictive-text/worker-main/docs/worker-communication-protocol.md similarity index 100% rename from common/predictive-text/docs/worker-communication-protocol.md rename to web/src/engine/predictive-text/worker-main/docs/worker-communication-protocol.md diff --git a/common/predictive-text/package.json b/web/src/engine/predictive-text/worker-main/package.json similarity index 100% rename from common/predictive-text/package.json rename to web/src/engine/predictive-text/worker-main/package.json diff --git a/common/predictive-text/src/index.ts b/web/src/engine/predictive-text/worker-main/src/index.ts similarity index 100% rename from common/predictive-text/src/index.ts rename to web/src/engine/predictive-text/worker-main/src/index.ts diff --git a/common/predictive-text/src/lmlayer.ts b/web/src/engine/predictive-text/worker-main/src/lmlayer.ts similarity index 100% rename from common/predictive-text/src/lmlayer.ts rename to web/src/engine/predictive-text/worker-main/src/lmlayer.ts diff --git a/common/predictive-text/src/node/index.ts b/web/src/engine/predictive-text/worker-main/src/node/index.ts similarity index 100% rename from common/predictive-text/src/node/index.ts rename to web/src/engine/predictive-text/worker-main/src/node/index.ts diff --git a/common/predictive-text/src/node/mappedWorker.ts b/web/src/engine/predictive-text/worker-main/src/node/mappedWorker.ts similarity index 100% rename from common/predictive-text/src/node/mappedWorker.ts rename to web/src/engine/predictive-text/worker-main/src/node/mappedWorker.ts diff --git a/common/predictive-text/src/node/sourcemappedWorker.ts b/web/src/engine/predictive-text/worker-main/src/node/sourcemappedWorker.ts similarity index 100% rename from common/predictive-text/src/node/sourcemappedWorker.ts rename to web/src/engine/predictive-text/worker-main/src/node/sourcemappedWorker.ts diff --git a/common/predictive-text/src/node/tsconfig.json b/web/src/engine/predictive-text/worker-main/src/node/tsconfig.json similarity index 89% rename from common/predictive-text/src/node/tsconfig.json rename to web/src/engine/predictive-text/worker-main/src/node/tsconfig.json index f95364c4175..a1ab0f2f7cd 100644 --- a/common/predictive-text/src/node/tsconfig.json +++ b/web/src/engine/predictive-text/worker-main/src/node/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "../../../web/tsconfig.kmw-main-base.json", + "extends": "../../../../../../tsconfig.base.json", "compilerOptions": { // The Worker class needs access to the `new` keyword, else Node will throw an // error when constructing the Worker for unit tests. ES6 is sufficient. diff --git a/common/predictive-text/src/node/worker.ts b/web/src/engine/predictive-text/worker-main/src/node/worker.ts similarity index 100% rename from common/predictive-text/src/node/worker.ts rename to web/src/engine/predictive-text/worker-main/src/node/worker.ts diff --git a/common/predictive-text/src/promise-store.ts b/web/src/engine/predictive-text/worker-main/src/promise-store.ts similarity index 100% rename from common/predictive-text/src/promise-store.ts rename to web/src/engine/predictive-text/worker-main/src/promise-store.ts diff --git a/common/predictive-text/src/tsconfig.json b/web/src/engine/predictive-text/worker-main/src/tsconfig.json similarity index 54% rename from common/predictive-text/src/tsconfig.json rename to web/src/engine/predictive-text/worker-main/src/tsconfig.json index 675bf7cd537..edf6195b3ab 100644 --- a/common/predictive-text/src/tsconfig.json +++ b/web/src/engine/predictive-text/worker-main/src/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "../../web/tsconfig.kmw-main-base.json", + "extends": "../../../../../tsconfig.base.json", "compilerOptions": { "baseUrl": "./", @@ -8,11 +8,9 @@ "rootDir": "./" }, "references": [ - { "path": "../../web/utils" }, - { "path": "../../models/types"}, - { "path": "../../models/wordbreakers"}, - { "path": "../../web/lm-message-types"}, - { "path": "../../models/templates"} + { "path": "../../../../../../common/web/utils" }, + { "path": "../../../../../../common/models/types"}, + { "path": "../../types"} ], "include" : [ "./*.ts" ], "exclude" : [ diff --git a/common/predictive-text/src/unwrap.ts b/web/src/engine/predictive-text/worker-main/src/unwrap.ts similarity index 100% rename from common/predictive-text/src/unwrap.ts rename to web/src/engine/predictive-text/worker-main/src/unwrap.ts diff --git a/common/predictive-text/src/web/index.ts b/web/src/engine/predictive-text/worker-main/src/web/index.ts similarity index 100% rename from common/predictive-text/src/web/index.ts rename to web/src/engine/predictive-text/worker-main/src/web/index.ts diff --git a/common/predictive-text/src/web/sourcemappedWorker.ts b/web/src/engine/predictive-text/worker-main/src/web/sourcemappedWorker.ts similarity index 100% rename from common/predictive-text/src/web/sourcemappedWorker.ts rename to web/src/engine/predictive-text/worker-main/src/web/sourcemappedWorker.ts diff --git a/common/predictive-text/src/web/tsconfig.json b/web/src/engine/predictive-text/worker-main/src/web/tsconfig.json similarity index 83% rename from common/predictive-text/src/web/tsconfig.json rename to web/src/engine/predictive-text/worker-main/src/web/tsconfig.json index c16936db12d..769c97d589d 100644 --- a/common/predictive-text/src/web/tsconfig.json +++ b/web/src/engine/predictive-text/worker-main/src/web/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "../../../web/tsconfig.kmw-main-base.json", + "extends": "../../../../../../tsconfig.base.json", "compilerOptions": { "lib": ["es6", "DOM"], "baseUrl": "../", diff --git a/common/predictive-text/src/web/worker.ts b/web/src/engine/predictive-text/worker-main/src/web/worker.ts similarity index 100% rename from common/predictive-text/src/web/worker.ts rename to web/src/engine/predictive-text/worker-main/src/web/worker.ts diff --git a/common/predictive-text/src/worker-interface.d.ts b/web/src/engine/predictive-text/worker-main/src/worker-interface.d.ts similarity index 100% rename from common/predictive-text/src/worker-interface.d.ts rename to web/src/engine/predictive-text/worker-main/src/worker-interface.d.ts diff --git a/common/predictive-text/tsconfig.all.json b/web/src/engine/predictive-text/worker-main/tsconfig.all.json similarity index 70% rename from common/predictive-text/tsconfig.all.json rename to web/src/engine/predictive-text/worker-main/tsconfig.all.json index f47b208edaf..722ce0114bd 100644 --- a/common/predictive-text/tsconfig.all.json +++ b/web/src/engine/predictive-text/worker-main/tsconfig.all.json @@ -3,7 +3,7 @@ // require separate sub-tsconfigs, both of which require common references. The multi-config // setup in this project and its subfolders ensure all modules can compile cleanly and in the // necessary compilation order. - "extends": "../web/tsconfig.kmw-main-base.json", + "extends": "../../../../tsconfig.base.json", "compilerOptions": { "baseUrl": "./", @@ -13,11 +13,9 @@ }, "files": [], "references": [ - { "path": "../web/utils" }, - { "path": "../models/types"}, - { "path": "../models/wordbreakers"}, - { "path": "../web/lm-message-types"}, - { "path": "../models/templates"}, + { "path": "../../../../../common/web/utils" }, + { "path": "../../../../../common/models/types"}, + { "path": "../types"}, { "path": "src/node" }, { "path": "src/web" } ], diff --git a/common/predictive-text/unit_tests/headless/promise-store.js b/web/src/engine/predictive-text/worker-main/unit_tests/headless/promise-store.js similarity index 100% rename from common/predictive-text/unit_tests/headless/promise-store.js rename to web/src/engine/predictive-text/worker-main/unit_tests/headless/promise-store.js diff --git a/common/predictive-text/unit_tests/headless/top-level-lmlayer.js b/web/src/engine/predictive-text/worker-main/unit_tests/headless/top-level-lmlayer.js similarity index 100% rename from common/predictive-text/unit_tests/headless/top-level-lmlayer.js rename to web/src/engine/predictive-text/worker-main/unit_tests/headless/top-level-lmlayer.js diff --git a/common/predictive-text/unit_tests/headless/worker-dummy-integration.js b/web/src/engine/predictive-text/worker-main/unit_tests/headless/worker-dummy-integration.js similarity index 100% rename from common/predictive-text/unit_tests/headless/worker-dummy-integration.js rename to web/src/engine/predictive-text/worker-main/unit_tests/headless/worker-dummy-integration.js diff --git a/common/predictive-text/unit_tests/headless/worker-trie-integration.js b/web/src/engine/predictive-text/worker-main/unit_tests/headless/worker-trie-integration.js similarity index 100% rename from common/predictive-text/unit_tests/headless/worker-trie-integration.js rename to web/src/engine/predictive-text/worker-main/unit_tests/headless/worker-trie-integration.js diff --git a/common/predictive-text/unit_tests/in_browser/cases/top-level-lmlayer.spec.ts b/web/src/engine/predictive-text/worker-main/unit_tests/in_browser/cases/top-level-lmlayer.spec.ts similarity index 100% rename from common/predictive-text/unit_tests/in_browser/cases/top-level-lmlayer.spec.ts rename to web/src/engine/predictive-text/worker-main/unit_tests/in_browser/cases/top-level-lmlayer.spec.ts diff --git a/common/predictive-text/unit_tests/in_browser/cases/worker-dummy-integration.spec.ts b/web/src/engine/predictive-text/worker-main/unit_tests/in_browser/cases/worker-dummy-integration.spec.ts similarity index 100% rename from common/predictive-text/unit_tests/in_browser/cases/worker-dummy-integration.spec.ts rename to web/src/engine/predictive-text/worker-main/unit_tests/in_browser/cases/worker-dummy-integration.spec.ts diff --git a/common/predictive-text/unit_tests/in_browser/cases/worker-trie-integration.spec.ts b/web/src/engine/predictive-text/worker-main/unit_tests/in_browser/cases/worker-trie-integration.spec.ts similarity index 100% rename from common/predictive-text/unit_tests/in_browser/cases/worker-trie-integration.spec.ts rename to web/src/engine/predictive-text/worker-main/unit_tests/in_browser/cases/worker-trie-integration.spec.ts diff --git a/common/predictive-text/unit_tests/in_browser/helpers.mjs b/web/src/engine/predictive-text/worker-main/unit_tests/in_browser/helpers.mjs similarity index 100% rename from common/predictive-text/unit_tests/in_browser/helpers.mjs rename to web/src/engine/predictive-text/worker-main/unit_tests/in_browser/helpers.mjs diff --git a/common/predictive-text/unit_tests/in_browser/web-test-runner.CI.config.mjs b/web/src/engine/predictive-text/worker-main/unit_tests/in_browser/web-test-runner.CI.config.mjs similarity index 100% rename from common/predictive-text/unit_tests/in_browser/web-test-runner.CI.config.mjs rename to web/src/engine/predictive-text/worker-main/unit_tests/in_browser/web-test-runner.CI.config.mjs diff --git a/common/predictive-text/unit_tests/in_browser/web-test-runner.config.mjs b/web/src/engine/predictive-text/worker-main/unit_tests/in_browser/web-test-runner.config.mjs similarity index 96% rename from common/predictive-text/unit_tests/in_browser/web-test-runner.config.mjs rename to web/src/engine/predictive-text/worker-main/unit_tests/in_browser/web-test-runner.config.mjs index 523cd254577..6f9df64ddbf 100644 --- a/common/predictive-text/unit_tests/in_browser/web-test-runner.config.mjs +++ b/web/src/engine/predictive-text/worker-main/unit_tests/in_browser/web-test-runner.config.mjs @@ -7,7 +7,7 @@ import { dirname, resolve } from 'path'; import { sessionStabilityReporter } from '@keymanapp/common-test-resources/test-runner-stability-reporter.mjs'; const dir = dirname(fileURLToPath(import.meta.url)); -const KEYMAN_ROOT = resolve(dir, '../../../../'); +const KEYMAN_ROOT = resolve(dir, '../../../../../../../'); /** @type {import('@web/test-runner').TestRunnerConfig} */ export default { diff --git a/common/predictive-text/unit_tests/test.sh b/web/src/engine/predictive-text/worker-main/unit_tests/test.sh similarity index 93% rename from common/predictive-text/unit_tests/test.sh rename to web/src/engine/predictive-text/worker-main/unit_tests/test.sh index 3eb87bae569..dda8d578640 100755 --- a/common/predictive-text/unit_tests/test.sh +++ b/web/src/engine/predictive-text/worker-main/unit_tests/test.sh @@ -3,7 +3,7 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../../resources/build/build-utils.sh" +. "${THIS_SCRIPT%/*}/../../../../../../resources/build/build-utils.sh" ## END STANDARD BUILD SCRIPT INCLUDE . "$KEYMAN_ROOT/resources/build/build-utils-ci.inc.sh" @@ -57,9 +57,9 @@ if builder_start_action test:libraries; then "$KEYMAN_ROOT/common/models/templates/build.sh" test $TEST_OPTS popd - pushd "$KEYMAN_ROOT/common/web/lm-worker" + pushd "$KEYMAN_ROOT/web/src/engine/predictive-text/worker-thread" echo - echo "### Running ${BUILDER_TERM_START}common/web/lm-worker${BUILDER_TERM_END} tests" + echo "### Running ${BUILDER_TERM_START}web/src/engine/predictive-text/worker-thread${BUILDER_TERM_END} tests" ./build.sh test $TEST_OPTS popd diff --git a/common/web/lm-worker/.c8rc.json b/web/src/engine/predictive-text/worker-thread/.c8rc.json similarity index 100% rename from common/web/lm-worker/.c8rc.json rename to web/src/engine/predictive-text/worker-thread/.c8rc.json diff --git a/common/web/lm-worker/build-polyfiller.js b/web/src/engine/predictive-text/worker-thread/build-polyfiller.js similarity index 98% rename from common/web/lm-worker/build-polyfiller.js rename to web/src/engine/predictive-text/worker-thread/build-polyfiller.js index 41cec75ce7d..eab268e9867 100644 --- a/common/web/lm-worker/build-polyfiller.js +++ b/web/src/engine/predictive-text/worker-thread/build-polyfiller.js @@ -166,7 +166,7 @@ let fullWorkerConcatenation = concatScriptsAndSourcemaps(sourceFileSet, destFile // New stage: cleaning the sourcemaps // Sources are being passed into the sourcemap concatenator via our working directory. -let sourceRoot = '@keymanapp/keyman/common/web/lm-worker/'; +let sourceRoot = '@keymanapp/keyman/web/src/engine/predictive-text/worker-thread/'; fullWorkerConcatenation.sourcemapJSON.sourceRoot = sourceRoot; // End "cleaning the sourcemaps" diff --git a/common/web/lm-worker/build-wrapper.js b/web/src/engine/predictive-text/worker-thread/build-wrapper.js similarity index 100% rename from common/web/lm-worker/build-wrapper.js rename to web/src/engine/predictive-text/worker-thread/build-wrapper.js diff --git a/common/web/lm-worker/build.sh b/web/src/engine/predictive-text/worker-thread/build.sh similarity index 87% rename from common/web/lm-worker/build.sh rename to web/src/engine/predictive-text/worker-thread/build.sh index 22ffa9208c5..8c6a4ca0012 100755 --- a/common/web/lm-worker/build.sh +++ b/web/src/engine/predictive-text/worker-thread/build.sh @@ -6,7 +6,7 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../../resources/build/builder.inc.sh" +. "${THIS_SCRIPT%/*}/../../../../../resources/build/builder.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE . "$KEYMAN_ROOT/resources/shellHelperFunctions.sh" @@ -17,7 +17,7 @@ WORKER_OUTPUT_FILENAME=build/lib/worker-main.js INTERMEDIATE=./build/intermediate LIB=./build/lib -bundle_cmd="node ../es-bundling/build/common-bundle.mjs" +bundle_cmd="node ${KEYMAN_ROOT}/common/web/es-bundling/build/common-bundle.mjs" SRCMAP_CLEANER="node $KEYMAN_ROOT/web/build/tools/building/sourcemap-root/index.js" @@ -34,7 +34,7 @@ builder_describe \ builder_describe_outputs \ configure /node_modules \ - build /common/web/lm-worker/$LIB/worker-main.wrapped.min.js + build "/web/src/engine/predictive-text/worker-thread/${LIB}/worker-main.wrapped.min.js" builder_parse "$@" @@ -65,7 +65,7 @@ function do_build() { $bundle_cmd src/main/worker-main.ts \ --out $INTERMEDIATE/worker-main.js \ --target "es6" \ - --sourceRoot '@keymanapp/keyman/common/web/lm-worker/src/main' + --sourceRoot '@keymanapp/keyman/web/src/engine/predictive-text/worker-thread/src/main' $SRCMAP_CLEANER \ $INTERMEDIATE/worker-main.js.map \ @@ -77,7 +77,7 @@ function do_build() { --minify \ --profile build/filesize-profile.log \ --target "es6" \ - --sourceRoot '@keymanapp/keyman/common/web/lm-worker/src/main' + --sourceRoot '@keymanapp/keyman/web/src/engine/predictive-text/worker-thread/src/main' $SRCMAP_CLEANER \ $INTERMEDIATE/worker-main.min.js.map \ @@ -98,7 +98,7 @@ function do_test() { if builder_has_option --ci; then MOCHA_FLAGS="$MOCHA_FLAGS --reporter mocha-teamcity-reporter" - WTR_CONFIG=.ci + WTR_CONFIG=.CI fi if builder_has_option --debug; then diff --git a/common/web/lm-worker/package.json b/web/src/engine/predictive-text/worker-thread/package.json similarity index 100% rename from common/web/lm-worker/package.json rename to web/src/engine/predictive-text/worker-thread/package.json diff --git a/common/web/lm-worker/src/main/correction/classical-calculation.ts b/web/src/engine/predictive-text/worker-thread/src/main/correction/classical-calculation.ts similarity index 100% rename from common/web/lm-worker/src/main/correction/classical-calculation.ts rename to web/src/engine/predictive-text/worker-thread/src/main/correction/classical-calculation.ts diff --git a/common/web/lm-worker/src/main/correction/context-tracker.ts b/web/src/engine/predictive-text/worker-thread/src/main/correction/context-tracker.ts similarity index 100% rename from common/web/lm-worker/src/main/correction/context-tracker.ts rename to web/src/engine/predictive-text/worker-thread/src/main/correction/context-tracker.ts diff --git a/common/web/lm-worker/src/main/correction/distance-modeler.ts b/web/src/engine/predictive-text/worker-thread/src/main/correction/distance-modeler.ts similarity index 100% rename from common/web/lm-worker/src/main/correction/distance-modeler.ts rename to web/src/engine/predictive-text/worker-thread/src/main/correction/distance-modeler.ts diff --git a/common/web/lm-worker/src/main/correction/execution-timer.ts b/web/src/engine/predictive-text/worker-thread/src/main/correction/execution-timer.ts similarity index 100% rename from common/web/lm-worker/src/main/correction/execution-timer.ts rename to web/src/engine/predictive-text/worker-thread/src/main/correction/execution-timer.ts diff --git a/common/web/lm-worker/src/main/correction/index.ts b/web/src/engine/predictive-text/worker-thread/src/main/correction/index.ts similarity index 100% rename from common/web/lm-worker/src/main/correction/index.ts rename to web/src/engine/predictive-text/worker-thread/src/main/correction/index.ts diff --git a/common/web/lm-worker/src/main/correction/transform-tokenization.ts b/web/src/engine/predictive-text/worker-thread/src/main/correction/transform-tokenization.ts similarity index 100% rename from common/web/lm-worker/src/main/correction/transform-tokenization.ts rename to web/src/engine/predictive-text/worker-thread/src/main/correction/transform-tokenization.ts diff --git a/common/web/lm-worker/src/main/index.ts b/web/src/engine/predictive-text/worker-thread/src/main/index.ts similarity index 100% rename from common/web/lm-worker/src/main/index.ts rename to web/src/engine/predictive-text/worker-thread/src/main/index.ts diff --git a/common/web/lm-worker/src/main/model-compositor.ts b/web/src/engine/predictive-text/worker-thread/src/main/model-compositor.ts similarity index 99% rename from common/web/lm-worker/src/main/model-compositor.ts rename to web/src/engine/predictive-text/worker-thread/src/main/model-compositor.ts index 39a6add834a..c6b8367b782 100644 --- a/common/web/lm-worker/src/main/model-compositor.ts +++ b/web/src/engine/predictive-text/worker-thread/src/main/model-compositor.ts @@ -225,7 +225,7 @@ export class ModelCompositor { // Handles display string for reversions triggered by accepting a suggestion mid-token. const preCaretToken = postContextTokenization.left[postContextTokenization.left.length - 1]; revertedPrefix = (preCaretToken && !preCaretToken.isWhitespace) ? preCaretToken.text : ''; - revertedPrefix += postContextTokenization.caretSplitsToken ? postContextTokenization.right[0] : ''; + revertedPrefix += postContextTokenization.caretSplitsToken ? postContextTokenization.right[0].text : ''; } else { revertedPrefix = this.wordbreak(postContext); } diff --git a/common/web/lm-worker/src/main/model-helpers.ts b/web/src/engine/predictive-text/worker-thread/src/main/model-helpers.ts similarity index 100% rename from common/web/lm-worker/src/main/model-helpers.ts rename to web/src/engine/predictive-text/worker-thread/src/main/model-helpers.ts diff --git a/common/web/lm-worker/src/main/models/dummy-model.ts b/web/src/engine/predictive-text/worker-thread/src/main/models/dummy-model.ts similarity index 100% rename from common/web/lm-worker/src/main/models/dummy-model.ts rename to web/src/engine/predictive-text/worker-thread/src/main/models/dummy-model.ts diff --git a/common/web/lm-worker/src/main/models/index.ts b/web/src/engine/predictive-text/worker-thread/src/main/models/index.ts similarity index 100% rename from common/web/lm-worker/src/main/models/index.ts rename to web/src/engine/predictive-text/worker-thread/src/main/models/index.ts diff --git a/common/web/lm-worker/src/main/predict-helpers.ts b/web/src/engine/predictive-text/worker-thread/src/main/predict-helpers.ts similarity index 100% rename from common/web/lm-worker/src/main/predict-helpers.ts rename to web/src/engine/predictive-text/worker-thread/src/main/predict-helpers.ts diff --git a/common/web/lm-worker/src/main/transformUtils.ts b/web/src/engine/predictive-text/worker-thread/src/main/transformUtils.ts similarity index 100% rename from common/web/lm-worker/src/main/transformUtils.ts rename to web/src/engine/predictive-text/worker-thread/src/main/transformUtils.ts diff --git a/common/web/lm-worker/src/main/worker-interfaces.ts b/web/src/engine/predictive-text/worker-thread/src/main/worker-interfaces.ts similarity index 100% rename from common/web/lm-worker/src/main/worker-interfaces.ts rename to web/src/engine/predictive-text/worker-thread/src/main/worker-interfaces.ts diff --git a/common/web/lm-worker/src/main/worker-main.ts b/web/src/engine/predictive-text/worker-thread/src/main/worker-main.ts similarity index 100% rename from common/web/lm-worker/src/main/worker-main.ts rename to web/src/engine/predictive-text/worker-thread/src/main/worker-main.ts diff --git a/common/web/lm-worker/src/polyfills/array.fill.js b/web/src/engine/predictive-text/worker-thread/src/polyfills/array.fill.js similarity index 100% rename from common/web/lm-worker/src/polyfills/array.fill.js rename to web/src/engine/predictive-text/worker-thread/src/polyfills/array.fill.js diff --git a/common/web/lm-worker/src/polyfills/array.findIndex.js b/web/src/engine/predictive-text/worker-thread/src/polyfills/array.findIndex.js similarity index 100% rename from common/web/lm-worker/src/polyfills/array.findIndex.js rename to web/src/engine/predictive-text/worker-thread/src/polyfills/array.findIndex.js diff --git a/common/web/lm-worker/src/polyfills/array.from.js b/web/src/engine/predictive-text/worker-thread/src/polyfills/array.from.js similarity index 100% rename from common/web/lm-worker/src/polyfills/array.from.js rename to web/src/engine/predictive-text/worker-thread/src/polyfills/array.from.js diff --git a/common/web/lm-worker/src/polyfills/array.includes.js b/web/src/engine/predictive-text/worker-thread/src/polyfills/array.includes.js similarity index 100% rename from common/web/lm-worker/src/polyfills/array.includes.js rename to web/src/engine/predictive-text/worker-thread/src/polyfills/array.includes.js diff --git a/common/web/lm-worker/src/polyfills/object.values.js b/web/src/engine/predictive-text/worker-thread/src/polyfills/object.values.js similarity index 100% rename from common/web/lm-worker/src/polyfills/object.values.js rename to web/src/engine/predictive-text/worker-thread/src/polyfills/object.values.js diff --git a/common/web/lm-worker/src/polyfills/symbol-es6.min.js b/web/src/engine/predictive-text/worker-thread/src/polyfills/symbol-es6.min.js similarity index 100% rename from common/web/lm-worker/src/polyfills/symbol-es6.min.js rename to web/src/engine/predictive-text/worker-thread/src/polyfills/symbol-es6.min.js diff --git a/common/web/lm-worker/src/test/mocha/cases/auto-correct.js b/web/src/engine/predictive-text/worker-thread/src/test/mocha/cases/auto-correct.js similarity index 100% rename from common/web/lm-worker/src/test/mocha/cases/auto-correct.js rename to web/src/engine/predictive-text/worker-thread/src/test/mocha/cases/auto-correct.js diff --git a/common/web/lm-worker/src/test/mocha/cases/casing-detection.js b/web/src/engine/predictive-text/worker-thread/src/test/mocha/cases/casing-detection.js similarity index 100% rename from common/web/lm-worker/src/test/mocha/cases/casing-detection.js rename to web/src/engine/predictive-text/worker-thread/src/test/mocha/cases/casing-detection.js diff --git a/common/web/lm-worker/src/test/mocha/cases/early-correction-search-stopping.js b/web/src/engine/predictive-text/worker-thread/src/test/mocha/cases/early-correction-search-stopping.js similarity index 100% rename from common/web/lm-worker/src/test/mocha/cases/early-correction-search-stopping.js rename to web/src/engine/predictive-text/worker-thread/src/test/mocha/cases/early-correction-search-stopping.js diff --git a/common/web/lm-worker/src/test/mocha/cases/edit-distance/classical-calculation.js b/web/src/engine/predictive-text/worker-thread/src/test/mocha/cases/edit-distance/classical-calculation.js similarity index 100% rename from common/web/lm-worker/src/test/mocha/cases/edit-distance/classical-calculation.js rename to web/src/engine/predictive-text/worker-thread/src/test/mocha/cases/edit-distance/classical-calculation.js diff --git a/common/web/lm-worker/src/test/mocha/cases/edit-distance/context-tracker.js b/web/src/engine/predictive-text/worker-thread/src/test/mocha/cases/edit-distance/context-tracker.js similarity index 100% rename from common/web/lm-worker/src/test/mocha/cases/edit-distance/context-tracker.js rename to web/src/engine/predictive-text/worker-thread/src/test/mocha/cases/edit-distance/context-tracker.js diff --git a/common/web/lm-worker/src/test/mocha/cases/edit-distance/distance-modeler.js b/web/src/engine/predictive-text/worker-thread/src/test/mocha/cases/edit-distance/distance-modeler.js similarity index 100% rename from common/web/lm-worker/src/test/mocha/cases/edit-distance/distance-modeler.js rename to web/src/engine/predictive-text/worker-thread/src/test/mocha/cases/edit-distance/distance-modeler.js diff --git a/common/web/lm-worker/src/test/mocha/cases/edit-distance/execution-timer.js b/web/src/engine/predictive-text/worker-thread/src/test/mocha/cases/edit-distance/execution-timer.js similarity index 100% rename from common/web/lm-worker/src/test/mocha/cases/edit-distance/execution-timer.js rename to web/src/engine/predictive-text/worker-thread/src/test/mocha/cases/edit-distance/execution-timer.js diff --git a/common/web/lm-worker/src/test/mocha/cases/predict-from-corrections.js b/web/src/engine/predictive-text/worker-thread/src/test/mocha/cases/predict-from-corrections.js similarity index 100% rename from common/web/lm-worker/src/test/mocha/cases/predict-from-corrections.js rename to web/src/engine/predictive-text/worker-thread/src/test/mocha/cases/predict-from-corrections.js diff --git a/common/web/lm-worker/src/test/mocha/cases/suggestion-deduplication.js b/web/src/engine/predictive-text/worker-thread/src/test/mocha/cases/suggestion-deduplication.js similarity index 100% rename from common/web/lm-worker/src/test/mocha/cases/suggestion-deduplication.js rename to web/src/engine/predictive-text/worker-thread/src/test/mocha/cases/suggestion-deduplication.js diff --git a/common/web/lm-worker/src/test/mocha/cases/suggestion-finalization.js b/web/src/engine/predictive-text/worker-thread/src/test/mocha/cases/suggestion-finalization.js similarity index 100% rename from common/web/lm-worker/src/test/mocha/cases/suggestion-finalization.js rename to web/src/engine/predictive-text/worker-thread/src/test/mocha/cases/suggestion-finalization.js diff --git a/common/web/lm-worker/src/test/mocha/cases/suggestion-similarity.js b/web/src/engine/predictive-text/worker-thread/src/test/mocha/cases/suggestion-similarity.js similarity index 100% rename from common/web/lm-worker/src/test/mocha/cases/suggestion-similarity.js rename to web/src/engine/predictive-text/worker-thread/src/test/mocha/cases/suggestion-similarity.js diff --git a/common/web/lm-worker/src/test/mocha/cases/transform-tokenization.js b/web/src/engine/predictive-text/worker-thread/src/test/mocha/cases/transform-tokenization.js similarity index 100% rename from common/web/lm-worker/src/test/mocha/cases/transform-tokenization.js rename to web/src/engine/predictive-text/worker-thread/src/test/mocha/cases/transform-tokenization.js diff --git a/common/web/lm-worker/src/test/mocha/cases/transform-utils.js b/web/src/engine/predictive-text/worker-thread/src/test/mocha/cases/transform-utils.js similarity index 100% rename from common/web/lm-worker/src/test/mocha/cases/transform-utils.js rename to web/src/engine/predictive-text/worker-thread/src/test/mocha/cases/transform-utils.js diff --git a/common/web/lm-worker/src/test/mocha/cases/worker-custom-punctuation.js b/web/src/engine/predictive-text/worker-thread/src/test/mocha/cases/worker-custom-punctuation.js similarity index 100% rename from common/web/lm-worker/src/test/mocha/cases/worker-custom-punctuation.js rename to web/src/engine/predictive-text/worker-thread/src/test/mocha/cases/worker-custom-punctuation.js diff --git a/common/web/lm-worker/src/test/mocha/cases/worker-initialization.js b/web/src/engine/predictive-text/worker-thread/src/test/mocha/cases/worker-initialization.js similarity index 100% rename from common/web/lm-worker/src/test/mocha/cases/worker-initialization.js rename to web/src/engine/predictive-text/worker-thread/src/test/mocha/cases/worker-initialization.js diff --git a/common/web/lm-worker/src/test/mocha/cases/worker-model-compositor.js b/web/src/engine/predictive-text/worker-thread/src/test/mocha/cases/worker-model-compositor.js similarity index 100% rename from common/web/lm-worker/src/test/mocha/cases/worker-model-compositor.js rename to web/src/engine/predictive-text/worker-thread/src/test/mocha/cases/worker-model-compositor.js diff --git a/common/web/lm-worker/src/test/mocha/cases/worker-predict-dummy.js b/web/src/engine/predictive-text/worker-thread/src/test/mocha/cases/worker-predict-dummy.js similarity index 100% rename from common/web/lm-worker/src/test/mocha/cases/worker-predict-dummy.js rename to web/src/engine/predictive-text/worker-thread/src/test/mocha/cases/worker-predict-dummy.js diff --git a/common/web/lm-worker/src/test/mocha/cases/worker-predict.js b/web/src/engine/predictive-text/worker-thread/src/test/mocha/cases/worker-predict.js similarity index 100% rename from common/web/lm-worker/src/test/mocha/cases/worker-predict.js rename to web/src/engine/predictive-text/worker-thread/src/test/mocha/cases/worker-predict.js diff --git a/common/web/lm-worker/src/test/test-runner/cases/worker.spec.ts b/web/src/engine/predictive-text/worker-thread/src/test/test-runner/cases/worker.spec.ts similarity index 100% rename from common/web/lm-worker/src/test/test-runner/cases/worker.spec.ts rename to web/src/engine/predictive-text/worker-thread/src/test/test-runner/cases/worker.spec.ts diff --git a/common/web/keyboard-processor/tests/dom/web-test-runner.CI.config.mjs b/web/src/engine/predictive-text/worker-thread/src/test/test-runner/web-test-runner.CI.config.mjs similarity index 100% rename from common/web/keyboard-processor/tests/dom/web-test-runner.CI.config.mjs rename to web/src/engine/predictive-text/worker-thread/src/test/test-runner/web-test-runner.CI.config.mjs diff --git a/common/web/lm-worker/src/test/test-runner/web-test-runner.config.mjs b/web/src/engine/predictive-text/worker-thread/src/test/test-runner/web-test-runner.config.mjs similarity index 100% rename from common/web/lm-worker/src/test/test-runner/web-test-runner.config.mjs rename to web/src/engine/predictive-text/worker-thread/src/test/test-runner/web-test-runner.config.mjs diff --git a/web/src/engine/predictive-text/worker-thread/tsconfig.json b/web/src/engine/predictive-text/worker-thread/tsconfig.json new file mode 100644 index 00000000000..bfc8498730b --- /dev/null +++ b/web/src/engine/predictive-text/worker-thread/tsconfig.json @@ -0,0 +1,32 @@ +{ + "extends": "../../../../tsconfig.base.json", + + "compilerOptions": { + "baseUrl": "./", + "outDir": "build/obj", + "tsBuildInfoFile": "build/obj/tsconfig.tsbuildinfo", + "rootDir": "./src/main", + // To help better support legacy Android devices + "downlevelIteration": true, + // Facilitates & simplifies stitching together the worker sourcemaps during the polyfill-concatenation step. + "inlineSourceMap": true, + // May not be set at the same time as the prior setting. + "sourceMap": false, + + // As this one is the one that directly interfaces with the worker (from the inside) + "lib": ["webworker", "es6"], + }, + "references": [ + // types + { "path": "../../../../../common/models/types" }, + { "path": "../types" }, + // modules + { "path": "../../../../../common/web/keyman-version" }, + { "path": "../../../../../common/web/utils" }, + { "path": "../../../../../common/models/templates" }, + { "path": "../../../../../common/models/wordbreakers" }, + ], + "include": [ + "src/main/**/*.ts" + ] +} diff --git a/web/src/test/auto/dom/cases/browser/contextManager.spec.ts b/web/src/test/auto/dom/cases/browser/contextManager.spec.ts index f31cc453185..eabf6eefdd8 100644 --- a/web/src/test/auto/dom/cases/browser/contextManager.spec.ts +++ b/web/src/test/auto/dom/cases/browser/contextManager.spec.ts @@ -3,10 +3,10 @@ import { outputTargetForElement } from 'keyman/engine/attachment'; import { LegacyEventEmitter } from 'keyman/engine/events'; -import { StubAndKeyboardCache, toPrefixedKeyboardId as prefixed } from 'keyman/engine/package-cache'; +import { StubAndKeyboardCache, toPrefixedKeyboardId as prefixed } from 'keyman/engine/keyboard-storage'; -import { KeyboardHarness, MinimalKeymanGlobal } from '@keymanapp/keyboard-processor'; -import { DOMKeyboardLoader } from '@keymanapp/keyboard-processor/dom-keyboard-loader'; +import { KeyboardHarness, MinimalKeymanGlobal } from 'keyman/engine/keyboard'; +import { DOMKeyboardLoader } from 'keyman/engine/keyboard/dom-keyboard-loader'; import { loadKeyboardsFromStubs } from '../../kbdLoader.js'; import { DeviceSpec, ManagedPromise, timedPromise } from '@keymanapp/web-utils'; diff --git a/web/src/test/auto/dom/cases/element-wrappers/element_interfaces.spec.ts b/web/src/test/auto/dom/cases/element-wrappers/element_interfaces.spec.ts index cee86cc35e3..cdfc8814af3 100644 --- a/web/src/test/auto/dom/cases/element-wrappers/element_interfaces.spec.ts +++ b/web/src/test/auto/dom/cases/element-wrappers/element_interfaces.spec.ts @@ -1,6 +1,7 @@ import { assert } from 'chai'; -import { extendString, Mock } from '@keymanapp/keyboard-processor'; +import { extendString } from 'keyman/engine/keyboard'; +import { Mock } from 'keyman/engine/js-processor'; import * as wrappers from 'keyman/engine/element-wrappers'; import { DynamicElements } from '../../test_utils.js'; @@ -1385,7 +1386,7 @@ describe('Element Input/Output Interfacing', function () { // Unique to the Mock type - element interface cloning tests. Is element state properly copied? // As those require a very different setup, they're in the target_mocks.js test case file instead. - // Basic text-retrieval unit tests are now done headlessly in @keymanapp/keyboard-processor. + // Basic text-retrieval unit tests are now done headlessly in keyman/engine/keyboard. describe('Text Mutation', function () { describe('deleteCharsBeforeCaret', function () { diff --git a/web/src/test/auto/dom/cases/element-wrappers/target_mocks.spec.ts b/web/src/test/auto/dom/cases/element-wrappers/target_mocks.spec.ts index e850efaa550..707d8638997 100644 --- a/web/src/test/auto/dom/cases/element-wrappers/target_mocks.spec.ts +++ b/web/src/test/auto/dom/cases/element-wrappers/target_mocks.spec.ts @@ -1,6 +1,7 @@ import { assert } from 'chai'; -import { extendString, Mock } from '@keymanapp/keyboard-processor'; +import { extendString } from 'keyman/engine/keyboard'; +import { Mock } from 'keyman/engine/js-processor'; import { Input } from 'keyman/engine/element-wrappers'; import { DEFAULT_BROWSER_TIMEOUT } from '@keymanapp/common-test-resources/test-timeouts.mjs'; diff --git a/web/src/test/auto/dom/cases/packages/cloudQueries.spec.ts b/web/src/test/auto/dom/cases/keyboard-storage/cloudQueries.spec.ts similarity index 98% rename from web/src/test/auto/dom/cases/packages/cloudQueries.spec.ts rename to web/src/test/auto/dom/cases/keyboard-storage/cloudQueries.spec.ts index 9a2fe93c45a..4af873b23cd 100644 --- a/web/src/test/auto/dom/cases/packages/cloudQueries.spec.ts +++ b/web/src/test/auto/dom/cases/keyboard-storage/cloudQueries.spec.ts @@ -1,9 +1,9 @@ import { assert } from 'chai'; import sinon from 'sinon'; -import { CloudQueryEngine, type KeyboardStub } from 'keyman/engine/package-cache'; +import { CloudQueryEngine, type KeyboardStub } from 'keyman/engine/keyboard-storage'; import { PathConfiguration } from 'keyman/engine/interfaces'; -import DOMCloudRequester from 'keyman/engine/package-cache/dom-requester'; +import DOMCloudRequester from 'keyman/engine/keyboard-storage/dom-requester'; import { ManagedPromise } from '@keymanapp/web-utils'; const pathConfig = new PathConfiguration({ diff --git a/web/src/test/auto/dom/cases/packages/domCloudRequester.spec.ts b/web/src/test/auto/dom/cases/keyboard-storage/domCloudRequester.spec.ts similarity index 97% rename from web/src/test/auto/dom/cases/packages/domCloudRequester.spec.ts rename to web/src/test/auto/dom/cases/keyboard-storage/domCloudRequester.spec.ts index 840acf9d991..7101bfd9f7e 100644 --- a/web/src/test/auto/dom/cases/packages/domCloudRequester.spec.ts +++ b/web/src/test/auto/dom/cases/keyboard-storage/domCloudRequester.spec.ts @@ -1,7 +1,7 @@ import { assert } from 'chai'; import sinon from 'sinon'; -import DOMCloudRequester from 'keyman/engine/package-cache/dom-requester'; +import DOMCloudRequester from 'keyman/engine/keyboard-storage/dom-requester'; describe("Mocked cloud query results ('canary' testing)", () => { function performMockedRequest(mockedResultsPath: string) { diff --git a/web/src/test/auto/dom/cases/packages/keyboardRequisitioner.spec.ts b/web/src/test/auto/dom/cases/keyboard-storage/keyboardRequisitioner.spec.ts similarity index 92% rename from web/src/test/auto/dom/cases/packages/keyboardRequisitioner.spec.ts rename to web/src/test/auto/dom/cases/keyboard-storage/keyboardRequisitioner.spec.ts index ab585bc9e07..bada557bf89 100644 --- a/web/src/test/auto/dom/cases/packages/keyboardRequisitioner.spec.ts +++ b/web/src/test/auto/dom/cases/keyboard-storage/keyboardRequisitioner.spec.ts @@ -1,11 +1,11 @@ import { assert } from 'chai'; import sinon from 'sinon'; -import { KeyboardHarness, MinimalKeymanGlobal } from '@keymanapp/keyboard-processor'; -import { DOMKeyboardLoader } from '@keymanapp/keyboard-processor/dom-keyboard-loader'; +import { KeyboardHarness, MinimalKeymanGlobal } from 'keyman/engine/keyboard'; +import { DOMKeyboardLoader } from 'keyman/engine/keyboard/dom-keyboard-loader'; import { PathConfiguration } from 'keyman/engine/interfaces'; -import { CloudQueryEngine, KeyboardRequisitioner, type KeyboardStub } from 'keyman/engine/package-cache'; -import DOMCloudRequester from 'keyman/engine/package-cache/dom-requester'; +import { CloudQueryEngine, KeyboardRequisitioner, type KeyboardStub } from 'keyman/engine/keyboard-storage'; +import DOMCloudRequester from 'keyman/engine/keyboard-storage/dom-requester'; const pathConfig = new PathConfiguration({ root: '', @@ -14,7 +14,7 @@ const pathConfig = new PathConfiguration({ fonts: '', // The primary / top-level module here is the keyboard-cache bundle. // So, we set up our path config based upon that module for verification purposes. -}, 'http://localhost:9876/@keymanapp/keyman/build/engine/package-cache/lib'); // TODO: set up the current path like actual KMW does it. +}, 'http://localhost:9876/@keymanapp/keyman/build/engine/keyboard-storage/lib'); // TODO: set up the current path like actual KMW does it. /** * Performs mocking setup to facilitate unit testing for the `CloudQueryEngine` class. diff --git a/common/web/keyboard-processor/tests/dom/cases/domKeyboardLoader.spec.ts b/web/src/test/auto/dom/cases/keyboard/domKeyboardLoader.spec.ts similarity index 93% rename from common/web/keyboard-processor/tests/dom/cases/domKeyboardLoader.spec.ts rename to web/src/test/auto/dom/cases/keyboard/domKeyboardLoader.spec.ts index 1965adc8534..da610402eb0 100644 --- a/common/web/keyboard-processor/tests/dom/cases/domKeyboardLoader.spec.ts +++ b/web/src/test/auto/dom/cases/keyboard/domKeyboardLoader.spec.ts @@ -1,7 +1,8 @@ import { assert } from 'chai'; -import { DOMKeyboardLoader } from '@keymanapp/keyboard-processor/dom-keyboard-loader'; -import { extendString, KeyboardHarness, Keyboard, KeyboardInterface, MinimalKeymanGlobal, Mock, DeviceSpec, KeyboardKeymanGlobal } from '@keymanapp/keyboard-processor'; +import { DOMKeyboardLoader } from 'keyman/engine/keyboard/dom-keyboard-loader'; +import { extendString, KeyboardHarness, Keyboard, MinimalKeymanGlobal, DeviceSpec, KeyboardKeymanGlobal } from 'keyman/engine/keyboard'; +import { KeyboardInterface, Mock } from 'keyman/engine/js-processor'; declare let window: typeof globalThis; // KeymanEngine from the web/ folder... when available. diff --git a/web/src/test/auto/dom/kbdLoader.ts b/web/src/test/auto/dom/kbdLoader.ts index 629ade205e9..b0d092c12d5 100644 --- a/web/src/test/auto/dom/kbdLoader.ts +++ b/web/src/test/auto/dom/kbdLoader.ts @@ -1,15 +1,15 @@ import { DOMKeyboardLoader -} from '@keymanapp/keyboard-processor/dom-keyboard-loader'; +} from 'keyman/engine/keyboard/dom-keyboard-loader'; import { Keyboard, - KeyboardInterface, KeyboardProperties, MinimalKeymanGlobal -} from '@keymanapp/keyboard-processor'; +} from 'keyman/engine/keyboard'; -import { KeyboardStub } from 'keyman/engine/package-cache'; +import { KeyboardInterface } from 'keyman/engine/js-processor'; +import { KeyboardStub } from 'keyman/engine/keyboard-storage'; const loader = new DOMKeyboardLoader(new KeyboardInterface(window, MinimalKeymanGlobal)); diff --git a/web/src/test/auto/dom/web-test-runner.config.mjs b/web/src/test/auto/dom/web-test-runner.config.mjs index 1cd8ed0ca29..62edfc7dbf9 100644 --- a/web/src/test/auto/dom/web-test-runner.config.mjs +++ b/web/src/test/auto/dom/web-test-runner.config.mjs @@ -56,9 +56,9 @@ export default { files: ['build/test/dom/cases/osk/**/*.spec.mjs'] }, { - name: 'engine/package-cache', + name: 'engine/keyboard-storage', // Relative, from the containing package.json - files: ['build/test/dom/cases/packages/**/*.spec.mjs'] + files: ['build/test/dom/cases/keyboard-storage/**/*.spec.mjs'] } ], middleware: [ diff --git a/web/src/test/auto/headless/app/browser/hardware-event-processing.tests.ts b/web/src/test/auto/headless/app/browser/hardware-event-processing.tests.ts index 447b2b1806e..d00425362d1 100644 --- a/web/src/test/auto/headless/app/browser/hardware-event-processing.tests.ts +++ b/web/src/test/auto/headless/app/browser/hardware-event-processing.tests.ts @@ -4,14 +4,14 @@ import { preprocessKeyboardEvent } from 'keyman/app/browser'; import { processForMnemonicsAndLegacy } from 'keyman/engine/main'; import { PhysicalInputEventSpec } from '@keymanapp/recorder-core'; import { DeviceSpec } from '@keymanapp/web-utils'; -import { Codes, Keyboard, KeyEvent } from '@keymanapp/keyboard-processor'; +import { Codes, Keyboard, KeyEvent } from 'keyman/engine/keyboard'; const ModifierCodes = Codes.modifierCodes; const KeyCodes = Codes.keyCodes; const DUMMY_DEVICE = new DeviceSpec('chrome', 'desktop', 'windows', false); -// Compare and contrast the unit tests here with those for keyboard-processor unit testing +// Compare and contrast the unit tests here with those for keyboard unit testing // in the non-positional-rules set; the output objects here should have the same format // as the inputs for rules as used there. diff --git a/web/src/test/auto/headless/engine/interfaces/prediction/predictionContext.spec.js b/web/src/test/auto/headless/engine/interfaces/prediction/predictionContext.spec.js index 6bd0f7555cb..4217aeb69b6 100644 --- a/web/src/test/auto/headless/engine/interfaces/prediction/predictionContext.spec.js +++ b/web/src/test/auto/headless/engine/interfaces/prediction/predictionContext.spec.js @@ -4,7 +4,8 @@ import sinon from 'sinon'; import { LanguageProcessor, TranscriptionCache } from 'keyman/engine/main'; import { PredictionContext } from 'keyman/engine/interfaces'; import { Worker as LMWorker } from "@keymanapp/lexical-model-layer/node"; -import { DeviceSpec, KeyboardProcessor, Mock } from '@keymanapp/keyboard-processor'; +import { DeviceSpec } from 'keyman/engine/keyboard'; +import { Mock } from 'keyman/engine/js-processor'; function compileDummyModel(suggestionSets) { return ` @@ -14,14 +15,6 @@ LMLayerWorker.loadModel(new models.DummyModel({ `; } -// Common spec used for each test's setup. It's actually irrelevant for the tests, -// but KeyboardProcessor needs an instance. -const deviceSpec = new DeviceSpec( - DeviceSpec.Browser.Chrome, - DeviceSpec.FormFactor.Desktop, - DeviceSpec.OperatingSystem.Windows -); - const appleDummySuggestionSets = [[ // Set 1: { @@ -66,6 +59,10 @@ const appleDummyModel = { code: compileDummyModel(appleDummySuggestionSets) }; +function dummiedGetLayer() { + return 'default'; +} + describe("PredictionContext", () => { let worker; @@ -81,8 +78,7 @@ describe("PredictionContext", () => { const langProcessor = new LanguageProcessor(worker, new TranscriptionCache()); await langProcessor.loadModel(appleDummyModel); // await: must fully 'configure', load script into worker. - const kbdProcessor = new KeyboardProcessor(deviceSpec); - const predictiveContext = new PredictionContext(langProcessor, kbdProcessor); + const predictiveContext = new PredictionContext(langProcessor, dummiedGetLayer); let updateFake = sinon.fake(); predictiveContext.on('update', updateFake); @@ -107,7 +103,7 @@ describe("PredictionContext", () => { mock.insertTextBeforeCaret('e'); // appl| + e = apple let transcription = mock.buildTranscriptionFrom(initialMock, null, true); - await langProcessor.predict(transcription, kbdProcessor.layerId); + await langProcessor.predict(transcription, dummiedGetLayer()); // First predict call results: our second set of dummy suggestions, the first of which includes // a 'keep' of the original text. @@ -122,8 +118,7 @@ describe("PredictionContext", () => { const langProcessor = new LanguageProcessor(worker, new TranscriptionCache()); await langProcessor.loadModel(appleDummyModel); // await: must fully 'configure', load script into worker. - const kbdProcessor = new KeyboardProcessor(deviceSpec); - const predictiveContext = new PredictionContext(langProcessor, kbdProcessor); + const predictiveContext = new PredictionContext(langProcessor, dummiedGetLayer); let updateFake = sinon.fake(); predictiveContext.on('update', updateFake); @@ -150,13 +145,13 @@ describe("PredictionContext", () => { // Mocking: corresponds to the second set of mocked predictions - round 2 of // 'apple', 'apply', 'apples'. - const skippedPromise = langProcessor.predict(baseTranscription, kbdProcessor.layerId); + const skippedPromise = langProcessor.predict(baseTranscription, dummiedGetLayer()); mock.insertTextBeforeCaret('e'); // appl| + e = apple const finalTranscription = mock.buildTranscriptionFrom(initialMock, null, true); // Mocking: corresponds to the third set of mocked predictions - 'applied'. - const expectedPromise = langProcessor.predict(finalTranscription, kbdProcessor.layerId); + const expectedPromise = langProcessor.predict(finalTranscription, dummiedGetLayer()); await Promise.all([skippedPromise, expectedPromise]); const expected = await expectedPromise; @@ -181,8 +176,7 @@ describe("PredictionContext", () => { const langProcessor = new LanguageProcessor(worker, new TranscriptionCache()); await langProcessor.loadModel(appleDummyModel); // await: must fully 'configure', load script into worker. - const kbdProcessor = new KeyboardProcessor(deviceSpec); - const predictiveContext = new PredictionContext(langProcessor, kbdProcessor); + const predictiveContext = new PredictionContext(langProcessor, dummiedGetLayer); let mock = new Mock("appl", 4); // "appl|", with '|' as the caret position. const initialSuggestions = await predictiveContext.setCurrentTarget(mock); @@ -207,8 +201,7 @@ describe("PredictionContext", () => { const langProcessor = new LanguageProcessor(worker, new TranscriptionCache()); await langProcessor.loadModel(appleDummyModel); // await: must fully 'configure', load script into worker. - const kbdProcessor = new KeyboardProcessor(deviceSpec); - const predictiveContext = new PredictionContext(langProcessor, kbdProcessor); + const predictiveContext = new PredictionContext(langProcessor, dummiedGetLayer); let textState = new Mock("appl", 4); // "appl|", with '|' as the caret position. @@ -222,7 +215,7 @@ describe("PredictionContext", () => { let previousTextState = Mock.from(textState); textState.insertTextBeforeCaret('e'); // appl| + e = apple let transcription = textState.buildTranscriptionFrom(previousTextState, null, true); - await langProcessor.predict(transcription, kbdProcessor.layerId); + await langProcessor.predict(transcription, dummiedGetLayer()); // Verify setup. assert.equal(updateFake.callCount, 1); @@ -275,8 +268,7 @@ describe("PredictionContext", () => { const langProcessor = new LanguageProcessor(worker, new TranscriptionCache()); await langProcessor.loadModel(appleDummyModel); // await: must fully 'configure', load script into worker. - const kbdProcessor = new KeyboardProcessor(deviceSpec); - const predictiveContext = new PredictionContext(langProcessor, kbdProcessor); + const predictiveContext = new PredictionContext(langProcessor, dummiedGetLayer); let textState = new Mock("appl", 4); // "appl|", with '|' as the caret position. @@ -291,7 +283,7 @@ describe("PredictionContext", () => { let suggestionCaptureFake = sinon.fake(); predictiveContext.once('update', suggestionCaptureFake); - await langProcessor.predict(transcription, kbdProcessor.layerId); + await langProcessor.predict(transcription, dummiedGetLayer()); // We need to capture the suggestion we wish to apply. We could hardcode a forced // value, but that might become brittle in the long-term. diff --git a/common/web/keyboard-processor/tests/node/basic-engine.js b/web/src/test/auto/headless/engine/js-processor/basic-engine.js similarity index 87% rename from common/web/keyboard-processor/tests/node/basic-engine.js rename to web/src/test/auto/headless/engine/js-processor/basic-engine.js index 1e7ff3a205e..68eb5136e3d 100644 --- a/common/web/keyboard-processor/tests/node/basic-engine.js +++ b/web/src/test/auto/headless/engine/js-processor/basic-engine.js @@ -4,8 +4,9 @@ import fs from 'fs'; import { createRequire } from 'module'; const require = createRequire(import.meta.url); -import { KeyboardInterface, MinimalKeymanGlobal } from '@keymanapp/keyboard-processor'; -import { NodeKeyboardLoader } from '@keymanapp/keyboard-processor/node-keyboard-loader'; +import { MinimalKeymanGlobal } from 'keyman/engine/keyboard'; +import { KeyboardInterface } from 'keyman/engine/js-processor'; +import { NodeKeyboardLoader } from 'keyman/engine/keyboard/node-keyboard-loader'; import { KeyboardTest, NodeProctor } from '@keymanapp/recorder-core'; describe('Engine - Basic Simulation', function() { @@ -24,7 +25,7 @@ describe('Engine - Basic Simulation', function() { before(async function() { // -- START: Standard Recorder-based unit test loading boilerplate -- let keyboardLoader = new NodeKeyboardLoader(new KeyboardInterface({}, MinimalKeymanGlobal)); - let keyboard = await keyboardLoader.loadKeyboardFromPath('../../test/' + testSuite.keyboard.filename); + let keyboard = await keyboardLoader.loadKeyboardFromPath('../../../../../common/test/' + testSuite.keyboard.filename); keyboardWithHarness = keyboardLoader.harness; keyboardWithHarness.activeKeyboard = keyboard; diff --git a/common/web/keyboard-processor/tests/node/basic-init.js b/web/src/test/auto/headless/engine/js-processor/basic-init.js similarity index 92% rename from common/web/keyboard-processor/tests/node/basic-init.js rename to web/src/test/auto/headless/engine/js-processor/basic-init.js index 8f04a6a6e8b..cabdb9d4322 100644 --- a/common/web/keyboard-processor/tests/node/basic-init.js +++ b/web/src/test/auto/headless/engine/js-processor/basic-init.js @@ -3,8 +3,8 @@ import { assert } from 'chai'; import { createRequire } from 'module'; const require = createRequire(import.meta.url); -import { KeyboardProcessor } from '@keymanapp/keyboard-processor'; -import { NodeKeyboardLoader } from '@keymanapp/keyboard-processor/node-keyboard-loader'; +import { KeyboardProcessor } from 'keyman/engine/js-processor'; +import { NodeKeyboardLoader } from 'keyman/engine/keyboard/node-keyboard-loader'; global.keyman = {}; // So that keyboard-based checks against the global `keyman` succeed. // 10.0+ dependent keyboards, like khmer_angkor, will otherwise fail to load. diff --git a/common/web/keyboard-processor/tests/node/bundled-module.js b/web/src/test/auto/headless/engine/js-processor/bundled-module.js similarity index 74% rename from common/web/keyboard-processor/tests/node/bundled-module.js rename to web/src/test/auto/headless/engine/js-processor/bundled-module.js index bfff937b626..a6b80d50043 100644 --- a/common/web/keyboard-processor/tests/node/bundled-module.js +++ b/web/src/test/auto/headless/engine/js-processor/bundled-module.js @@ -1,5 +1,6 @@ import { assert } from "chai"; -import * as Package from "../../build/lib/index.mjs"; +import * as Package from "keyman/engine/js-processor"; +import * as Package2 from "keyman/engine/keyboard"; // A few small tests to ensure that the ES Module bundle was successfully constructed and is usable. @@ -12,7 +13,7 @@ var toSupplementaryPairString = function(code){ let u = toSupplementaryPairString; -describe('Bundled ES Module', function() { +describe('Bundled ES Module for js-processor', function() { describe('KeyboardProcessor', function () { it('should initialize without errors', function () { let kp = new Package.KeyboardProcessor(); @@ -40,11 +41,20 @@ describe('Bundled ES Module', function() { } }); }); +}); - describe("Imported `utils`", function() { +describe('Bundled ES Module for keyboard', function () { + describe('Keyboard', function () { + it('should initialize without errors', function () { + let kp = new Package2.Keyboard(); + assert.isNotNull(kp); + }); + }); + + describe("Imported `utils`", function () { it("should include `utils` package's Version class", () => { - let v16 = new Package.Version([16, 1]); + let v16 = new Package2.Version([16, 1]); assert.equal(v16.toString(), "16.1"); }); - }) -}); \ No newline at end of file + }); +}); diff --git a/common/web/keyboard-processor/tests/node/chirality.js b/web/src/test/auto/headless/engine/js-processor/chirality.js similarity index 97% rename from common/web/keyboard-processor/tests/node/chirality.js rename to web/src/test/auto/headless/engine/js-processor/chirality.js index 70dca3d47fd..c2220b7f5d2 100644 --- a/common/web/keyboard-processor/tests/node/chirality.js +++ b/web/src/test/auto/headless/engine/js-processor/chirality.js @@ -4,8 +4,9 @@ import fs from 'fs'; import { createRequire } from 'module'; const require = createRequire(import.meta.url); -import { KeyboardInterface, MinimalKeymanGlobal } from '@keymanapp/keyboard-processor'; -import { NodeKeyboardLoader } from '@keymanapp/keyboard-processor/node-keyboard-loader'; +import { MinimalKeymanGlobal } from 'keyman/engine/keyboard'; +import { KeyboardInterface } from 'keyman/engine/js-processor'; +import { NodeKeyboardLoader } from 'keyman/engine/keyboard/node-keyboard-loader'; import { KeyboardTest, NodeProctor } from '@keymanapp/recorder-core'; import { ModifierKeyConstants } from '@keymanapp/common-types'; @@ -25,7 +26,7 @@ describe('Engine - Chirality', function() { before(async function() { // -- START: Standard Recorder-based unit test loading boilerplate -- let keyboardLoader = new NodeKeyboardLoader(new KeyboardInterface({}, MinimalKeymanGlobal)); - let keyboard = await keyboardLoader.loadKeyboardFromPath('../../test/' + testSuite.keyboard.filename); + let keyboard = await keyboardLoader.loadKeyboardFromPath('../../../../../common/test/' + testSuite.keyboard.filename); keyboardWithHarness = keyboardLoader.harness; keyboardWithHarness.activeKeyboard = keyboard; diff --git a/common/web/keyboard-processor/tests/node/deadkeys.js b/web/src/test/auto/headless/engine/js-processor/deadkeys.js similarity index 87% rename from common/web/keyboard-processor/tests/node/deadkeys.js rename to web/src/test/auto/headless/engine/js-processor/deadkeys.js index e2fe70837eb..fd5d1457d3d 100644 --- a/common/web/keyboard-processor/tests/node/deadkeys.js +++ b/web/src/test/auto/headless/engine/js-processor/deadkeys.js @@ -4,8 +4,9 @@ import fs from 'fs'; import { createRequire } from 'module'; const require = createRequire(import.meta.url); -import { KeyboardInterface, MinimalKeymanGlobal } from '@keymanapp/keyboard-processor'; -import { NodeKeyboardLoader } from '@keymanapp/keyboard-processor/node-keyboard-loader'; +import { MinimalKeymanGlobal } from 'keyman/engine/keyboard'; +import { KeyboardInterface } from 'keyman/engine/js-processor'; +import { NodeKeyboardLoader } from 'keyman/engine/keyboard/node-keyboard-loader'; import { KeyboardTest, NodeProctor } from '@keymanapp/recorder-core'; describe('Engine - Deadkeys', function() { @@ -24,7 +25,7 @@ describe('Engine - Deadkeys', function() { before(async function() { // -- START: Standard Recorder-based unit test loading boilerplate -- let keyboardLoader = new NodeKeyboardLoader(new KeyboardInterface({}, MinimalKeymanGlobal)); - let keyboard = await keyboardLoader.loadKeyboardFromPath('../../test/' + testSuite.keyboard.filename); + let keyboard = await keyboardLoader.loadKeyboardFromPath('../../../../../common/test/' + testSuite.keyboard.filename); keyboardWithHarness = keyboardLoader.harness; keyboardWithHarness.activeKeyboard = keyboard; diff --git a/common/web/keyboard-processor/tests/node/engine/context.js b/web/src/test/auto/headless/engine/js-processor/engine/context.js similarity index 99% rename from common/web/keyboard-processor/tests/node/engine/context.js rename to web/src/test/auto/headless/engine/js-processor/engine/context.js index a1fe5863df9..a355ead2650 100644 --- a/common/web/keyboard-processor/tests/node/engine/context.js +++ b/web/src/test/auto/headless/engine/js-processor/engine/context.js @@ -3,8 +3,9 @@ import { assert } from 'chai'; import { createRequire } from 'module'; const require = createRequire(import.meta.url); -import { KeyboardInterface, KeyboardProcessor, MinimalKeymanGlobal, Mock } from '@keymanapp/keyboard-processor'; -import { NodeKeyboardLoader } from '@keymanapp/keyboard-processor/node-keyboard-loader'; +import { MinimalKeymanGlobal } from 'keyman/engine/keyboard'; +import { KeyboardInterface, KeyboardProcessor, Mock } from 'keyman/engine/js-processor'; +import { NodeKeyboardLoader } from 'keyman/engine/keyboard/node-keyboard-loader'; import { NodeProctor, RecordedKeystrokeSequence } from '@keymanapp/recorder-core'; diff --git a/common/web/keyboard-processor/tests/node/engine/notany_context.js b/web/src/test/auto/headless/engine/js-processor/engine/notany_context.js similarity index 94% rename from common/web/keyboard-processor/tests/node/engine/notany_context.js rename to web/src/test/auto/headless/engine/js-processor/engine/notany_context.js index 2329463d465..ecc7dd92d0d 100644 --- a/common/web/keyboard-processor/tests/node/engine/notany_context.js +++ b/web/src/test/auto/headless/engine/js-processor/engine/notany_context.js @@ -3,8 +3,9 @@ import { assert } from 'chai'; import { createRequire } from 'module'; const require = createRequire(import.meta.url); -import { KeyboardInterface, MinimalKeymanGlobal, Mock } from '@keymanapp/keyboard-processor'; -import { NodeKeyboardLoader } from '@keymanapp/keyboard-processor/node-keyboard-loader'; +import { MinimalKeymanGlobal } from 'keyman/engine/keyboard'; +import { KeyboardInterface, Mock } from 'keyman/engine/js-processor'; +import { NodeKeyboardLoader } from 'keyman/engine/keyboard/node-keyboard-loader'; import { NodeProctor, RecordedKeystrokeSequence } from '@keymanapp/recorder-core'; import { extendString } from '@keymanapp/web-utils'; diff --git a/common/web/keyboard-processor/tests/node/engine/stores.js b/web/src/test/auto/headless/engine/js-processor/engine/stores.js similarity index 94% rename from common/web/keyboard-processor/tests/node/engine/stores.js rename to web/src/test/auto/headless/engine/js-processor/engine/stores.js index ade1c2bd471..8174cbe0779 100644 --- a/common/web/keyboard-processor/tests/node/engine/stores.js +++ b/web/src/test/auto/headless/engine/js-processor/engine/stores.js @@ -1,6 +1,7 @@ import { assert } from 'chai'; -import { Keyboard, KeyboardProcessor } from '@keymanapp/keyboard-processor'; +import { Keyboard } from 'keyman/engine/keyboard'; +import { KeyboardProcessor } from 'keyman/engine/js-processor'; import { extendString } from '@keymanapp/web-utils'; extendString(); diff --git a/common/web/keyboard-processor/tests/node/engine/unmatched_final_group.js b/web/src/test/auto/headless/engine/js-processor/engine/unmatched_final_group.js similarity index 84% rename from common/web/keyboard-processor/tests/node/engine/unmatched_final_group.js rename to web/src/test/auto/headless/engine/js-processor/engine/unmatched_final_group.js index 81f9765da4c..8bfee041ebe 100644 --- a/common/web/keyboard-processor/tests/node/engine/unmatched_final_group.js +++ b/web/src/test/auto/headless/engine/js-processor/engine/unmatched_final_group.js @@ -4,8 +4,9 @@ import fs from 'fs'; import { createRequire } from 'module'; const require = createRequire(import.meta.url); -import { KeyboardInterface, MinimalKeymanGlobal } from '@keymanapp/keyboard-processor'; -import { NodeKeyboardLoader } from '@keymanapp/keyboard-processor/node-keyboard-loader'; +import { MinimalKeymanGlobal } from 'keyman/engine/keyboard'; +import { KeyboardInterface } from 'keyman/engine/js-processor'; +import { NodeKeyboardLoader } from 'keyman/engine/keyboard/node-keyboard-loader'; import { KeyboardTest, NodeProctor } from '@keymanapp/recorder-core'; describe('Engine - Unmatched Final Groups', function() { @@ -23,7 +24,7 @@ describe('Engine - Unmatched Final Groups', function() { before(async function() { // -- START: Standard Recorder-based unit test loading boilerplate -- let keyboardLoader = new NodeKeyboardLoader(new KeyboardInterface({}, MinimalKeymanGlobal)); - const keyboard = await keyboardLoader.loadKeyboardFromPath('../../test/' + testSuite.keyboard.filename); + const keyboard = await keyboardLoader.loadKeyboardFromPath('../../../../../common/test/' + testSuite.keyboard.filename); keyboardWithHarness = keyboardLoader.harness; keyboardWithHarness.activeKeyboard = keyboard; diff --git a/web/src/test/auto/headless/engine/js-processor/kbdInterface.tests.js b/web/src/test/auto/headless/engine/js-processor/kbdInterface.tests.js new file mode 100644 index 00000000000..0d084964425 --- /dev/null +++ b/web/src/test/auto/headless/engine/js-processor/kbdInterface.tests.js @@ -0,0 +1,91 @@ +import { assert } from 'chai'; + +import { createRequire } from 'module'; +const require = createRequire(import.meta.url); + +import { MinimalKeymanGlobal } from 'keyman/engine/keyboard'; +import { KeyboardInterface, Mock } from 'keyman/engine/js-processor'; +import { NodeKeyboardLoader } from 'keyman/engine/keyboard/node-keyboard-loader'; + +describe('Headless keyboard loading', function () { + const laoPath = require.resolve('@keymanapp/common-test-resources/keyboards/lao_2008_basic.js'); + const khmerPath = require.resolve('@keymanapp/common-test-resources/keyboards/khmer_angkor.js'); + const nonKeyboardPath = require.resolve('@keymanapp/common-test-resources/index.mjs'); + const ipaPath = require.resolve('@keymanapp/common-test-resources/keyboards/sil_ipa.js'); + // Common test suite setup. + + let device = { + formFactor: 'desktop', + OS: 'windows', + browser: 'native' + } + + describe('Full harness loading', () => { + it('successfully loads', async function () { + // -- START: Standard Recorder-based unit test loading boilerplate -- + let harness = new KeyboardInterface({}, MinimalKeymanGlobal); + let keyboardLoader = new NodeKeyboardLoader(harness); + let keyboard = await keyboardLoader.loadKeyboardFromPath(laoPath); + harness.activeKeyboard = keyboard; + // -- END: Standard Recorder-based unit test loading boilerplate -- + + // This part provides assurance that the keyboard properly loaded. + assert.equal(keyboard.id, "Keyboard_lao_2008_basic"); + }); + + it('can evaluate rules', async function () { + // -- START: Standard Recorder-based unit test loading boilerplate -- + let harness = new KeyboardInterface({}, MinimalKeymanGlobal); + let keyboardLoader = new NodeKeyboardLoader(harness); + let keyboard = await keyboardLoader.loadKeyboardFromPath(laoPath); + harness.activeKeyboard = keyboard; + // -- END: Standard Recorder-based unit test loading boilerplate -- + + // Runs a blank KeyEvent through the keyboard's rule processing. + harness.processKeystroke(new Mock(), keyboard.constructNullKeyEvent(device)); + }); + + it('does not change the active kehboard', async function () { + let harness = new KeyboardInterface({}, MinimalKeymanGlobal); + let keyboardLoader = new NodeKeyboardLoader(harness); + const lao_keyboard = await keyboardLoader.loadKeyboardFromPath(laoPath); + assert.isNotOk(harness.activeKeyboard); + assert.isOk(lao_keyboard); + + // This part provides assurance that the keyboard properly loaded. + assert.equal(lao_keyboard.id, "Keyboard_lao_2008_basic"); + + harness.activeKeyboard = lao_keyboard; + + const khmer_keyboard = await keyboardLoader.loadKeyboardFromPath(khmerPath); + assert.strictEqual(lao_keyboard, harness.activeKeyboard); + + assert.equal(khmer_keyboard.id, "Keyboard_khmer_angkor"); + }); + + it('throws distinct errors', async function () { + const invalidPath = 'totally_invalid_path.js'; + + let harness = new KeyboardInterface({}, MinimalKeymanGlobal); + let keyboardLoader = new NodeKeyboardLoader(harness); + let missingError; + try { + await keyboardLoader.loadKeyboardFromPath(invalidPath); + } catch (err) { + missingError = err; + } + assert.isOk(missingError); + + let scriptLoadError; + try { + await keyboardLoader.loadKeyboardFromPath(nonKeyboardPath); + } catch (err) { + scriptLoadError = err; + } + assert.isOk(scriptLoadError); + + // The main test: do the errors match? + assert.notEqual(scriptLoadError.message, missingError.message); + }); + }) +}); diff --git a/common/web/keyboard-processor/tests/node/mocks.js b/web/src/test/auto/headless/engine/js-processor/mocks.js similarity index 98% rename from common/web/keyboard-processor/tests/node/mocks.js rename to web/src/test/auto/headless/engine/js-processor/mocks.js index 9a84e2a8cc0..bad81bfcca4 100644 --- a/common/web/keyboard-processor/tests/node/mocks.js +++ b/web/src/test/auto/headless/engine/js-processor/mocks.js @@ -1,5 +1,5 @@ import { assert } from 'chai'; -import { Mock } from '@keymanapp/keyboard-processor'; +import { Mock } from 'keyman/engine/js-processor'; describe('Mocks', function() { describe('app|les', () => { diff --git a/common/web/keyboard-processor/tests/node/non-positional-rules.js b/web/src/test/auto/headless/engine/js-processor/non-positional-rules.js similarity index 97% rename from common/web/keyboard-processor/tests/node/non-positional-rules.js rename to web/src/test/auto/headless/engine/js-processor/non-positional-rules.js index aba54643aee..a65cec6708d 100644 --- a/common/web/keyboard-processor/tests/node/non-positional-rules.js +++ b/web/src/test/auto/headless/engine/js-processor/non-positional-rules.js @@ -4,8 +4,9 @@ import { ModifierKeyConstants } from '@keymanapp/common-types'; import { createRequire } from 'module'; const require = createRequire(import.meta.url); -import { Codes, KeyboardInterface, KeyEvent, MinimalKeymanGlobal, Mock } from '@keymanapp/keyboard-processor'; -import { NodeKeyboardLoader } from '@keymanapp/keyboard-processor/node-keyboard-loader'; +import { Codes, KeyEvent, MinimalKeymanGlobal } from 'keyman/engine/keyboard'; +import { KeyboardInterface, Mock } from 'keyman/engine/js-processor'; +import { NodeKeyboardLoader } from 'keyman/engine/keyboard/node-keyboard-loader'; // Compare and contrast the unit tests here with those for app/browser key-event unit testing // in the hardware-event-processing set; the output objects there should have the same format diff --git a/common/web/keyboard-processor/tests/node/specialized-backspace.js b/web/src/test/auto/headless/engine/js-processor/specialized-backspace.js similarity index 98% rename from common/web/keyboard-processor/tests/node/specialized-backspace.js rename to web/src/test/auto/headless/engine/js-processor/specialized-backspace.js index db2be071e8f..7e35817f07f 100644 --- a/common/web/keyboard-processor/tests/node/specialized-backspace.js +++ b/web/src/test/auto/headless/engine/js-processor/specialized-backspace.js @@ -4,8 +4,9 @@ import fs from 'fs'; import { createRequire } from 'module'; const require = createRequire(import.meta.url); -import { Codes, KeyboardInterface, KeyboardProcessor, KeyEvent, MinimalKeymanGlobal, Mock } from '@keymanapp/keyboard-processor'; -import { NodeKeyboardLoader } from '@keymanapp/keyboard-processor/node-keyboard-loader'; +import { Codes, KeyEvent, MinimalKeymanGlobal } from 'keyman/engine/keyboard'; +import { KeyboardInterface, KeyboardProcessor, Mock } from 'keyman/engine/js-processor'; +import { NodeKeyboardLoader } from 'keyman/engine/keyboard/node-keyboard-loader'; import { ModifierKeyConstants } from '@keymanapp/common-types'; diff --git a/common/web/keyboard-processor/tests/node/transcriptions.js b/web/src/test/auto/headless/engine/js-processor/transcriptions.js similarity index 99% rename from common/web/keyboard-processor/tests/node/transcriptions.js rename to web/src/test/auto/headless/engine/js-processor/transcriptions.js index b071532bf2a..45c09b3b80c 100644 --- a/common/web/keyboard-processor/tests/node/transcriptions.js +++ b/web/src/test/auto/headless/engine/js-processor/transcriptions.js @@ -1,6 +1,6 @@ import { assert } from 'chai'; -import { Mock, findCommonSubstringEndIndex } from '@keymanapp/keyboard-processor'; +import { Mock, findCommonSubstringEndIndex } from 'keyman/engine/js-processor'; import { extendString } from '@keymanapp/web-utils'; extendString(); // Ensure KMW's string-extension functionality is available. diff --git a/web/src/test/auto/headless/engine/package-cache/cloudQueries.js b/web/src/test/auto/headless/engine/keyboard-storage/cloudQueries.js similarity index 98% rename from web/src/test/auto/headless/engine/package-cache/cloudQueries.js rename to web/src/test/auto/headless/engine/keyboard-storage/cloudQueries.js index 4f379ea5636..eb078c17681 100644 --- a/web/src/test/auto/headless/engine/package-cache/cloudQueries.js +++ b/web/src/test/auto/headless/engine/keyboard-storage/cloudQueries.js @@ -2,9 +2,9 @@ import { assert } from 'chai'; import sinon from 'sinon'; import { ManagedPromise } from '@keymanapp/web-utils'; -import { CloudQueryEngine, StubAndKeyboardCache, toPrefixedKeyboardId as prefixed } from 'keyman/engine/package-cache'; +import { CloudQueryEngine, StubAndKeyboardCache, toPrefixedKeyboardId as prefixed } from 'keyman/engine/keyboard-storage'; import { PathConfiguration } from 'keyman/engine/interfaces'; -import NodeCloudRequester from 'keyman/engine/package-cache/node-requester'; +import NodeCloudRequester from 'keyman/engine/keyboard-storage/node-requester'; import path from 'path'; import { fileURLToPath } from 'url'; diff --git a/web/src/test/auto/headless/engine/package-cache/keyboardRequisitioner.js b/web/src/test/auto/headless/engine/keyboard-storage/keyboardRequisitioner.js similarity index 97% rename from web/src/test/auto/headless/engine/package-cache/keyboardRequisitioner.js rename to web/src/test/auto/headless/engine/keyboard-storage/keyboardRequisitioner.js index b2d64c05538..b158f4ef9a2 100644 --- a/web/src/test/auto/headless/engine/package-cache/keyboardRequisitioner.js +++ b/web/src/test/auto/headless/engine/keyboard-storage/keyboardRequisitioner.js @@ -2,14 +2,14 @@ import { assert } from 'chai'; import sinon from 'sinon'; import fs from 'fs'; -import { KeyboardHarness, ManagedPromise, MinimalKeymanGlobal } from '@keymanapp/keyboard-processor'; -import { NodeKeyboardLoader } from '@keymanapp/keyboard-processor/node-keyboard-loader'; +import { KeyboardHarness, ManagedPromise, MinimalKeymanGlobal } from 'keyman/engine/keyboard'; +import { NodeKeyboardLoader } from 'keyman/engine/keyboard/node-keyboard-loader'; import { KeyboardRequisitioner, toPrefixedKeyboardId as prefixed -} from 'keyman/engine/package-cache'; +} from 'keyman/engine/keyboard-storage'; import { PathConfiguration } from 'keyman/engine/interfaces'; -import NodeCloudRequester from 'keyman/engine/package-cache/node-requester'; +import NodeCloudRequester from 'keyman/engine/keyboard-storage/node-requester'; import path from 'path'; import { fileURLToPath } from 'url'; @@ -33,7 +33,7 @@ const pathConfig = new PathConfiguration({ // Keyboard paths in fixtures are relative to the repository root. keyboards: `file:///${resolvedResourcePackage}/../../..`, // the one part NEEDED for unit tests below. fonts: '', -}, `file:///${path.dirname(require.resolve('keyman/engine/package-cache'))}`); +}, `file:///${path.dirname(require.resolve('keyman/engine/keyboard-storage'))}`); /** * Performs mocking setup to facilitate unit testing for the `CloudQueryEngine` class. diff --git a/web/src/test/auto/headless/engine/package-cache/keyboardStub.js b/web/src/test/auto/headless/engine/keyboard-storage/keyboardStub.js similarity index 96% rename from web/src/test/auto/headless/engine/package-cache/keyboardStub.js rename to web/src/test/auto/headless/engine/keyboard-storage/keyboardStub.js index 6b81e187a46..af7d6b6a57b 100644 --- a/web/src/test/auto/headless/engine/package-cache/keyboardStub.js +++ b/web/src/test/auto/headless/engine/keyboard-storage/keyboardStub.js @@ -1,8 +1,8 @@ import { assert } from 'chai'; import sinon from 'sinon'; -import { KeyboardStub } from 'keyman/engine/package-cache'; -import NodeCloudRequester from 'keyman/engine/package-cache/node-requester'; +import { KeyboardStub } from 'keyman/engine/keyboard-storage'; +import NodeCloudRequester from 'keyman/engine/keyboard-storage/node-requester'; import path from 'path'; import { fileURLToPath } from 'url'; diff --git a/web/src/test/auto/headless/engine/package-cache/nodeCloudRequester.js b/web/src/test/auto/headless/engine/keyboard-storage/nodeCloudRequester.js similarity index 97% rename from web/src/test/auto/headless/engine/package-cache/nodeCloudRequester.js rename to web/src/test/auto/headless/engine/keyboard-storage/nodeCloudRequester.js index 708781932c2..07ac9e98fb6 100644 --- a/web/src/test/auto/headless/engine/package-cache/nodeCloudRequester.js +++ b/web/src/test/auto/headless/engine/keyboard-storage/nodeCloudRequester.js @@ -1,7 +1,7 @@ import { assert } from 'chai'; import sinon from 'sinon'; -import NodeCloudRequester from 'keyman/engine/package-cache/node-requester'; +import NodeCloudRequester from 'keyman/engine/keyboard-storage/node-requester'; import path from 'path'; import { fileURLToPath } from 'url'; diff --git a/web/src/test/auto/headless/engine/package-cache/stubAndKeyboardCache.js b/web/src/test/auto/headless/engine/keyboard-storage/stubAndKeyboardCache.js similarity index 97% rename from web/src/test/auto/headless/engine/package-cache/stubAndKeyboardCache.js rename to web/src/test/auto/headless/engine/keyboard-storage/stubAndKeyboardCache.js index 03f6fbf5c5a..7260b9982e7 100644 --- a/web/src/test/auto/headless/engine/package-cache/stubAndKeyboardCache.js +++ b/web/src/test/auto/headless/engine/keyboard-storage/stubAndKeyboardCache.js @@ -2,10 +2,10 @@ import { assert } from 'chai'; import sinon from 'sinon'; import fs from 'fs'; -import { KeyboardStub, StubAndKeyboardCache } from 'keyman/engine/package-cache'; +import { KeyboardStub, StubAndKeyboardCache } from 'keyman/engine/keyboard-storage'; -import { NodeKeyboardLoader } from '@keymanapp/keyboard-processor/node-keyboard-loader'; -import { KeyboardHarness, MinimalKeymanGlobal } from '@keymanapp/keyboard-processor'; +import { NodeKeyboardLoader } from 'keyman/engine/keyboard/node-keyboard-loader'; +import { KeyboardHarness, MinimalKeymanGlobal } from 'keyman/engine/keyboard'; import path from 'path'; diff --git a/common/web/keyboard-processor/tests/node/keyboard-loading.js b/web/src/test/auto/headless/engine/keyboard/keyboard-loading.js similarity index 56% rename from common/web/keyboard-processor/tests/node/keyboard-loading.js rename to web/src/test/auto/headless/engine/keyboard/keyboard-loading.js index 500780dfedb..0dd89a36148 100644 --- a/common/web/keyboard-processor/tests/node/keyboard-loading.js +++ b/web/src/test/auto/headless/engine/keyboard/keyboard-loading.js @@ -1,11 +1,11 @@ import { assert } from 'chai'; -import fs from 'fs'; import { createRequire } from 'module'; const require = createRequire(import.meta.url); -import { KeyboardHarness, KeyboardInterface, KeyboardLoaderBase, MinimalKeymanGlobal, Mock } from '@keymanapp/keyboard-processor'; -import { NodeKeyboardLoader } from '@keymanapp/keyboard-processor/node-keyboard-loader'; +import { KeyboardHarness, MinimalKeymanGlobal } from 'keyman/engine/keyboard'; +import { KeyboardInterface, Mock } from 'keyman/engine/js-processor'; +import { NodeKeyboardLoader } from 'keyman/engine/keyboard/node-keyboard-loader'; describe('Headless keyboard loading', function() { const laoPath = require.resolve('@keymanapp/common-test-resources/keyboards/lao_2008_basic.js'); @@ -40,7 +40,7 @@ describe('Headless keyboard loading', function() { it('successfully loads (has variable stores)', async () => { // -- START: Standard Recorder-based unit test loading boilerplate -- - let harness = new KeyboardInterface({}, MinimalKeymanGlobal); + let harness = new KeyboardHarness({}, MinimalKeymanGlobal); let keyboardLoader = new NodeKeyboardLoader(harness); let keyboard = await keyboardLoader.loadKeyboardFromPath(ipaPath); // -- END: Standard Recorder-based unit test loading boilerplate -- @@ -91,73 +91,4 @@ describe('Headless keyboard loading', function() { assert.isFalse(mobileLayout.hasMultitaps); }); }); - - describe('Full harness loading', () => { - it('successfully loads', async function() { - // -- START: Standard Recorder-based unit test loading boilerplate -- - let harness = new KeyboardInterface({}, MinimalKeymanGlobal); - let keyboardLoader = new NodeKeyboardLoader(harness); - let keyboard = await keyboardLoader.loadKeyboardFromPath(laoPath); - harness.activeKeyboard = keyboard; - // -- END: Standard Recorder-based unit test loading boilerplate -- - - // This part provides assurance that the keyboard properly loaded. - assert.equal(keyboard.id, "Keyboard_lao_2008_basic"); - }); - - it('can evaluate rules', async function() { - // -- START: Standard Recorder-based unit test loading boilerplate -- - let harness = new KeyboardInterface({}, MinimalKeymanGlobal); - let keyboardLoader = new NodeKeyboardLoader(harness); - let keyboard = await keyboardLoader.loadKeyboardFromPath(laoPath); - harness.activeKeyboard = keyboard; - // -- END: Standard Recorder-based unit test loading boilerplate -- - - // Runs a blank KeyEvent through the keyboard's rule processing. - harness.processKeystroke(new Mock(), keyboard.constructNullKeyEvent(device)); - }); - - it('does not change the active kehboard', async function() { - let harness = new KeyboardInterface({}, MinimalKeymanGlobal); - let keyboardLoader = new NodeKeyboardLoader(harness); - const lao_keyboard = await keyboardLoader.loadKeyboardFromPath(laoPath); - assert.isNotOk(harness.activeKeyboard); - assert.isOk(lao_keyboard); - - // This part provides assurance that the keyboard properly loaded. - assert.equal(lao_keyboard.id, "Keyboard_lao_2008_basic"); - - harness.activeKeyboard = lao_keyboard; - - const khmer_keyboard = await keyboardLoader.loadKeyboardFromPath(khmerPath); - assert.strictEqual(lao_keyboard, harness.activeKeyboard); - - assert.equal(khmer_keyboard.id, "Keyboard_khmer_angkor"); - }); - - it('throws distinct errors', async function() { - const invalidPath = 'totally_invalid_path.js'; - - let harness = new KeyboardInterface({}, MinimalKeymanGlobal); - let keyboardLoader = new NodeKeyboardLoader(harness); - let missingError; - try { - await keyboardLoader.loadKeyboardFromPath(invalidPath); - } catch (err) { - missingError = err; - } - assert.isOk(missingError); - - let scriptLoadError; - try { - await keyboardLoader.loadKeyboardFromPath(nonKeyboardPath); - } catch (err) { - scriptLoadError = err; - } - assert.isOk(scriptLoadError); - - // The main test: do the errors match? - assert.notEqual(scriptLoadError.message, missingError.message); - }); - }) -}); \ No newline at end of file +}); diff --git a/common/web/keyboard-processor/tests/node/keyboard-properties.js b/web/src/test/auto/headless/engine/keyboard/keyboard-properties.js similarity index 98% rename from common/web/keyboard-processor/tests/node/keyboard-properties.js rename to web/src/test/auto/headless/engine/keyboard/keyboard-properties.js index b6ec2dbf8d2..f23d56e3b52 100644 --- a/common/web/keyboard-processor/tests/node/keyboard-properties.js +++ b/web/src/test/auto/headless/engine/keyboard/keyboard-properties.js @@ -5,7 +5,7 @@ import path from 'path'; import { createRequire } from 'module'; const require = createRequire(import.meta.url); -import { KeyboardProperties, SpacebarText } from '@keymanapp/keyboard-processor'; +import { KeyboardProperties, SpacebarText } from 'keyman/engine/keyboard'; describe('Keyboard Properties', function() { let commonResourcesPackage = '@keymanapp/common-test-resources'; diff --git a/web/src/test/auto/headless/engine/main/headless/inputProcessor.spec.js b/web/src/test/auto/headless/engine/main/headless/inputProcessor.spec.js index d1b728e6253..56aa2f3e5ed 100644 --- a/web/src/test/auto/headless/engine/main/headless/inputProcessor.spec.js +++ b/web/src/test/auto/headless/engine/main/headless/inputProcessor.spec.js @@ -5,8 +5,8 @@ import { createRequire } from 'module'; const require = createRequire(import.meta.url); import { InputProcessor } from 'keyman/engine/main'; -import { KeyboardInterface, MinimalKeymanGlobal, Mock } from '@keymanapp/keyboard-processor'; -import { NodeKeyboardLoader } from '@keymanapp/keyboard-processor/node-keyboard-loader'; +import { KeyboardInterface, MinimalKeymanGlobal, Mock } from 'keyman/engine/keyboard'; +import { NodeKeyboardLoader } from 'keyman/engine/keyboard/node-keyboard-loader'; import { KeyboardTest } from '@keymanapp/recorder-core'; import { Worker } from '@keymanapp/lexical-model-layer/node'; @@ -48,8 +48,8 @@ describe('InputProcessor', function() { assert.isUndefined(core.activeKeyboard); // No keyboard should be loaded yet. assert.isUndefined(core.activeModel); // Same for the model. - // These checks are lifted from the keyboard-processor init checks found in - // common/web/keyboard-processor/tests/cases/basic-init.js. + // These checks are lifted from the keyboard init checks found in + // web/src/test/auto/headless/engine/js-processor/basic-init.js. assert.equal('us', core.keyboardProcessor.baseLayout, 'KeyboardProcessor has unexpected base layout') assert.isNotNull(global.KeymanWeb, 'KeymanWeb global was not automatically installed'); assert.equal('default', core.keyboardProcessor.layerId, 'Default layer is not set to "default"'); diff --git a/web/src/test/auto/headless/engine/main/headless/languageProcessor.spec.js b/web/src/test/auto/headless/engine/main/headless/languageProcessor.spec.js index a0eec4c5319..e4572de21d0 100644 --- a/web/src/test/auto/headless/engine/main/headless/languageProcessor.spec.js +++ b/web/src/test/auto/headless/engine/main/headless/languageProcessor.spec.js @@ -2,7 +2,7 @@ import { assert } from 'chai'; import { LanguageProcessor, TranscriptionCache } from 'keyman/engine/main'; import { SourcemappedWorker as LMWorker } from "@keymanapp/lexical-model-layer/node"; -import { Mock } from '@keymanapp/keyboard-processor'; +import { Mock } from 'keyman/engine/keyboard'; /* * Unit tests for the Dummy prediction model. @@ -43,8 +43,8 @@ describe('LanguageProcessor', function() { it('has expected default values after initialization', function () { let languageProcessor = new LanguageProcessor(worker, new TranscriptionCache()); - // These checks are lifted from the keyboard-processor init checks found in - // common/web/keyboard-processor/tests/cases/basic-init.js. + // These checks are lifted from the keyboard init checks found in + // web/src/test/auto/headless/engine/js-processor/basic-init.js. assert.isDefined(languageProcessor.lmEngine); assert.isUndefined(languageProcessor.activeModel); assert.isFalse(languageProcessor.isActive); diff --git a/web/src/test/auto/headless/engine/osk/input/gestures/browser/subkeyPopup.tests.ts b/web/src/test/auto/headless/engine/osk/input/gestures/browser/subkeyPopup.tests.ts index b70c17d7dc1..72679897f37 100644 --- a/web/src/test/auto/headless/engine/osk/input/gestures/browser/subkeyPopup.tests.ts +++ b/web/src/test/auto/headless/engine/osk/input/gestures/browser/subkeyPopup.tests.ts @@ -4,7 +4,7 @@ import { JSDOM } from 'jsdom'; import sinon from 'sinon'; import { GesturePath, GestureSequence, GestureSource, GestureSourceSubview } from '@keymanapp/gesture-recognizer'; -import { ActiveSubKey, DeviceSpec } from '@keymanapp/keyboard-processor'; +import { ActiveSubKey, DeviceSpec } from 'keyman/engine/keyboard'; import { DEFAULT_GESTURE_PARAMS, KeyElement, VisualKeyboard } from 'keyman/engine/osk'; import { OSKBaseKey, OSKRow, SubkeyPopup, link } from 'keyman/engine/osk/internals'; diff --git a/web/src/test/auto/integrated/cases/engine.spec.ts b/web/src/test/auto/integrated/cases/engine.spec.ts index 1ef78358a2e..a7e624cbf44 100644 --- a/web/src/test/auto/integrated/cases/engine.spec.ts +++ b/web/src/test/auto/integrated/cases/engine.spec.ts @@ -9,7 +9,7 @@ import { import * as KMWRecorder from '#recorder'; import { type KeymanEngine, type KeyboardInterface } from 'keyman/app/browser'; -import { type KeyboardStub } from 'keyman/engine/package-cache'; +import { type KeyboardStub } from 'keyman/engine/keyboard-storage'; import { type OSKInputEventSpec } from '#recorder'; type WindowKey = keyof typeof window; diff --git a/web/src/test/auto/integrated/test_utils.ts b/web/src/test/auto/integrated/test_utils.ts index e3c49ef0ee2..6e04885b1a5 100644 --- a/web/src/test/auto/integrated/test_utils.ts +++ b/web/src/test/auto/integrated/test_utils.ts @@ -3,7 +3,7 @@ import Device from 'keyman/engine/device-detect'; import * as KMWRecorder from '#recorder'; import { type BrowserInitOptionSpec, type KeymanEngine } from 'keyman/app/browser'; -import { ErrorStub, type KeyboardAPISpec, type KeyboardStub } from 'keyman/engine/package-cache'; +import { ErrorStub, type KeyboardAPISpec, type KeyboardStub } from 'keyman/engine/keyboard-storage'; type WindowKey = keyof typeof window; const keyman_window = 'keyman' as WindowKey; diff --git a/common/test/predictive-text/.gitignore b/web/src/test/manual/predictive-text/.gitignore similarity index 100% rename from common/test/predictive-text/.gitignore rename to web/src/test/manual/predictive-text/.gitignore diff --git a/common/test/predictive-text/README.md b/web/src/test/manual/predictive-text/README.md similarity index 96% rename from common/test/predictive-text/README.md rename to web/src/test/manual/predictive-text/README.md index ddf89bb8be5..0ab038ca11a 100644 --- a/common/test/predictive-text/README.md +++ b/web/src/test/manual/predictive-text/README.md @@ -9,10 +9,9 @@ Install **NOTE**: Requires Node >= 10.0 First, ensure that Keyman web and the LMLayer are built. You can run the -build script in `/web/source` to do this for you: +build script in `/web` to do this for you: - cd ../../../web/source - ./build.sh + ../../../../web/build.sh Then, you can install locally with `npm`: diff --git a/common/test/predictive-text/index.js b/web/src/test/manual/predictive-text/index.mjs similarity index 96% rename from common/test/predictive-text/index.js rename to web/src/test/manual/predictive-text/index.mjs index 934dcf67a6c..33398a69577 100755 --- a/common/test/predictive-text/index.js +++ b/web/src/test/manual/predictive-text/index.mjs @@ -5,16 +5,17 @@ * model. */ -const fs = require('fs'); -const path = require('path'); -const readline = require('readline'); +import fs from 'fs'; +import path from 'path'; +import readline from 'readline'; +import vm from 'vm'; -const {EventIterator} = require('event-iterator'); -const program = require('commander'); +import {EventIterator} from 'event-iterator'; +import program from 'commander'; // Load the most recent LMLayer code locally. -const LMLayer = require('../../predictive-text'); - +import { LMLayer } from 'keyman/engine/predictive-text/worker-main'; +import { LMLayerWorker } from '@keymanapp/lm-worker'; ///////////////////////////////// Constants ///////////////////////////////// @@ -22,7 +23,7 @@ const WORKER_DEBUG = false; const EXIT_USAGE = 1; /** "Control sequence introducer" for ANSI escape codes: */ -const CSI = '\033['; +const CSI = '\u001b['; /** * ANSI escape codes to deal with the screen and the cursor. * See https://en.wikipedia.org/wiki/ANSI_escape_code#CSI_sequences @@ -47,8 +48,7 @@ main(); function main() { // Command line options: program - .name(require('./package.json').name) - .version(require('./package.json').version) + .name('lmlayer-cli') .usage('[-i | -p [-p ...]] (-f | )') .description('CLI for trying lexical models.') .arguments('[model-id]') @@ -330,9 +330,6 @@ async function asyncRepl(modelFile) { function createAsyncWorker() { - // XXX: import the LMLayerWorker directly -- I know where it is built. - const LMLayerWorker = require('../../predictive-text/build/intermediate'); - const vm = require('vm'); let worker = { postMessage(message) { diff --git a/common/test/predictive-text/package.json b/web/src/test/manual/predictive-text/package.json similarity index 100% rename from common/test/predictive-text/package.json rename to web/src/test/manual/predictive-text/package.json diff --git a/web/src/test/manual/web/chirality/chirality.js b/web/src/test/manual/web/chirality/chirality.js index eaf349463c1..68740ffc308 100644 --- a/web/src/test/manual/web/chirality/chirality.js +++ b/web/src/test/manual/web/chirality/chirality.js @@ -10,10 +10,10 @@ */ function Keyboard_chirality() { - // Refer to $KEYMAN_ROOT/common/web/keyboard-processor/src/text/codes.ts, same method name. + // Refer to $KEYMAN_ROOT/web/src/engine/keyboard/src/text/codes.ts, same method name. // May be moved within common/web/types at some point in the future. var getModifierState = function(layerId) { - // Refer to C:\keymanapp\keyman\common\web\keyboard-processor\src\keyboards\keyboardHarness.ts, + // Refer to C:\keymanapp\keyman\web\src\engine\keyboard\src\keyboards\keyboardHarness.ts, // `MinimalKeyboardHarness`. let osk = keyman.osk; // Codes endpoint, as part of the standard keyboard harness. @@ -86,7 +86,7 @@ function Keyboard_chirality() { this.g0 = function (t, e) { var k = KeymanWeb, r = 0, m = 0; - // Refer to C:\keymanapp\keyman\common\web\keyboard-processor\src\keyboards\keyboardHarness.ts, + // Refer to C:\keymanapp\keyman\web\src\engine\keyboard\src\keyboards\keyboardHarness.ts, // `MinimalKeyboardHarness`. var Constants = keyman.osk; // Holds anchor points for relevant Codes properties. diff --git a/web/src/test/manual/web/osk/kbdLoader.mjs b/web/src/test/manual/web/osk/kbdLoader.mjs index a9db786c33d..edf2d3a6484 100644 --- a/web/src/test/manual/web/osk/kbdLoader.mjs +++ b/web/src/test/manual/web/osk/kbdLoader.mjs @@ -1,16 +1,16 @@ import { Keyboard, KeyboardProperties, Codes } from '../../../../../build/engine/osk/lib/index.mjs'; // // The following block would be sufficient to replace the `loadKeyboardFromPath` func below... -// // were it not for common/web/keyboard-processor being outside of the standard `localhost` config +// // were it not for web/src/engine/keyboard/ being outside of the standard `localhost` config // // used for manual Web testing. // import { // DOMKeyboardLoader -// } from '../../../../../../common/web/keyboard-processor/build/lib/dom-keyboard-loader.mjs'; +// } from '../../../keyboard/build/lib/dom-keyboard-loader.mjs'; // import { // KeyboardInterface, // MinimalKeymanGlobal -// } from '../../../../../../common/web/keyboard-processor/build/lib/index.mjs'; +// } from '../../../keyboard/build/lib/index.mjs'; // // This script may or may not be temporary; the KMW "KeyboardManager" class may be spun off in a manner // // that could replace this. diff --git a/web/src/tools/build.sh b/web/src/tools/build.sh index 24d455bc8fd..b7670418ab3 100755 --- a/web/src/tools/build.sh +++ b/web/src/tools/build.sh @@ -14,7 +14,7 @@ THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" builder_describe "Builds the Keyman Engine for Web's development & unit-testing tools" \ "@/common/web/keyman-version" \ - "@/common/web/keyboard-processor" \ + "@/web/src/engine/keyboard" \ "configure" \ "clean" \ "build" \ diff --git a/web/src/tools/testing/recorder-core/package.json b/web/src/tools/testing/recorder-core/package.json index 4812d8a7787..426595e567e 100644 --- a/web/src/tools/testing/recorder-core/package.json +++ b/web/src/tools/testing/recorder-core/package.json @@ -19,7 +19,6 @@ }, "homepage": "https://github.com/keymanapp/keyman#readme", "dependencies": { - "@keymanapp/keyboard-processor": "*", "@keymanapp/models-types": "*", "@keymanapp/keyman-version": "*", "@keymanapp/web-utils": "*" diff --git a/web/src/tools/testing/recorder-core/src/index.ts b/web/src/tools/testing/recorder-core/src/index.ts index 94c2c9dc4d8..2a739710232 100644 --- a/web/src/tools/testing/recorder-core/src/index.ts +++ b/web/src/tools/testing/recorder-core/src/index.ts @@ -1,5 +1,5 @@ -import { type OutputTarget } from "@keymanapp/keyboard-processor"; -import { KeyDistribution, KeyEvent, Mock } from "@keymanapp/keyboard-processor"; +import { Mock, type OutputTarget } from "keyman/engine/js-processor"; +import { KeyDistribution, KeyEvent } from "keyman/engine/keyboard"; import Proctor from "./proctor.js"; diff --git a/web/src/tools/testing/recorder-core/src/nodeProctor.ts b/web/src/tools/testing/recorder-core/src/nodeProctor.ts index 6f6d7fd6a0d..204ef219394 100644 --- a/web/src/tools/testing/recorder-core/src/nodeProctor.ts +++ b/web/src/tools/testing/recorder-core/src/nodeProctor.ts @@ -8,8 +8,10 @@ import { RecordedSyntheticKeystroke } from "./index.js"; -import { KeyboardInterface, KeyEvent, KeyEventSpec, KeyboardProcessor, Mock, type OutputTarget, KeyboardHarness } from "@keymanapp/keyboard-processor"; +import { KeyEvent, KeyEventSpec, KeyboardHarness } from "keyman/engine/keyboard"; +import { Mock, type OutputTarget } from "keyman/engine/js-processor"; import { DeviceSpec } from "@keymanapp/web-utils"; +import { KeyboardInterface, KeyboardProcessor } from 'keyman/engine/js-processor'; export default class NodeProctor extends Proctor { private keyboardWithHarness: KeyboardHarness; diff --git a/web/src/tools/testing/recorder-core/src/proctor.ts b/web/src/tools/testing/recorder-core/src/proctor.ts index 940fa4a76b1..ec4f584b048 100644 --- a/web/src/tools/testing/recorder-core/src/proctor.ts +++ b/web/src/tools/testing/recorder-core/src/proctor.ts @@ -1,5 +1,5 @@ import { type DeviceSpec } from "@keymanapp/web-utils"; -import { type OutputTarget } from "@keymanapp/keyboard-processor"; +import { type OutputTarget } from "keyman/engine/js-processor"; import type { KeyboardTest, TestSet, TestSequence } from "./index.js"; @@ -9,7 +9,7 @@ export type AssertCallback = (s1: any, s2: any, msg?: string) => void; * Facilitates running Recorder-generated tests on various platforms. * * Note that DOM-aware KeymanWeb will implement a Browser-based version, while - * keyboard-processor and input-processor will use a Node-based version instead. + * keyboard and input-processor will use a Node-based version instead. */ export default abstract class Proctor { device: DeviceSpec; diff --git a/web/src/tools/testing/recorder-core/tsconfig.json b/web/src/tools/testing/recorder-core/tsconfig.json index a7bd08c3745..01e14319451 100644 --- a/web/src/tools/testing/recorder-core/tsconfig.json +++ b/web/src/tools/testing/recorder-core/tsconfig.json @@ -16,7 +16,8 @@ "references": [ { "path": "../../../../../common/web/keyman-version" }, { "path": "../../../../../common/web/utils/" }, - { "path": "../../../../../common/web/keyboard-processor/" }, - { "path": "../../../../../common/web/lm-message-types" } + { "path": "../../../engine/predictive-text/types" }, + { "path": "../../../engine/js-processor" }, + { "path": "../../../engine/keyboard" }, ], } diff --git a/web/src/tools/testing/recorder/browserProctor.ts b/web/src/tools/testing/recorder/browserProctor.ts index 40e7247423a..a48e5572cbe 100644 --- a/web/src/tools/testing/recorder/browserProctor.ts +++ b/web/src/tools/testing/recorder/browserProctor.ts @@ -4,7 +4,7 @@ import { type DeviceSpec } from "@keymanapp/web-utils"; -import { type OutputTarget } from "@keymanapp/keyboard-processor"; +import { type OutputTarget } from "keyman/engine/js-processor"; import { type KeymanEngine } from 'keyman/app/browser'; diff --git a/web/src/tools/testing/recorder/build.sh b/web/src/tools/testing/recorder/build.sh index 28cc43a767d..91f27d09e57 100755 --- a/web/src/tools/testing/recorder/build.sh +++ b/web/src/tools/testing/recorder/build.sh @@ -16,7 +16,7 @@ SUBPROJECT_NAME=tools/testing/recorder builder_describe "Builds the Keyman Engine for Web's test-sequence recording tool" \ "@/common/web/keyman-version" \ - "@/common/web/keyboard-processor" \ + "@/web/src/engine/keyboard" \ "@../recorder-core" \ "clean" \ "configure" \ diff --git a/web/src/tools/testing/recorder/scribe.ts b/web/src/tools/testing/recorder/scribe.ts index 371afadeefe..c266f1a5ccf 100644 --- a/web/src/tools/testing/recorder/scribe.ts +++ b/web/src/tools/testing/recorder/scribe.ts @@ -1,7 +1,7 @@ import { EventEmitter } from "eventemitter3"; -import type { KeyEvent } from "@keymanapp/keyboard-processor"; +import type { KeyEvent } from "keyman/engine/keyboard"; import { Constraint, diff --git a/web/src/tools/testing/recorder/tsconfig.json b/web/src/tools/testing/recorder/tsconfig.json index 63cb543efa1..e25a4f60341 100644 --- a/web/src/tools/testing/recorder/tsconfig.json +++ b/web/src/tools/testing/recorder/tsconfig.json @@ -12,9 +12,9 @@ "references": [ { "path": "../../../../../common/web/keyman-version" }, { "path": "../../../../../common/web/utils" }, - { "path": "../../../../../common/web/keyboard-processor" }, - { "path": "../../../../../common/web/lm-message-types" }, - { "path": "../../../../../web/src/app/browser" }, + { "path": "../../../engine/predictive-text/types" }, + { "path": "../../../app/browser" }, { "path": "../recorder-core" }, + { "path": "../../../engine/keyboard" }, ] } diff --git a/windows/src/desktop/kmshell/xml/splash.xsl b/windows/src/desktop/kmshell/xml/splash.xsl index 51514e91118..956447d0201 100644 --- a/windows/src/desktop/kmshell/xml/splash.xsl +++ b/windows/src/desktop/kmshell/xml/splash.xsl @@ -30,7 +30,13 @@
-
+
+ + + + + +
diff --git a/windows/src/desktop/kmshell/xml/strings.xml b/windows/src/desktop/kmshell/xml/strings.xml index 1e1df09ce00..774a4d5e425 100644 --- a/windows/src/desktop/kmshell/xml/strings.xml +++ b/windows/src/desktop/kmshell/xml/strings.xml @@ -792,13 +792,18 @@ keyboard that you use in Windows. Keyman keyboards will adapt automatically to - Keyman + Keyman Start Keyman + + + + Start %1$s + @@ -855,7 +860,7 @@ keyboard that you use in Windows. Keyman keyboards will adapt automatically to - Keyman + Keyman @@ -1021,7 +1026,7 @@ keyboard that you use in Windows. Keyman keyboards will adapt automatically to - Keyman + Keyman