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