-

+

+
diff --git a/android/KMAPro/kMAPro/src/main/assets/banner/keyman_banner_dark.png b/android/KMAPro/kMAPro/src/main/assets/banner/keyman_banner_dark.png
new file mode 100644
index 0000000000..644af21a2d
Binary files /dev/null and b/android/KMAPro/kMAPro/src/main/assets/banner/keyman_banner_dark.png differ
diff --git a/android/KMAPro/kMAPro/src/main/java/com/keyman/android/BannerController.java b/android/KMAPro/kMAPro/src/main/java/com/keyman/android/BannerController.java
index f716faa9aa..5c5a16e597 100644
--- a/android/KMAPro/kMAPro/src/main/java/com/keyman/android/BannerController.java
+++ b/android/KMAPro/kMAPro/src/main/java/com/keyman/android/BannerController.java
@@ -13,7 +13,7 @@ public class BannerController {
public static final String KM_BANNER_DIR = "banner";
public static final String KM_BANNER_THEME_KEYMAN = "banner.html";
- public static void setHTMLBanner(Context context, KMManager.KeyboardType keyboardType) {
+ public static void setHTMLBanner(Context context, KMManager.KeyboardType keyboardType, boolean isDarkMode) {
if (keyboardType == KMManager.KeyboardType.KEYBOARD_TYPE_UNDEFINED) {
return;
}
@@ -22,6 +22,9 @@ public class BannerController {
// Always use Keyman banner theme
String contents = FileUtils.readContents(context, KM_BANNER_THEME_KEYMAN);
+ if (isDarkMode) {
+ contents = contents.replace("class=\"keyman-banner\"", "class=\"keyman-banner dark\"");
+ }
KMManager.setHTMLBanner(keyboardType, contents);
KMManager.setBanner(keyboardType, KMManager.BannerType.HTML);
KMManager.showBanner(true);
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 16330bfcda..c3939c03f8 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
@@ -7,7 +7,7 @@ package com.keyman.android;
import com.keyman.engine.util.DownloadFileUtils;
import com.tavultesoft.kmapro.BuildConfig;
import com.tavultesoft.kmapro.DefaultLanguageResource;
-import com.tavultesoft.kmapro.KeymanSettingsActivity;
+import com.tavultesoft.kmapro.KeymanSettingsKeys;
import com.tavultesoft.kmapro.PreferencesManager;
import com.keyman.engine.KMManager;
import com.keyman.engine.KMManager.KeyboardType;
@@ -76,13 +76,13 @@ public class SystemKeyboard extends InputMethodService implements OnKeyboardEven
KMManager.setInputMethodService(this); // for HW interface
SharedPreferences prefs = context.getSharedPreferences(context.getString(R.string.kma_prefs_name), Context.MODE_PRIVATE);
- KMManager.SpacebarText spacebarText = KMManager.SpacebarText.fromString(prefs.getString(KeymanSettingsActivity.spacebarTextKey, KMManager.SpacebarText.LANGUAGE_KEYBOARD.toString()));
+ KMManager.SpacebarText spacebarText = KMManager.SpacebarText.fromString(prefs.getString(KeymanSettingsKeys.SPACEBAR_TEXT, KMManager.SpacebarText.LANGUAGE_KEYBOARD.toString()));
KMManager.setSpacebarText(spacebarText);
// Set the system keyboard HTML banner
- BannerController.setHTMLBanner(this, KeyboardType.KEYBOARD_TYPE_SYSTEM);
+ BannerController.setHTMLBanner(this, KeyboardType.KEYBOARD_TYPE_SYSTEM, isDarkMode());
- boolean mayHaveHapticFeedback = prefs.getBoolean(KeymanSettingsActivity.hapticFeedbackKey, false);
+ boolean mayHaveHapticFeedback = prefs.getBoolean(KeymanSettingsKeys.HAPTIC_FEEDBACK, false);
KMManager.setHapticFeedback(mayHaveHapticFeedback);
// Checking for updates should never be allowed to crash the keyboard.
@@ -176,7 +176,7 @@ public class SystemKeyboard extends InputMethodService implements OnKeyboardEven
if (KMManager.getPredictionsSuspended(KeyboardType.KEYBOARD_TYPE_SYSTEM)) {
KMManager.setBannerOptions(false);
// Set the system keyboard HTML banner
- BannerController.setHTMLBanner(this, KeyboardType.KEYBOARD_TYPE_SYSTEM);
+ BannerController.setHTMLBanner(this, KeyboardType.KEYBOARD_TYPE_SYSTEM, isDarkMode());
} else if (KMManager.isKeyboardLoaded(KeyboardType.KEYBOARD_TYPE_SYSTEM)){
// Check if predictions needs to be re-enabled per Settings preference
Keyboard kbInfo = KMManager.getCurrentKeyboardInfo(appContext);
@@ -282,7 +282,7 @@ public class SystemKeyboard extends InputMethodService implements OnKeyboardEven
@Override
public void onKeyboardChanged(String newKeyboard) {
// Refresh banner theme
- BannerController.setHTMLBanner(this, KeyboardType.KEYBOARD_TYPE_SYSTEM);
+ BannerController.setHTMLBanner(this, KeyboardType.KEYBOARD_TYPE_SYSTEM, isDarkMode());
KMManager.showSystemKeyboard();
sendCurrentFontName();
}
@@ -290,7 +290,7 @@ public class SystemKeyboard extends InputMethodService implements OnKeyboardEven
@Override
public void onKeyboardShown() {
// Refresh banner theme
- BannerController.setHTMLBanner(this, KeyboardType.KEYBOARD_TYPE_SYSTEM);
+ BannerController.setHTMLBanner(this, KeyboardType.KEYBOARD_TYPE_SYSTEM, isDarkMode());
}
@Override
@@ -309,7 +309,7 @@ public class SystemKeyboard extends InputMethodService implements OnKeyboardEven
Context context = getApplicationContext();
SharedPreferences prefs = context.getSharedPreferences(PreferencesManager.kma_prefs_name, Context.MODE_PRIVATE);
- boolean showOSK = prefs.getBoolean(KeymanSettingsActivity.oskWithPhysicalKeyboardKey, false);
+ boolean showOSK = prefs.getBoolean(KeymanSettingsKeys.OSK_WITH_PHYSICAL_KEYBOARD, false);
return showOSK;
}
@@ -349,4 +349,8 @@ public class SystemKeyboard extends InputMethodService implements OnKeyboardEven
public boolean onKeyLongPress(int keyCode, KeyEvent event) {
return interpreter.onKeyLongPress(keyCode, event);
}
+ private boolean isDarkMode() {
+ return (getResources().getConfiguration().uiMode &
+ Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES;
+ }
}
\ No newline at end of file
diff --git a/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/GetStartedActivity.java b/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/GetStartedActivity.java
index 59558fdb1f..2fc9e90d41 100644
--- a/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/GetStartedActivity.java
+++ b/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/GetStartedActivity.java
@@ -16,7 +16,7 @@ import android.os.Bundle;
import android.provider.Settings;
import android.view.View;
import android.view.Window;
-import android.view.inputmethod.InputMethodInfo;
+
import android.view.inputmethod.InputMethodManager;
import android.widget.AdapterView;
import android.widget.CheckBox;
@@ -24,7 +24,7 @@ import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.ImageButton;
import android.widget.ListView;
-import androidx.appcompat.app.AppCompatActivity;
+
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
@@ -36,14 +36,25 @@ public class GetStartedActivity extends BaseActivity {
private static ArrayList
> list = null;
private static KMListAdapter listAdapter = null;
protected static final String showGetStartedKey = "ShowGetStarted";
+ public static final String openDefaultKeyboardPickerOnlyKey = "OpenDefaultKeyboardPickerOnly";
private final String iconKey = "icon";
private final String textKey = "text";
private final String isEnabledKey = "isEnabled";
+ private boolean openDefaultKeyboardPickerOnly = false;
+ private boolean didAutoOpenDefaultKeyboardPicker = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
+ openDefaultKeyboardPickerOnly = getIntent().getBooleanExtra(openDefaultKeyboardPickerOnlyKey, false);
+
+ if (openDefaultKeyboardPickerOnly) {
+ // Picker-only mode: intentionally skip rendering Get Started UI.
+ KMManager.dontCloseParentAppOnShowKeyboardPicker();
+ return;
+ }
+
final Context context = this;
setContentView(R.layout.get_started_list_layout);
@@ -161,20 +172,40 @@ public class GetStartedActivity extends BaseActivity {
super.onResume();
}
- @Override
- protected void onPause() {
- super.onPause();
- }
-
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if (hasFocus) {
+ if (openDefaultKeyboardPickerOnly) {
+
+ // When the user taps 'Set Default Keyboard' from the drawer,
+ // GetStartedActivity is launched in picker-only mode. But you
+ // can't open the system keyboard picker directly from a menu
+ // tap — it requires a window with focus. So we use
+ // onWindowFocusChanged as the trigger point: first focus opens
+ // the picker, and when the user dismisses it and focus returns
+ // to this activity, we call finish() to close it. The activity
+ // is essentially just a invisible bridge to show the system
+ // picker.
+
+ if (!didAutoOpenDefaultKeyboardPicker) {
+ didAutoOpenDefaultKeyboardPicker = true;
+ InputMethodManager imManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
+ if (imManager != null) {
+ imManager.showInputMethodPicker();
+ }
+ } else {
+ // Focus returned after the picker was dismissed, so close the host activity.
+ finish();
+ }
+ return;
+ }
+
// Enumerated steps are replaced with checkboxes when the user completes a step
String one = String.valueOf(R.drawable.ic_looks_one);
String two = String.valueOf(R.drawable.ic_looks_two);
String three = String.valueOf(R.drawable.ic_looks_three);
- String checkbox_on = String.valueOf(android.R.drawable.checkbox_on_background);
+ String checkbox_on = String.valueOf(R.drawable.ic_checkbox);
String info = String.valueOf(R.drawable.ic_info_outline);
List kbList = KMManager.getKeyboardsList(this);
@@ -208,4 +239,12 @@ public class GetStartedActivity extends BaseActivity {
uncheckGetStartedIfComplete();
}
}
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ if (openDefaultKeyboardPickerOnly && didAutoOpenDefaultKeyboardPicker) {
+ finish();
+ }
+ }
}
\ No newline at end of file
diff --git a/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/KMPBrowserActivity.java b/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/KMPBrowserActivity.java
index 2a2b85a32d..e6a2a597cf 100644
--- a/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/KMPBrowserActivity.java
+++ b/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/KMPBrowserActivity.java
@@ -81,7 +81,7 @@ public class KMPBrowserActivity extends BaseActivity {
}
setupEdgeToEdge(R.id.kmp_browser_layout);
- setupStatusBarColors(R.color.complementary_5, android.R.color.white);
+ setupStatusBarColors(R.color.keyman_blue, android.R.color.white);
webView = (WebView) findViewById(R.id.kmpBrowserWebView);
webView.getSettings().setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NORMAL);
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 689548a56e..a41690b254 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
@@ -96,7 +96,7 @@ public final class KeyboardSettingsActivity extends AppCompatActivity {
// Display notification to download update if available
if (kbd.hasUpdateAvailable()) {
hashMap.put(subtitleKey, context.getString(R.string.update_available, kbVersion));
- icon = String.valueOf(R.drawable.ic_cloud_download);
+ icon = String.valueOf(R.drawable.ic_cloud_download_mode);
}
hashMap.put(iconKey, icon);
infoList.add(hashMap);
@@ -105,7 +105,7 @@ public final class KeyboardSettingsActivity extends AppCompatActivity {
hashMap = new HashMap<>();
final String customHelpLink = kbd.getHelpLink();
// Check if app declared FileProvider
- icon = String.valueOf(R.drawable.ic_action_forward);
+ icon = String.valueOf(R.drawable.ic_action_forward_mode);
// 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/KeymanApplication.java b/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/KeymanApplication.java
new file mode 100644
index 0000000000..3fb3588f5a
--- /dev/null
+++ b/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/KeymanApplication.java
@@ -0,0 +1,25 @@
+/*
+ * Keyman is copyright (C) SIL Global. MIT License.
+ *
+ * Created by MengchouChey on 2026-06-10
+ * Internship Program, 9th Feb - 27th Apr 2026
+ *
+ * This file is the Application class for Keyman, which is used to set the default night mode based on user preferences when the application starts.
+ *
+ */
+package com.tavultesoft.kmapro;
+
+import android.app.Application;
+import android.content.SharedPreferences;
+
+import androidx.appcompat.app.AppCompatDelegate;
+
+public class KeymanApplication extends Application {
+ @Override
+ public void onCreate() {
+ SharedPreferences prefs = getSharedPreferences("settings", MODE_PRIVATE);
+ int mode = prefs.getInt("theme_mode", AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM);
+ AppCompatDelegate.setDefaultNightMode(mode);
+ super.onCreate();
+ }
+}
diff --git a/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/KeymanSettingsActivity.java b/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/KeymanSettingsActivity.java
deleted file mode 100644
index bda56856b2..0000000000
--- a/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/KeymanSettingsActivity.java
+++ /dev/null
@@ -1,53 +0,0 @@
-package com.tavultesoft.kmapro;
-
-import android.os.Bundle;
-
-import com.keyman.engine.BaseActivity;
-import com.keyman.engine.KMManager;
-
-import androidx.appcompat.widget.Toolbar;
-
-public class KeymanSettingsActivity extends BaseActivity {
- protected static final String installedLanguagesKey = "InstalledLanguages";
- protected static final String installKeyboardOrDictionaryKey = "InstallKeyboardOrDictionary";
- protected static final String sendCrashReport = "SendCrashReport";
- public static final String spacebarTextKey = "SpacebarText";
- public static final String hapticFeedbackKey = "HapticFeedback";
- public static final String oskWithPhysicalKeyboardKey = "ShowOSK";
-
- protected KeymanSettingsFragment innerFragment;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- setContentView(R.layout.keyman_settings_layout);
-
- setupEdgeToEdge(R.id.keyman_settings_layout);
- setupStatusBarColors(R.color.keyman_blue, android.R.color.white);
-
- Toolbar toolbar = (Toolbar) findViewById(R.id.keyman_settings_toolbar);
- toolbar.setTitleTextColor(getResources().getColor(android.R.color.white));
- toolbar.setTitle(R.string.keyman_settings);
- setSupportActionBar(toolbar);
- if (getSupportActionBar() != null) {
- getSupportActionBar().setDisplayHomeAsUpEnabled(true);
- getSupportActionBar().setDisplayShowHomeEnabled(true);
- getSupportActionBar().setDisplayShowTitleEnabled(true);
- }
-
- innerFragment = (KeymanSettingsFragment) getSupportFragmentManager().findFragmentById(R.id.keyman_settings_fragment);
-
- // For Keyman sites, disable keyboard picker task flag so keyboard picker doesn't dismiss Keyman app
- KMManager.dontCloseParentAppOnShowKeyboardPicker();
- }
-
- @Override
- public void onWindowFocusChanged(boolean hasFocus) {
- super.onWindowFocusChanged(hasFocus);
-
- if(hasFocus) {
- innerFragment.update();
- }
- }
-}
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 3aa149f477..e1819dd97f 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
@@ -44,7 +44,7 @@ public class KeymanSettingsFragment extends PreferenceFragmentCompat {
getPreferenceManager().setSharedPreferencesName(getString(R.string.kma_prefs_name));
languagesPreference = new Preference(context);
- languagesPreference.setKey(KeymanSettingsActivity.installedLanguagesKey);
+ languagesPreference.setKey(KeymanSettingsKeys.INSTALLED_LANGUAGES);
languagesPreference.setTitle(getInstalledLanguagesText());
languagesPreference.setWidgetLayoutResource(R.layout.preference_icon_layout);
Intent languagesIntent = new Intent(context, LanguagesSettingsActivity.class);
@@ -52,7 +52,7 @@ public class KeymanSettingsFragment extends PreferenceFragmentCompat {
languagesPreference.setIntent(languagesIntent);
installKeyboardOrDictionary = new Preference(context);
- installKeyboardOrDictionary.setKey(KeymanSettingsActivity.installKeyboardOrDictionaryKey);
+ installKeyboardOrDictionary.setKey(KeymanSettingsKeys.INSTALL_KEYBOARD_OR_DICTIONARY);
installKeyboardOrDictionary.setTitle(getString(R.string.install_keyboard_or_dictionary));
installKeyboardOrDictionary.setWidgetLayoutResource(R.layout.preference_add_icon_layout);
Intent installIntent = new Intent(context, KeymanSettingsInstallActivity.class);
@@ -75,7 +75,7 @@ public class KeymanSettingsFragment extends PreferenceFragmentCompat {
};
setSystemKeyboardPreference = new CheckBoxPreference(context);
- setSystemKeyboardPreference.setTitle(R.string.enable_system_keyboard);
+ setSystemKeyboardPreference.setTitle(R.string.system_keyboards);
setSystemKeyboardPreference.setSingleLineTitle(false);
setSystemKeyboardPreference.setDefaultValue(SystemIMESettings.isEnabledAsSystemKB(context));
setSystemKeyboardPreference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
@@ -120,7 +120,7 @@ public class KeymanSettingsFragment extends PreferenceFragmentCompat {
/* Spacebar Caption Preference */
spacebarTextPreference = new ListPreference(context);
- spacebarTextPreference.setKey(KeymanSettingsActivity.spacebarTextKey);
+ spacebarTextPreference.setKey(KeymanSettingsKeys.SPACEBAR_TEXT);
spacebarTextPreference.setTitle(getString(R.string.spacebar_caption)); // getString(R.string.change_display_language));
@@ -161,7 +161,7 @@ public class KeymanSettingsFragment extends PreferenceFragmentCompat {
});
SwitchPreference oskWithPhysicalKeyboardPreference = new SwitchPreference(context);
- oskWithPhysicalKeyboardPreference.setKey(KeymanSettingsActivity.oskWithPhysicalKeyboardKey);
+ oskWithPhysicalKeyboardPreference.setKey(KeymanSettingsKeys.OSK_WITH_PHYSICAL_KEYBOARD);
oskWithPhysicalKeyboardPreference.setTitle(getString(R.string.show_osk));
oskWithPhysicalKeyboardPreference.setSummary(getString(R.string.show_osk_hint));
@@ -169,7 +169,7 @@ public class KeymanSettingsFragment extends PreferenceFragmentCompat {
PreferencesManager.kma_prefs_name, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = prefs.edit();
boolean showOSK = prefs.getBoolean(
- KeymanSettingsActivity.oskWithPhysicalKeyboardKey, false);
+ KeymanSettingsKeys.OSK_WITH_PHYSICAL_KEYBOARD, false);
oskWithPhysicalKeyboardPreference.setDefaultValue(showOSK);
oskWithPhysicalKeyboardPreference.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@@ -179,14 +179,14 @@ public class KeymanSettingsFragment extends PreferenceFragmentCompat {
return false;
}
- editor.putBoolean(KeymanSettingsActivity.oskWithPhysicalKeyboardKey, (boolean)newValue);
+ editor.putBoolean(KeymanSettingsKeys.OSK_WITH_PHYSICAL_KEYBOARD, (boolean)newValue);
editor.commit();
return true;
}
});
SwitchPreference hapticFeedbackPreference = new SwitchPreference(context);
- hapticFeedbackPreference.setKey(KeymanSettingsActivity.hapticFeedbackKey);
+ hapticFeedbackPreference.setKey(KeymanSettingsKeys.HAPTIC_FEEDBACK);
hapticFeedbackPreference.setTitle(getString(R.string.haptic_feedback));
hapticFeedbackPreference.setDefaultValue(false);
hapticFeedbackPreference.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@@ -212,7 +212,7 @@ public class KeymanSettingsFragment extends PreferenceFragmentCompat {
getStartedPreference.setDefaultValue(true);
SwitchPreference sendCrashReportPreference = new SwitchPreference(context);
- sendCrashReportPreference.setKey(KeymanSettingsActivity.sendCrashReport);
+ sendCrashReportPreference.setKey(KeymanSettingsKeys.SEND_CRASH_REPORT);
sendCrashReportPreference.setTitle(getString(R.string.show_send_crash_report));
sendCrashReportPreference.setSummaryOn(getString(R.string.show_send_crash_report_on));
sendCrashReportPreference.setSummaryOff(getString(R.string.show_send_crash_report_off));
diff --git a/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/KeymanSettingsInstallActivity.java b/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/KeymanSettingsInstallActivity.java
index 3a7a9ce8a7..1775dd0267 100644
--- a/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/KeymanSettingsInstallActivity.java
+++ b/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/KeymanSettingsInstallActivity.java
@@ -21,7 +21,6 @@ import android.view.Window;
import android.widget.AdapterView;
import android.widget.ListAdapter;
import android.widget.ListView;
-import android.os.Bundle;
import android.widget.SimpleAdapter;
import android.widget.TextView;
@@ -71,13 +70,13 @@ public class KeymanSettingsInstallActivity extends BaseActivity {
final String noIcon = "0";
HashMap hashMap = new HashMap<>();
hashMap.put(titleKey, getString(R.string.install_from_keyman_dot_com));
- String icon = String.valueOf(R.drawable.ic_content_add);
+ String icon = String.valueOf(R.drawable.ic_content_add_mode);
hashMap.put(iconKey, icon);
installOptionList.add(hashMap);
hashMap = new HashMap<>();
hashMap.put(titleKey, getString(R.string.install_from_local_file));
- icon = String.valueOf(R.drawable.ic_folder_open);
+ icon = String.valueOf(R.drawable.ic_folder_open_mode);
hashMap.put(iconKey, icon);
hashMap.put(isEnabledKey, "true");
installOptionList.add(hashMap);
@@ -95,7 +94,7 @@ public class KeymanSettingsInstallActivity extends BaseActivity {
hashMap = new HashMap<>();
hashMap.put(titleKey, getString(R.string.add_languages_to_installed_keyboard));
hashMap.put(subtitleKey, getString(R.string.add_language_subtext));
- icon = String.valueOf(R.drawable.ic_content_add);
+ icon = String.valueOf(R.drawable.ic_content_add_mode);
hashMap.put(iconKey, icon);
hashMap.put(isEnabledKey, "true");
installOptionList.add(hashMap);
diff --git a/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/KeymanSettingsKeys.java b/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/KeymanSettingsKeys.java
new file mode 100644
index 0000000000..9055307b6d
--- /dev/null
+++ b/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/KeymanSettingsKeys.java
@@ -0,0 +1,22 @@
+/*
+ * Keyman is copyright (C) SIL Global. MIT License.
+ *
+ * Created by MengchouChey on 2026-06-10
+ * Internship Program, 9th Feb - 27th Apr 2026
+ *
+ * These are the keys used to replace KeymanSettingsActivity and KeymanSettingsFragment
+ *
+ */
+package com.tavultesoft.kmapro;
+
+public final class KeymanSettingsKeys {
+ private KeymanSettingsKeys() {
+ }
+
+ public static final String INSTALLED_LANGUAGES = "InstalledLanguages";
+ public static final String INSTALL_KEYBOARD_OR_DICTIONARY = "InstallKeyboardOrDictionary";
+ public static final String SEND_CRASH_REPORT = "SendCrashReport";
+ public static final String SPACEBAR_TEXT = "SpacebarText";
+ public static final String HAPTIC_FEEDBACK = "HapticFeedback";
+ public static final String OSK_WITH_PHYSICAL_KEYBOARD = "ShowOSK";
+}
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 9342a0b528..887cfb7033 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
@@ -153,7 +153,7 @@ public final class LanguageSettingsActivity extends BaseActivity {
updateActiveLexicalModel();
ImageView imageView = (ImageView) layout.findViewById(R.id.image1);
- imageView.setImageResource(R.drawable.ic_action_forward);
+ imageView.setImageResource(R.drawable.ic_action_forward_mode);
layout.setEnabled(true);
layout.setOnClickListener(new View.OnClickListener() {
@Override
@@ -333,7 +333,7 @@ public final class LanguageSettingsActivity extends BaseActivity {
}
holder.text.setText(kbd.getResourceName());
- holder.img.setImageResource(R.drawable.ic_action_forward);
+ holder.img.setImageResource(R.drawable.ic_action_forward_mode);
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 79c845fa6f..3e3c6c785f 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
@@ -205,7 +205,7 @@ public final class LanguagesSettingsActivity extends BaseActivity {
}
holder.textLang.setText(data.name);
- holder.img.setImageResource(R.drawable.ic_action_forward);
+ holder.img.setImageResource(R.drawable.ic_action_forward_mode);
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 01f38a4a66..044f21abba 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
@@ -6,6 +6,8 @@ package com.tavultesoft.kmapro;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import com.google.android.material.navigation.NavigationView;
+import android.util.DisplayMetrics;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
@@ -26,6 +28,7 @@ import com.keyman.engine.KMHelpFileActivity;
import com.keyman.engine.KMKeyboardDownloaderActivity;
import com.keyman.engine.KMManager;
import com.keyman.engine.KMManager.KeyboardType;
+import com.keyman.engine.KeyboardEventHandler;
import com.keyman.engine.KmpInstallMode;
import com.keyman.engine.KMTextView;
import com.keyman.engine.KeyboardEventHandler.OnKeyboardDownloadEventListener;
@@ -36,7 +39,6 @@ import com.keyman.engine.cloud.impl.CloudLexicalModelMetaDataDownloadCallback;
import com.keyman.engine.data.CloudRepository;
import com.keyman.engine.data.Dataset;
import com.keyman.engine.data.Keyboard;
-import com.keyman.engine.data.LexicalModel;
import com.keyman.engine.util.FileUtils;
import com.keyman.engine.util.DownloadFileUtils;
import com.keyman.android.DownloadIntentService;
@@ -47,17 +49,15 @@ import com.keyman.engine.util.WebViewUtils;
import com.keyman.engine.util.WebViewUtils.EngineWebViewVersionStatus;
import com.keyman.engine.util.WebViewUtils.SystemWebViewStatus;
-import android.Manifest;
import android.app.ProgressDialog;
import android.content.pm.PackageManager;
+import android.graphics.Color;
import android.net.Uri;
import android.net.Uri.Builder;
import android.os.Build;
import android.os.Bundle;
-import android.os.Environment;
import android.os.Handler;
import android.os.ParcelFileDescriptor;
-import android.os.Parcelable;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
@@ -65,7 +65,10 @@ import androidx.activity.result.ActivityResult;
import androidx.activity.result.ActivityResultCallback;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
+import androidx.appcompat.app.ActionBarDrawerToggle;
+import androidx.appcompat.app.AppCompatDelegate;
import androidx.appcompat.widget.Toolbar;
+import androidx.appcompat.widget.SwitchCompat;
import androidx.appcompat.app.AlertDialog;
import androidx.constraintlayout.widget.ConstraintLayout;
import android.content.ClipData;
@@ -73,11 +76,10 @@ import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
-import android.content.pm.ResolveInfo;
+
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.Canvas;
-import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.Rect;
@@ -86,30 +88,61 @@ import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import androidx.annotation.NonNull;
import androidx.core.app.ActivityCompat;
+import androidx.core.view.MenuCompat;
+import androidx.core.view.WindowCompat;
+import androidx.core.view.WindowInsetsControllerCompat;
+import androidx.drawerlayout.widget.DrawerLayout;
+
+import androidx.recyclerview.widget.RecyclerView;
+
+import androidx.core.content.ContextCompat;
import android.provider.Settings;
import android.text.Html;
+import android.text.SpannableString;
+import android.text.Spanned;
+import android.text.style.ForegroundColorSpan;
+import android.text.style.RelativeSizeSpan;
import android.util.Log;
import android.util.TypedValue;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
+import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
+import android.view.Window;
+import android.view.WindowManager;
import android.widget.Button;
+import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.SeekBar;
import android.widget.TextView;
import android.widget.Toast;
+import android.view.Gravity;
+import android.view.inputmethod.InputMethodManager;
+import androidx.drawerlayout.widget.DrawerLayout;
+import androidx.appcompat.app.ActionBarDrawerToggle;
+import com.google.android.material.navigation.NavigationView;
+import androidx.core.content.ContextCompat;
+import androidx.core.view.GravityCompat;
+import android.view.MenuItem;
+import androidx.appcompat.widget.AppCompatCheckBox;
+
import io.sentry.android.core.SentryAndroid;
+import io.sentry.Sentry;
public class MainActivity extends BaseActivity implements OnKeyboardEventListener, OnKeyboardDownloadEventListener,
ActivityCompat.OnRequestPermissionsResultCallback {
public static Context context;
+ private DrawerLayout drawerLayout;
+ private ActionBarDrawerToggle drawerToggle;
+ private NavigationView navigationView;
+
// Fields used for installing kmp packages
public static final int PERMISSION_REQUEST_STORAGE = 0;
public static final int READ_REQUEST_CODE = 42;
@@ -118,6 +151,10 @@ public class MainActivity extends BaseActivity implements OnKeyboardEventListene
private ConstraintLayout constraintLayout;
private KMTextView textView;
+ private View textPopupToggleButton;
+ private View keyboardToolbarContainer;
+ private boolean isKeyboardToolbarExpanded = false;
+ private boolean suppressOutsideCloseForCurrentTouch = false;
private final int minTextSize = 16;
private final int maxTextSize = 72;
private int textSize = minTextSize;
@@ -126,17 +163,31 @@ public class MainActivity extends BaseActivity implements OnKeyboardEventListene
private static final String userTextSizeKey = "UserTextSize";
private Toolbar toolbar;
private Menu menu;
+ private String lastCurrentKeyboardDrawerTitle = null;
+ private String lastKnownKeyboardId = null;
+ private static final boolean DEBUG_CURRENT_KEYBOARD_LOGS = false;
+ private static final boolean SHOW_CURRENT_KEYBOARD_ERROR_TOAST = false;
private static Dataset repo;
private boolean didExecuteParser = false;
DownloadResultReceiver resultReceiver;
private static ProgressDialog progressDialog;
+ private static final String PREFS_NAME = "settings";
+ private static final String KEY_THEME_MODE = "theme_mode";
@Override
protected void onCreate(Bundle savedInstanceState) {
- setTheme(R.style.AppTheme);
+ SharedPreferences theme_prefs = getSharedPreferences(PREFS_NAME, MODE_PRIVATE);
+ int savedMode = theme_prefs.getInt(KEY_THEME_MODE, AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM);
+ AppCompatDelegate.setDefaultNightMode(savedMode);
+ getDelegate().applyDayNight();
super.onCreate(savedInstanceState);
+ KMManager.initialize(getApplicationContext(), KeyboardType.KEYBOARD_TYPE_INAPP);
+ setContentView(R.layout.activity_main);
+ constraintLayout = (ConstraintLayout) findViewById(R.id.constraintLayout);
+
+ setupEdgeToEdge(R.id.constraintLayout);
context = this;
checkSendCrashReport();
@@ -154,24 +205,43 @@ public class MainActivity extends BaseActivity implements OnKeyboardEventListene
KMManager.setDebugMode(true);
}
- // Verify WebView installed and enabled before attempting to initialize KMManager
- KMManager.initialize(getApplicationContext(), KeyboardType.KEYBOARD_TYPE_INAPP);
KMManager.executeResourceUpdate(this);
DefaultLanguageResource.install(context);
SharedPreferences prefs = getSharedPreferences(getString(R.string.kma_prefs_name), Context.MODE_PRIVATE);
- KMManager.SpacebarText spacebarText = KMManager.SpacebarText.fromString(prefs.getString(KeymanSettingsActivity.spacebarTextKey, KMManager.SpacebarText.LANGUAGE_KEYBOARD.toString()));
+ KMManager.SpacebarText spacebarText = KMManager.SpacebarText.fromString(prefs.getString(KeymanSettingsKeys.SPACEBAR_TEXT, KMManager.SpacebarText.LANGUAGE_KEYBOARD.toString()));
KMManager.setSpacebarText(spacebarText);
checkHapticFeedback();
- setContentView(R.layout.activity_main);
+ NavigationView navView = findViewById(R.id.nav_view);
+ DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
+ int width = (int) (displayMetrics.widthPixels * 0.80);
+ navView.getLayoutParams().width = width;
+ navView.requestLayout();
- constraintLayout = (ConstraintLayout)findViewById(R.id.constraintLayout);
- setupEdgeToEdge(R.id.constraintLayout);
- setupStatusBarColors(android.R.color.white, R.color.neutral_2);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ Window window = getWindow();
+ window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
+ window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
+
+ window.setStatusBarColor(ContextCompat.getColor(this, R.color.toolbarColor));
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ int flags = window.getDecorView().getSystemUiVisibility();
+
+ boolean isDarkMode = isDarkMode();
+
+ if (!isDarkMode) {
+ flags |= View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
+ } else {
+ flags &= ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
+ }
+ window.getDecorView().setSystemUiVisibility(flags);
+ }
+ }
toolbar = (Toolbar) findViewById(R.id.titlebar);
setSupportActionBar(toolbar);
@@ -180,14 +250,83 @@ public class MainActivity extends BaseActivity implements OnKeyboardEventListene
getSupportActionBar().setDisplayShowHomeEnabled(true);
getSupportActionBar().setLogo(R.drawable.keyman_logo);
getSupportActionBar().setDisplayShowTitleEnabled(false);
+ // For Darkmode - once switched to dark mode, the logo becomes white
+ getSupportActionBar().setLogo(R.drawable.keyman_logo_mode);
getSupportActionBar().setBackgroundDrawable(getActionBarDrawable(this));
+ drawerLayout = findViewById(R.id.drawer_layout);
+ drawerLayout.setScrimColor(Color.parseColor("#80000000"));
+ drawerToggle = new ActionBarDrawerToggle(this, drawerLayout, R.string.drawer_open, R.string.drawer_close);
+ drawerLayout.addDrawerListener(drawerToggle);
+ drawerToggle.syncState();
+
+ navigationView = findViewById(R.id.nav_view);
+ navigationView.setItemMaxLines(4);
+ MenuCompat.setGroupDividerEnabled(navigationView.getMenu(), true);
+ navigationView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {
+ @Override
+ public boolean onNavigationItemSelected(@NonNull MenuItem item) {
+ int id = item.getItemId();
+ if (id == R.id.nav_toggle_show_osk || id == R.id.nav_toggle_send_crash_report) {
+ toggleDrawerSwitch(navigationView, id);
+ return true;
+ }
+
+ drawerLayout.closeDrawers();
+ return handleDrawerNavigationItemSelected(id);
+ }
+ });
+ updateCurrentKeyboardDrawerItemTitle(navigationView);
+ updateInstalledLanguagesDrawerTitle(navigationView);
+ initializeDrawerItemSubtitles(navigationView);
+ initializeDrawerToggleOptions(navigationView);
+
+ drawerLayout.addDrawerListener(new DrawerLayout.SimpleDrawerListener() {
+
+ @Override
+ public void onDrawerSlide(View drawerView, float slideOffset) {
+ KMManager.hideSystemKeyboard();
+ textView.dismissKeyboard();
+ }
+
+ @Override
+ public void onDrawerOpened(View drawerView) {
+ if (navigationView != null) {
+ updateCurrentKeyboardDrawerItemTitle(navigationView);
+ updateInstalledLanguagesDrawerTitle(navigationView);
+ initializeDrawerItemSubtitles(navigationView);
+ }
+ }
+
+ @Override
+ public void onDrawerClosed(View drawerView) {
+ textView.callOnClick();
+ if (navigationView != null) {
+ View menuView = navigationView.getChildAt(0);
+ if (menuView instanceof RecyclerView) {
+ ((RecyclerView) menuView).scrollToPosition(0);
+ }
+ }
+ }
+ });
+
+ drawerToggle.getDrawerArrowDrawable().setColor(Color.BLACK);
+ drawerToggle.getDrawerArrowDrawable().setBarThickness(10f);
+ drawerToggle.getDrawerArrowDrawable().setGapSize(10f);
+
textView = (KMTextView) findViewById(R.id.kmTextView);
textView.setText(prefs.getString(userTextKey, ""));
textSize = prefs.getInt(userTextSizeKey, minTextSize);
textView.setTextSize((float) textSize);
textView.setSelection(textView.getText().length());
+ initializeKeyboardToolbar();
+
+ textView.post(() -> {
+ KMManager.onConfigurationChanged(this.getResources().getConfiguration());
+ KMManager.getKeyboardHeight(this);
+ });
+
// Check WebView enabled and Chrome version
SystemWebViewStatus webViewStatus = WebViewUtils.getSystemWebViewStatus(context);
EngineWebViewVersionStatus chromeStatus = KMManager.getEngineWebViewVersionStatus();
@@ -197,6 +336,56 @@ public class MainActivity extends BaseActivity implements OnKeyboardEventListene
checkGetStarted();
}
+ private void openThemeDialog() {
+ String[] options = {
+ getString(R.string.theme_dark_mode),
+ getString(R.string.theme_light_mode),
+ getString(R.string.theme_system_default)
+ };
+
+ SharedPreferences themePrefs = getSharedPreferences(PREFS_NAME, MODE_PRIVATE);
+ int currentMode = themePrefs.getInt(KEY_THEME_MODE, AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM);
+
+ int checkedItem;
+ if (currentMode == AppCompatDelegate.MODE_NIGHT_YES)
+ checkedItem = 0;
+ else if (currentMode == AppCompatDelegate.MODE_NIGHT_NO)
+ checkedItem = 1;
+ else
+ checkedItem = 2;
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setTitle(getString(R.string.theme_choose));
+
+ builder.setSingleChoiceItems(options, checkedItem, (dialog, which) -> {
+ int modeToApply;
+ switch (which) {
+ case 0:
+ modeToApply = AppCompatDelegate.MODE_NIGHT_YES;
+ break;
+ case 1:
+ modeToApply = AppCompatDelegate.MODE_NIGHT_NO;
+ break;
+ default:
+ modeToApply = AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM;
+ break;
+ }
+ // Save the choice
+ themePrefs.edit().putInt(KEY_THEME_MODE, modeToApply).apply();
+
+ // Apply immediately
+ AppCompatDelegate.setDefaultNightMode(modeToApply);
+ getDelegate().applyDayNight(); // ensures current activity updates
+ if (navigationView != null) {
+ updateThemeDrawerSubtitle(navigationView);
+ }
+
+ dialog.dismiss();
+ });
+ builder.setNegativeButton(android.R.string.cancel, (dialog, which) -> dialog.dismiss());
+ builder.show();
+ }
+
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
@Override
public void onActivityResult(int requestCode, int resultCode, Intent returnIntent) {
@@ -248,6 +437,12 @@ public class MainActivity extends BaseActivity implements OnKeyboardEventListene
protected void onResume() {
super.onResume();
+ if (navigationView != null) {
+ updateCurrentKeyboardDrawerItemTitle(navigationView);
+ updateInstalledLanguagesDrawerTitle(navigationView);
+ initializeDrawerItemSubtitles(navigationView);
+ }
+
if (textView != null) {
// Reset inAppPredictionsSuspendedForSensitiveInput flag
KMManager.setPredictionsSuspended(textView.getInputType(), KeyboardType.KEYBOARD_TYPE_INAPP);
@@ -366,31 +561,24 @@ public class MainActivity extends BaseActivity implements OnKeyboardEventListene
lastOrientation = newConfig.orientation;
}
- @SuppressLint("RestrictedApi")
- @Override
- public boolean onPrepareOptionsMenu(final Menu menu) {
- this.menu = menu;
- final MenuItem _overflowMenuItem = menu.findItem(R.id.action_overflow);
- if (_overflowMenuItem != null) {
- MenuItem updateKeyboards = this.menu.findItem(R.id.action_update_keyboards);
- updateUpdateCountIndicator(updateKeyboards,
- KMManager.getUpdateTool().getOpenUpdateCount(), true);
- }
- return super.onPrepareOptionsMenu(menu);
- }
-
private void updateUpdateCountIndicator(int anUpdateCount) {
if (menu == null) {
return;
}
- final MenuItem _overflowMenuItem = menu.findItem(R.id.action_overflow);
- if (_overflowMenuItem != null) {
- updateUpdateCountIndicator(_overflowMenuItem, anUpdateCount, false);
+ // 1. Update the Top Action Bar Menu
+ if (menu != null) {
+ final MenuItem toolbarUpdate = menu.findItem(R.id.action_update_keyboards);
+ if (toolbarUpdate != null) {
+ toolbarUpdate.setVisible(anUpdateCount > 0);
+ }
}
- final MenuItem _keyboardupdate = menu.findItem(R.id.action_update_keyboards);
- if (_keyboardupdate != null && anUpdateCount > 0) {
- _keyboardupdate.setVisible(true);
+ // 2. Update the Drawer/Sidebar Navigation Menu
+ if (navigationView != null) {
+ final MenuItem drawerUpdate = navigationView.getMenu().findItem(R.id.action_update_keyboards);
+ if (drawerUpdate != null) {
+ drawerUpdate.setVisible(anUpdateCount > 0);
+ }
}
}
@@ -432,8 +620,8 @@ public class MainActivity extends BaseActivity implements OnKeyboardEventListene
public void propertyChange(PropertyChangeEvent evt) {
if(!evt.getPropertyName().equals("updateCount"))
return;
- updateUpdateCountIndicator(
- evt.getNewValue()==null?0:(Integer) evt.getNewValue());
+ int count = KMManager.getUpdateTool().getOpenUpdateCount();
+ updateUpdateCountIndicator(count);
}
}
);
@@ -470,11 +658,13 @@ public class MainActivity extends BaseActivity implements OnKeyboardEventListene
KMManager.getUpdateTool().executeOpenUpdates();
// Dismiss icon - we intentionally hide the icon, even if the user cancels the update
updateUpdateCountIndicator(0);
- final MenuItem _keyboardupdate = menu.findItem(R.id.action_update_keyboards);
- if (_keyboardupdate != null && _keyboardupdate.isVisible()) {
- _keyboardupdate.setVisible(false);
+ return true;
+ } else if (item.getItemId() == R.id.action_sidebar) {
+ if (drawerLayout.isDrawerOpen(GravityCompat.END)) {
+ drawerLayout.closeDrawer(GravityCompat.END);
+ } else {
+ drawerLayout.openDrawer(GravityCompat.END);
}
-
return true;
} else {
return super.onOptionsItemSelected(item);
@@ -485,13 +675,51 @@ public class MainActivity extends BaseActivity implements OnKeyboardEventListene
public boolean onKeyUp(int keycode, KeyEvent e) {
switch (keycode) {
case KeyEvent.KEYCODE_MENU:
- menu.performIdentifierAction(R.id.action_overflow, Menu.FLAG_PERFORM_NO_CLOSE);
+ if (drawerLayout.isDrawerOpen(GravityCompat.END)) {
+ drawerLayout.closeDrawer(GravityCompat.END);
+ } else {
+ drawerLayout.openDrawer(GravityCompat.END);
+ }
return true;
}
return super.onKeyUp(keycode, e);
}
+ @Override
+ public boolean dispatchTouchEvent(MotionEvent ev) {
+ int action = ev.getActionMasked();
+ // Collapse the keyboard toolbar when the user taps outside of it
+ if (action == MotionEvent.ACTION_DOWN && isKeyboardToolbarExpanded && !suppressOutsideCloseForCurrentTouch) {
+ boolean touchOnToggle = isTouchInsideView(ev, textPopupToggleButton);
+ if (touchOnToggle) {
+ return super.dispatchTouchEvent(ev);
+ }
+
+ boolean touchOnToolbar = isTouchInsideView(ev, keyboardToolbarContainer);
+ if (!touchOnToolbar && !touchOnToggle) {
+ setKeyboardToolbarExpanded(false, true);
+ }
+ } else if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
+ suppressOutsideCloseForCurrentTouch = false;
+ }
+ return super.dispatchTouchEvent(ev);
+ }
+
+ private boolean isTouchInsideView(MotionEvent ev, View view) {
+ if (view == null || view.getVisibility() != View.VISIBLE) {
+ return false;
+ }
+
+ Rect bounds = new Rect();
+ view.getGlobalVisibleRect(bounds);
+ return bounds.contains((int) ev.getRawX(), (int) ev.getRawY());
+ }
+ private boolean isDarkMode() {
+ return (getResources().getConfiguration().uiMode &
+ Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES;
+ }
+
@Override
public void onKeyboardLoaded(KeyboardType keyboardType) {
// Do nothing
@@ -499,25 +727,137 @@ public class MainActivity extends BaseActivity implements OnKeyboardEventListene
@Override
public void onKeyboardChanged(String newKeyboard) {
- textView.setTypeface(KMManager.getKeyboardTextFontTypeface(this));
+ if (newKeyboard != null && !newKeyboard.isEmpty()) {
+ lastKnownKeyboardId = newKeyboard;
+ textView.setTypeface(KMManager.getKeyboardTextFontTypeface(this));
+ }
}
@Override
public void onKeyboardShown() {
// Refresh banner theme
- BannerController.setHTMLBanner(this, KeyboardType.KEYBOARD_TYPE_INAPP);
+ boolean isDarkMode = isDarkMode();
+ BannerController.setHTMLBanner(this, KeyboardType.KEYBOARD_TYPE_INAPP, isDarkMode);
resizeTextView(true);
+ updateKeyboardToolbarPosition();
+ if (textPopupToggleButton != null) {
+ textPopupToggleButton.setVisibility(View.VISIBLE);
+ }
}
@Override
public void onKeyboardDismissed() {
resizeTextView(false);
+ setKeyboardToolbarExpanded(false, false);
+ if (textPopupToggleButton != null) {
+ textPopupToggleButton.setVisibility(View.GONE);
+ }
+ }
+
+ private void initializeKeyboardToolbar() {
+ textPopupToggleButton = findViewById(R.id.keyboardToolbarToggleButton);
+ keyboardToolbarContainer = findViewById(R.id.keyboardToolbarContainer);
+
+ if (textPopupToggleButton == null || keyboardToolbarContainer == null) {
+ reportKeyboardToolbarLayoutIssue("Keyboard toolbar layout is missing required views.");
+ return;
+ }
+
+ setKeyboardToolbarExpanded(false, false);
+ textPopupToggleButton.setOnTouchListener((v, event) -> {
+ int action = event.getActionMasked();
+ if (action == MotionEvent.ACTION_DOWN) {
+ suppressOutsideCloseForCurrentTouch = true;
+ } else if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
+ suppressOutsideCloseForCurrentTouch = false;
+ }
+ return false;
+ });
+ textPopupToggleButton.setOnClickListener(v -> setKeyboardToolbarExpanded(!isKeyboardToolbarExpanded, true));
+
+ View shareButton = findViewById(R.id.keyboardToolbarShareButton);
+ if (shareButton != null) {
+ shareButton.setOnClickListener(v -> {
+ showShareDialog();
+ setKeyboardToolbarExpanded(false, true);
+ });
+ } else {
+ reportKeyboardToolbarLayoutIssue("Keyboard toolbar share button is missing.");
+ }
+
+ View textSizeButton = findViewById(R.id.keyboardToolbarTextSizeButton);
+ if (textSizeButton != null) {
+ textSizeButton.setOnClickListener(v -> {
+ showTextSizeDialog();
+ setKeyboardToolbarExpanded(false, true);
+ });
+ } else {
+ reportKeyboardToolbarLayoutIssue("Keyboard toolbar text size button is missing.");
+ }
+
+ View clearButton = findViewById(R.id.keyboardToolbarClearButton);
+ if (clearButton != null) {
+ clearButton.setOnClickListener(v -> {
+ showClearTextDialog();
+ setKeyboardToolbarExpanded(false, true);
+ });
+ } else {
+ reportKeyboardToolbarLayoutIssue("Keyboard toolbar clear button is missing.");
+ }
+ }
+
+ private void reportKeyboardToolbarLayoutIssue(String message) {
+ KMLog.LogError(TAG, message);
+ if (Sentry.isEnabled()) {
+ Sentry.captureMessage(message);
+ }
+ }
+
+ private void setKeyboardToolbarExpanded(boolean expanded, boolean animateRotation) {
+ if (keyboardToolbarContainer == null || textPopupToggleButton == null) {
+ return;
+ }
+
+ isKeyboardToolbarExpanded = expanded;
+ keyboardToolbarContainer.setVisibility(expanded ? View.VISIBLE : View.GONE);
+
+ float targetRotation = expanded ? 0f : 180f;
+ if (animateRotation) {
+ textPopupToggleButton.animate().rotation(targetRotation).setDuration(180).start();
+ } else {
+ textPopupToggleButton.setRotation(targetRotation);
+ }
+ }
+
+ private void updateKeyboardToolbarPosition() {
+ int keyboardTopOffset = KMManager.getKeyboardHeight(this) + KMManager.getBannerHeight(this)
+ + getResources().getDimensionPixelSize(R.dimen.keyboard_toolbar_keyboard_gap);
+ updateBottomMargin(textPopupToggleButton, keyboardTopOffset);
+ updateBottomMargin(keyboardToolbarContainer,
+ getResources().getDimensionPixelSize(R.dimen.keyboard_toolbar_container_bottom_margin));
+ }
+
+ private void updateBottomMargin(View view, int bottomMargin) {
+ if (view == null) {
+ return;
+ }
+
+ ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
+ if (!(layoutParams instanceof ViewGroup.MarginLayoutParams)) {
+ return;
+ }
+
+ ViewGroup.MarginLayoutParams marginLayoutParams = (ViewGroup.MarginLayoutParams) layoutParams;
+ if (marginLayoutParams.bottomMargin != bottomMargin) {
+ marginLayoutParams.bottomMargin = bottomMargin;
+ view.setLayoutParams(marginLayoutParams);
+ }
}
public void downloadKMP(String packageId, String bcp47, KmpInstallMode installMode) {
Uri downloadUri = bcp47 == null ?
- Uri.parse(KMString.format("https://keyman.com/go/package/download/%s", new Object[]{packageId})) :
- Uri.parse(KMString.format("https://keyman.com/go/package/download/%s?bcp47=%s", new Object[]{packageId, bcp47}));
+ Uri.parse(KMString.format("https://keyman.com/go/package/download/%s", new Object[] { packageId })) :
+ Uri.parse(KMString.format("https://keyman.com/go/package/download/%s?bcp47=%s", new Object[] { packageId, bcp47 }));
downloadKMP(downloadUri, installMode);
}
@@ -672,7 +1012,7 @@ public class MainActivity extends BaseActivity implements OnKeyboardEventListene
LayoutInflater inflater = LayoutInflater.from(MainActivity.this);
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.setIcon(R.drawable.ic_action_textsize_mode);
dialogBuilder.setTitle(getTextSizeString());
dialogBuilder.setView(textSizeController);
dialogBuilder.setPositiveButton(getString(R.string.label_ok), new DialogInterface.OnClickListener() {
@@ -684,6 +1024,7 @@ public class MainActivity extends BaseActivity implements OnKeyboardEventListene
final AlertDialog dialog = dialogBuilder.create();
dialog.show();
+ dialog.getWindow().setBackgroundDrawableResource(R.color.dialogBackground);
final SeekBar seekBar = (SeekBar) dialog.findViewById(R.id.seekBar);
seekBar.setProgress(textSize - minTextSize);
@@ -741,7 +1082,7 @@ public class MainActivity extends BaseActivity implements OnKeyboardEventListene
private void showClearTextDialog() {
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(MainActivity.this);
- dialogBuilder.setIcon(R.drawable.ic_light_action_trash);
+ dialogBuilder.setIcon(R.drawable.ic_action_trash_mode);
dialogBuilder.setTitle(getString(R.string.action_clear_text));
dialogBuilder.setMessage(getString(R.string.all_text_will_be_cleared));
dialogBuilder.setPositiveButton(getString(R.string.label_ok), new DialogInterface.OnClickListener() {
@@ -758,7 +1099,9 @@ public class MainActivity extends BaseActivity implements OnKeyboardEventListene
}
});
- dialogBuilder.show();
+ final AlertDialog dialog = dialogBuilder.create();
+ dialog.show();
+ dialog.getWindow().setBackgroundDrawableResource(R.color.dialogBackground);
}
private void checkGetStarted() {
@@ -783,13 +1126,13 @@ public class MainActivity extends BaseActivity implements OnKeyboardEventListene
private void checkSendCrashReport() {
SharedPreferences prefs = getSharedPreferences(getString(R.string.kma_prefs_name), Context.MODE_PRIVATE);
- boolean maySendCrashReport = prefs.getBoolean(KeymanSettingsActivity.sendCrashReport, true);
+ boolean maySendCrashReport = prefs.getBoolean(KeymanSettingsKeys.SEND_CRASH_REPORT, true);
KMManager.setMaySendCrashReport(maySendCrashReport);
}
private void checkHapticFeedback() {
SharedPreferences prefs = getSharedPreferences(getString(R.string.kma_prefs_name), Context.MODE_PRIVATE);
- boolean mayHaveHapticFeedback = prefs.getBoolean(KeymanSettingsActivity.hapticFeedbackKey, false);
+ boolean mayHaveHapticFeedback = prefs.getBoolean(KeymanSettingsKeys.HAPTIC_FEEDBACK, false);
KMManager.setHapticFeedback(mayHaveHapticFeedback);
}
@@ -966,8 +1309,393 @@ public class MainActivity extends BaseActivity implements OnKeyboardEventListene
}
private void showSettings() {
- Intent settingsIntent = new Intent(this, KeymanSettingsActivity.class);
- startActivity(settingsIntent);
+ if (drawerLayout != null) {
+ drawerLayout.openDrawer(GravityCompat.END);
+ }
+ }
+
+ private boolean handleDrawerNavigationItemSelected(int id) {
+ if (id == R.id.system_keyboards) {
+ startActivity(new Intent(Settings.ACTION_INPUT_METHOD_SETTINGS));
+ } else if (id == R.id.action_update_keyboards) {
+ KMManager.getUpdateTool().executeOpenUpdates();
+ updateUpdateCountIndicator(0);
+ } else if (id == R.id.action_settings) {
+ showSettings();
+ } else if (id == R.id.nav_installed_languages) {
+ Intent intent = new Intent(context, LanguagesSettingsActivity.class);
+ intent.putExtra(KMManager.KMKey_DisplayKeyboardSwitcher, false);
+ startActivity(intent);
+ } else if (id == R.id.nav_install_keyboard) {
+ startActivity(new Intent(context, KeymanSettingsInstallActivity.class));
+ } else if (id == R.id.nav_display_language) {
+ startActivity(new Intent(context, KeymanSettingsLocalizeActivity.class));
+ } else if (id == R.id.action_get_started) {
+ startActivity(new Intent(context, GetStartedActivity.class));
+ } else if (id == R.id.nav_keyboard_height) {
+ startActivity(new Intent(context, AdjustKeyboardHeightActivity.class));
+ } else if (id == R.id.nav_longpress_delay) {
+ startActivity(new Intent(context, AdjustLongpressDelayActivity.class));
+ } else if (id == R.id.nav_spacebar_caption) {
+ showSpacebarCaptionDialog();
+ } else if (id == R.id.nav_help) {
+ startActivity(new Intent(context, InfoActivity.class));
+ } else if (id == R.id.nav_palette) {
+ openThemeDialog();
+ } else if (id == R.id.nav_about_current_keyboard) {
+ showCurrentKeyboardSettings();
+ }
+ return true;
+ }
+
+ private void showSpacebarCaptionDialog() {
+ CharSequence[] entries = {
+ getString(R.string.spacebar_caption_language),
+ getString(R.string.spacebar_caption_keyboard),
+ getString(R.string.spacebar_caption_language_keyboard),
+ getString(R.string.spacebar_caption_blank)
+ };
+
+ KMManager.SpacebarText[] values = {
+ KMManager.SpacebarText.LANGUAGE,
+ KMManager.SpacebarText.KEYBOARD,
+ KMManager.SpacebarText.LANGUAGE_KEYBOARD,
+ KMManager.SpacebarText.BLANK
+ };
+
+ KMManager.SpacebarText current = KMManager.getSpacebarText();
+ int currentIndex = Arrays.asList(values).indexOf(current);
+
+ new AlertDialog.Builder(context)
+ .setTitle(getString(R.string.spacebar_caption))
+ .setSingleChoiceItems(entries, currentIndex, (dialog, which) -> {
+ KMManager.setSpacebarText(values[which]);
+
+ SharedPreferences prefs = getSharedPreferences(
+ getString(R.string.kma_prefs_name), Context.MODE_PRIVATE);
+ prefs.edit()
+ .putString(KeymanSettingsKeys.SPACEBAR_TEXT, values[which].toString())
+ .apply();
+
+ dialog.dismiss();
+ })
+ .setNegativeButton(getString(R.string.label_cancel), null)
+ .show();
+ }
+
+ private void initializeDrawerToggleOptions(NavigationView navigationView) {
+ SharedPreferences prefs = getSharedPreferences(getString(R.string.kma_prefs_name), Context.MODE_PRIVATE);
+
+ bindDrawerSwitch(navigationView, R.id.nav_toggle_show_osk,
+ prefs.getBoolean(KeymanSettingsKeys.OSK_WITH_PHYSICAL_KEYBOARD, false),
+ new SwitchChangeHandler() {
+ @Override
+ public void onChanged(boolean isChecked) {
+ SharedPreferences.Editor editor = prefs.edit();
+ editor.putBoolean(KeymanSettingsKeys.OSK_WITH_PHYSICAL_KEYBOARD, isChecked);
+ editor.apply();
+ }
+ });
+
+ bindDrawerSwitch(navigationView, R.id.nav_toggle_send_crash_report,
+ prefs.getBoolean(KeymanSettingsKeys.SEND_CRASH_REPORT, true),
+ new SwitchChangeHandler() {
+ @Override
+ public void onChanged(boolean isChecked) {
+ SharedPreferences.Editor editor = prefs.edit();
+ editor.putBoolean(KeymanSettingsKeys.SEND_CRASH_REPORT, isChecked);
+ editor.apply();
+ KMManager.setMaySendCrashReport(isChecked);
+ }
+ });
+ }
+
+ private void initializeDrawerItemSubtitles(NavigationView navigationView) {
+ setDrawerItemSubtitle(navigationView, R.id.nav_spacebar_caption,
+ getString(R.string.drawer_subtitle_spacebar_caption));
+ setDrawerItemSubtitle(navigationView, R.id.nav_toggle_show_osk,
+ getString(R.string.drawer_subtitle_show_osk));
+ setDrawerItemSubtitle(navigationView, R.id.nav_toggle_send_crash_report,
+ getString(R.string.drawer_subtitle_send_crash_report));
+ setDrawerItemSubtitle(navigationView, R.id.nav_about_current_keyboard,
+ getString(R.string.drawer_subtitle_about_current_keyboard));
+ updateThemeDrawerSubtitle(navigationView);
+ updateCurrentKeyboardDrawerSubtitle(navigationView);
+ }
+
+ private void updateCurrentKeyboardDrawerItemTitle(NavigationView navigationView) {
+ MenuItem menuItem = navigationView.getMenu().findItem(R.id.nav_about_current_keyboard);
+ if (menuItem == null) {
+ return;
+ }
+
+ String newTitle = getString(R.string.drawer_about_current_keyboard);
+
+ if (!newTitle.equals(lastCurrentKeyboardDrawerTitle)) {
+ menuItem.setTitle(newTitle);
+ lastCurrentKeyboardDrawerTitle = newTitle;
+ }
+ }
+
+ private void updateCurrentKeyboardDrawerSubtitle(NavigationView navigationView) {
+ setDrawerItemSubtitle(navigationView, R.id.nav_about_current_keyboard, getCurrentKeyboardDisplayName());
+ }
+
+ private void updateThemeDrawerSubtitle(NavigationView navigationView) {
+ setDrawerItemSubtitle(
+ navigationView,
+ R.id.nav_palette,
+ getString(R.string.drawer_subtitle_theme, getThemeModeLabel())
+ );
+ }
+
+ private void updateInstalledLanguagesDrawerTitle(NavigationView navigationView) {
+ MenuItem menuItem = navigationView.getMenu().findItem(R.id.nav_installed_languages);
+ if (menuItem == null) {
+ return;
+ }
+
+ menuItem.setTitle(getInstalledLanguagesText());
+ }
+
+ private String getInstalledLanguagesText() {
+ Dataset installedDataset = KMManager.getInstalledDataset(context);
+ if (installedDataset == null) {
+ return getResources().getQuantityString(R.plurals.installed_languages, 0, 0);
+ }
+
+ int langCount = 0;
+ for (Dataset.LanguageDataset language : installedDataset.asList()) {
+ if (language.keyboards.size() > 0) {
+ langCount++;
+ }
+ }
+ return getResources().getQuantityString(R.plurals.installed_languages, langCount, langCount);
+ }
+
+ private String getCurrentKeyboardDisplayName() {
+ Keyboard currentKeyboard = getBestAvailableKeyboard();
+ if (currentKeyboard != null && currentKeyboard.getKeyboardName() != null
+ && !currentKeyboard.getKeyboardName().isEmpty()) {
+ return currentKeyboard.getKeyboardName();
+ }
+ return getString(R.string.drawer_about_no_active_keyboard);
+ }
+
+ private String getThemeModeLabel() {
+ SharedPreferences themePrefs = getSharedPreferences(PREFS_NAME, MODE_PRIVATE);
+ int currentMode = themePrefs.getInt(KEY_THEME_MODE, AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM);
+ if (currentMode == AppCompatDelegate.MODE_NIGHT_YES) {
+ return getString(R.string.theme_dark_mode);
+ }
+ if (currentMode == AppCompatDelegate.MODE_NIGHT_NO) {
+ return getString(R.string.theme_light_mode);
+ }
+ return getString(R.string.theme_system_default);
+ }
+
+ private void showCurrentKeyboardSettings() {
+ Keyboard currentKeyboard = getBestAvailableKeyboard();
+ if (currentKeyboard == null) {
+ if (BuildConfig.DEBUG && DEBUG_CURRENT_KEYBOARD_LOGS) {
+ KMLog.LogInfo(TAG, "Current keyboard unavailable; not opening KeyboardSettingsActivity.");
+ }
+ if (SHOW_CURRENT_KEYBOARD_ERROR_TOAST) {
+ Toast.makeText(this, getString(R.string.drawer_about_no_active_keyboard), Toast.LENGTH_SHORT).show();
+ }
+ return;
+ }
+
+ Intent intent = new Intent(this, KeyboardSettingsActivity.class);
+ Bundle bundle = new Bundle();
+ bundle.putSerializable(KMManager.KMKey_Keyboard, currentKeyboard);
+ intent.putExtras(bundle);
+ startActivity(intent);
+ }
+
+ private Keyboard getBestAvailableKeyboard() {
+ List keyboards = KMManager.getKeyboardsList(this);
+ if (keyboards == null || keyboards.isEmpty()) {
+ if (BuildConfig.DEBUG && DEBUG_CURRENT_KEYBOARD_LOGS) {
+ KMLog.LogInfo(TAG, "No installed keyboards available.");
+ }
+ return null;
+ }
+
+ if (keyboards.size() == 1) {
+ return keyboards.get(0);
+ }
+
+ if (lastKnownKeyboardId != null && !lastKnownKeyboardId.isEmpty()) {
+ for (Keyboard keyboard : keyboards) {
+ String keyboardId = keyboard.getKeyboardID();
+ if (keyboardId != null && (keyboardId.equals(lastKnownKeyboardId)
+ || lastKnownKeyboardId.contains(keyboardId)
+ || keyboardId.contains(lastKnownKeyboardId))) {
+ return keyboard;
+ }
+ }
+ }
+
+ if (BuildConfig.DEBUG && DEBUG_CURRENT_KEYBOARD_LOGS) {
+ KMLog.LogInfo(TAG, "Current keyboard id unavailable; falling back to first installed keyboard.");
+ }
+
+ return keyboards.get(0);
+ }
+
+ private void setDrawerItemSubtitle(NavigationView navigationView, int menuItemId, String subtitle) {
+ MenuItem menuItem = navigationView.getMenu().findItem(menuItemId);
+ if (menuItem == null || subtitle == null || subtitle.isEmpty()) {
+ return;
+ }
+
+ CharSequence currentTitle = menuItem.getTitle();
+ if (currentTitle == null || currentTitle.length() == 0) {
+ return;
+ }
+
+ String baseTitle = currentTitle.toString();
+ int newlineIndex = baseTitle.indexOf('\n');
+ if (newlineIndex >= 0) {
+ baseTitle = baseTitle.substring(0, newlineIndex);
+ }
+
+ SpannableString fullTitle = new SpannableString(baseTitle + "\n" + subtitle);
+ int subtitleStart = baseTitle.length() + 1;
+ fullTitle.setSpan(new RelativeSizeSpan(0.85f), subtitleStart, fullTitle.length(),
+ Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+
+ TypedValue typedValue = new TypedValue();
+ getTheme().resolveAttribute(android.R.attr.textColorSecondary, typedValue, true);
+ int subtitleColor = typedValue.resourceId != 0
+ ? ContextCompat.getColor(this, typedValue.resourceId)
+ : typedValue.data;
+ fullTitle.setSpan(new ForegroundColorSpan(subtitleColor), subtitleStart, fullTitle.length(),
+ Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+
+ menuItem.setTitle(fullTitle);
+ }
+
+ private void bindDrawerSwitch(NavigationView navigationView, int menuItemId, boolean defaultValue,
+ final SwitchChangeHandler switchChangeHandler) {
+ MenuItem menuItem = navigationView.getMenu().findItem(menuItemId);
+ if (menuItem == null) {
+ return;
+ }
+
+ menuItem.setCheckable(false);
+
+ View actionView = menuItem.getActionView();
+ if (actionView == null) {
+ return;
+ }
+
+ final SwitchCompat switchView;
+ if (actionView instanceof SwitchCompat) {
+ switchView = (SwitchCompat) actionView;
+ } else {
+ switchView = actionView.findViewById(R.id.nav_switch);
+ }
+
+ if (switchView == null) {
+ return;
+ }
+
+ switchView.setClickable(false);
+ switchView.setFocusable(false);
+ switchView.setLongClickable(false);
+
+ switchView.setChecked(defaultValue);
+ switchView.setOnCheckedChangeListener((buttonView, isChecked) -> switchChangeHandler.onChanged(isChecked));
+ }
+
+ private void toggleDrawerSwitch(NavigationView navigationView, int menuItemId) {
+ MenuItem menuItem = navigationView.getMenu().findItem(menuItemId);
+ if (menuItem == null) {
+ return;
+ }
+
+ View actionView = menuItem.getActionView();
+ if (actionView == null) {
+ return;
+ }
+
+ if (actionView instanceof SwitchCompat) {
+ ((SwitchCompat) actionView).toggle();
+ return;
+ }
+
+ SwitchCompat switchView = actionView.findViewById(R.id.nav_switch);
+ if (switchView != null) {
+ switchView.toggle();
+ }
+ }
+
+ private void bindDrawerCheckboxAction(NavigationView navigationView, int menuItemId,
+ final Runnable onActivate) {
+ MenuItem menuItem = navigationView.getMenu().findItem(menuItemId);
+ if (menuItem == null) {
+ return;
+ }
+
+ menuItem.setCheckable(false);
+
+ View actionView = menuItem.getActionView();
+ if (actionView == null) {
+ return;
+ }
+
+ final AppCompatCheckBox checkBox;
+ if (actionView instanceof AppCompatCheckBox) {
+ checkBox = (AppCompatCheckBox) actionView;
+ } else {
+ checkBox = actionView.findViewById(R.id.nav_checkbox);
+ }
+
+ if (checkBox == null) {
+ return;
+ }
+
+ checkBox.setClickable(true);
+ checkBox.setFocusable(false);
+ checkBox.setOnClickListener(v -> {
+ if (onActivate != null) {
+ onActivate.run();
+ }
+ });
+ actionView.setOnClickListener(v -> {
+ if (onActivate != null) {
+ onActivate.run();
+ }
+ });
+ }
+
+ private void setDrawerCheckboxState(NavigationView navigationView, int menuItemId, boolean isChecked) {
+ MenuItem menuItem = navigationView.getMenu().findItem(menuItemId);
+ if (menuItem == null) {
+ return;
+ }
+
+ View actionView = menuItem.getActionView();
+ if (actionView == null) {
+ return;
+ }
+
+ AppCompatCheckBox checkBox;
+ if (actionView instanceof AppCompatCheckBox) {
+ checkBox = (AppCompatCheckBox) actionView;
+ } else {
+ checkBox = actionView.findViewById(R.id.nav_checkbox);
+ }
+
+ if (checkBox != null) {
+ checkBox.setChecked(isChecked);
+ }
+ }
+
+ private interface SwitchChangeHandler {
+ void onChanged(boolean isChecked);
}
/**
@@ -999,7 +1727,7 @@ public class MainActivity extends BaseActivity implements OnKeyboardEventListene
Paint p = new Paint();
p.setStyle(Paint.Style.FILL);
- p.setColor(Color.WHITE);
+ p.setColor(ContextCompat.getColor(context, R.color.toolbarColor));
canvas.drawRect(new Rect(0, 0, width, actionBarHeight), p);
p.setColor(context.getResources().getColor(R.color.keyman_orange));
diff --git a/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/SelectLanguageFragment.java b/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/SelectLanguageFragment.java
index 03d053e469..71cdfbe642 100644
--- a/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/SelectLanguageFragment.java
+++ b/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/SelectLanguageFragment.java
@@ -6,7 +6,7 @@ package com.tavultesoft.kmapro;
import android.content.Context;
import android.graphics.Typeface;
import android.os.Bundle;
-import android.os.Handler;
+
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -167,7 +167,7 @@ public final class SelectLanguageFragment extends Fragment implements BlockingSt
if (!excludeInstalledLanguages && !isInstallingPackage && KeyboardController.getInstance().keyboardExists(
k.getPackageID(), k.getKeyboardID(), k.getLanguageID())) {
// If Activity is listing an installed keyboard package, mark installed keyboards with a check
- icon = String.valueOf(R.drawable.ic_check);
+ icon = String.valueOf(R.drawable.ic_check_mode);
enable = "false";
} else {
// Update title
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 0b6a3ac9a7..c9bb86faca 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
@@ -83,7 +83,7 @@ public final class SelectPackageActivity extends BaseActivity {
HashMap hashMap = new HashMap<>();
hashMap.put(titleKey, keyboardName);
hashMap.put(subtitleKey, pkgID);
- String icon = String.valueOf(R.drawable.ic_action_forward);
+ String icon = String.valueOf(R.drawable.ic_action_forward_mode);
hashMap.put(iconKey, icon);
list.add(hashMap);
}
diff --git a/android/KMAPro/kMAPro/src/main/res/drawable-hdpi/keyman_ic.png b/android/KMAPro/kMAPro/src/main/res/drawable-hdpi/keyman_ic.png
new file mode 100644
index 0000000000..cdd82a9eaf
Binary files /dev/null and b/android/KMAPro/kMAPro/src/main/res/drawable-hdpi/keyman_ic.png differ
diff --git a/android/KMAPro/kMAPro/src/main/res/drawable-hdpi/keyman_text.png b/android/KMAPro/kMAPro/src/main/res/drawable-hdpi/keyman_text.png
new file mode 100644
index 0000000000..48b555f559
Binary files /dev/null and b/android/KMAPro/kMAPro/src/main/res/drawable-hdpi/keyman_text.png differ
diff --git a/android/KMAPro/kMAPro/src/main/res/drawable/bg_keyboard_toolbar_container.xml b/android/KMAPro/kMAPro/src/main/res/drawable/bg_keyboard_toolbar_container.xml
new file mode 100644
index 0000000000..1dfbf0c345
--- /dev/null
+++ b/android/KMAPro/kMAPro/src/main/res/drawable/bg_keyboard_toolbar_container.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/android/KMAPro/kMAPro/src/main/res/drawable/bg_keyboard_toolbar_toggle.xml b/android/KMAPro/kMAPro/src/main/res/drawable/bg_keyboard_toolbar_toggle.xml
new file mode 100644
index 0000000000..08f8c630b4
--- /dev/null
+++ b/android/KMAPro/kMAPro/src/main/res/drawable/bg_keyboard_toolbar_toggle.xml
@@ -0,0 +1,5 @@
+
+
+
+
\ No newline at end of file
diff --git a/android/KMAPro/kMAPro/src/main/res/drawable/branded_logo.xml b/android/KMAPro/kMAPro/src/main/res/drawable/branded_logo.xml
index 2f5d1eca2b..23720f7c57 100644
--- a/android/KMAPro/kMAPro/src/main/res/drawable/branded_logo.xml
+++ b/android/KMAPro/kMAPro/src/main/res/drawable/branded_logo.xml
@@ -3,7 +3,7 @@
android:opacity="opaque">
-
+
-
-
+
\ No newline at end of file
diff --git a/android/KMAPro/kMAPro/src/main/res/drawable/branded_logo_dark_mode.xml b/android/KMAPro/kMAPro/src/main/res/drawable/branded_logo_dark_mode.xml
new file mode 100644
index 0000000000..5cb427161f
--- /dev/null
+++ b/android/KMAPro/kMAPro/src/main/res/drawable/branded_logo_dark_mode.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+ -
+
+
+
+ -
+
+
+
+
+
+
\ No newline at end of file
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
index c3085bec5e..7f67a424a8 100644
--- a/android/KMAPro/kMAPro/src/main/res/drawable/ic_action_decrement.xml
+++ b/android/KMAPro/kMAPro/src/main/res/drawable/ic_action_decrement.xml
@@ -1,5 +1,5 @@
diff --git a/android/KMAPro/kMAPro/src/main/res/drawable/ic_action_forward_mode.xml b/android/KMAPro/kMAPro/src/main/res/drawable/ic_action_forward_mode.xml
new file mode 100644
index 0000000000..e50c72fd48
--- /dev/null
+++ b/android/KMAPro/kMAPro/src/main/res/drawable/ic_action_forward_mode.xml
@@ -0,0 +1,4 @@
+
+
diff --git a/android/KMAPro/kMAPro/src/main/res/drawable/ic_action_get_started_mode.xml b/android/KMAPro/kMAPro/src/main/res/drawable/ic_action_get_started_mode.xml
new file mode 100644
index 0000000000..945b28f2f7
--- /dev/null
+++ b/android/KMAPro/kMAPro/src/main/res/drawable/ic_action_get_started_mode.xml
@@ -0,0 +1,4 @@
+
+
\ No newline at end of file
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
index ea188f818f..04d80ab840 100644
--- a/android/KMAPro/kMAPro/src/main/res/drawable/ic_action_increment.xml
+++ b/android/KMAPro/kMAPro/src/main/res/drawable/ic_action_increment.xml
@@ -1,5 +1,5 @@
diff --git a/android/KMAPro/kMAPro/src/main/res/drawable/ic_action_info_mode.xml b/android/KMAPro/kMAPro/src/main/res/drawable/ic_action_info_mode.xml
new file mode 100644
index 0000000000..cde024c5df
--- /dev/null
+++ b/android/KMAPro/kMAPro/src/main/res/drawable/ic_action_info_mode.xml
@@ -0,0 +1,4 @@
+
+
\ No newline at end of file
diff --git a/android/KMAPro/kMAPro/src/main/res/drawable/ic_action_overflow_mode.png b/android/KMAPro/kMAPro/src/main/res/drawable/ic_action_overflow_mode.png
new file mode 100644
index 0000000000..b78c32e85f
Binary files /dev/null and b/android/KMAPro/kMAPro/src/main/res/drawable/ic_action_overflow_mode.png differ
diff --git a/android/KMAPro/kMAPro/src/main/res/drawable/ic_action_overflow_mode_small.xml b/android/KMAPro/kMAPro/src/main/res/drawable/ic_action_overflow_mode_small.xml
new file mode 100644
index 0000000000..b9581eec2c
--- /dev/null
+++ b/android/KMAPro/kMAPro/src/main/res/drawable/ic_action_overflow_mode_small.xml
@@ -0,0 +1,7 @@
+
+
\ No newline at end of file
diff --git a/android/KMAPro/kMAPro/src/main/res/drawable/ic_action_overflow_mode_tinted.xml b/android/KMAPro/kMAPro/src/main/res/drawable/ic_action_overflow_mode_tinted.xml
new file mode 100644
index 0000000000..7df2a0f1a5
--- /dev/null
+++ b/android/KMAPro/kMAPro/src/main/res/drawable/ic_action_overflow_mode_tinted.xml
@@ -0,0 +1,4 @@
+
+
diff --git a/android/KMAPro/kMAPro/src/main/res/drawable/ic_action_share_mode.xml b/android/KMAPro/kMAPro/src/main/res/drawable/ic_action_share_mode.xml
new file mode 100644
index 0000000000..094bccaf5f
--- /dev/null
+++ b/android/KMAPro/kMAPro/src/main/res/drawable/ic_action_share_mode.xml
@@ -0,0 +1,4 @@
+
+
\ No newline at end of file
diff --git a/android/KMAPro/kMAPro/src/main/res/drawable/ic_action_textsize_mode.xml b/android/KMAPro/kMAPro/src/main/res/drawable/ic_action_textsize_mode.xml
new file mode 100644
index 0000000000..c9bce1b644
--- /dev/null
+++ b/android/KMAPro/kMAPro/src/main/res/drawable/ic_action_textsize_mode.xml
@@ -0,0 +1,4 @@
+
+
\ No newline at end of file
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
index 80dc29e33f..c185c3bd11 100644
--- a/android/KMAPro/kMAPro/src/main/res/drawable/ic_action_timelapse.xml
+++ b/android/KMAPro/kMAPro/src/main/res/drawable/ic_action_timelapse.xml
@@ -2,4 +2,4 @@
-
+
\ No newline at end of file
diff --git a/android/KMAPro/kMAPro/src/main/res/drawable/ic_action_trash_mode.xml b/android/KMAPro/kMAPro/src/main/res/drawable/ic_action_trash_mode.xml
new file mode 100644
index 0000000000..7ce6e6f02e
--- /dev/null
+++ b/android/KMAPro/kMAPro/src/main/res/drawable/ic_action_trash_mode.xml
@@ -0,0 +1,4 @@
+
+
\ No newline at end of file
diff --git a/android/KMAPro/kMAPro/src/main/res/drawable/ic_arrow_dropdown.xml b/android/KMAPro/kMAPro/src/main/res/drawable/ic_arrow_dropdown.xml
new file mode 100644
index 0000000000..38b4ddbe58
--- /dev/null
+++ b/android/KMAPro/kMAPro/src/main/res/drawable/ic_arrow_dropdown.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/KMAPro/kMAPro/src/main/res/drawable/ic_check_mode.xml b/android/KMAPro/kMAPro/src/main/res/drawable/ic_check_mode.xml
new file mode 100644
index 0000000000..45126f7b91
--- /dev/null
+++ b/android/KMAPro/kMAPro/src/main/res/drawable/ic_check_mode.xml
@@ -0,0 +1,4 @@
+
+
\ No newline at end of file
diff --git a/android/KMAPro/kMAPro/src/main/res/drawable/ic_checkbox.xml b/android/KMAPro/kMAPro/src/main/res/drawable/ic_checkbox.xml
new file mode 100644
index 0000000000..bd790eec59
--- /dev/null
+++ b/android/KMAPro/kMAPro/src/main/res/drawable/ic_checkbox.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/android/KMAPro/kMAPro/src/main/res/drawable/ic_cloud_download_mode.xml b/android/KMAPro/kMAPro/src/main/res/drawable/ic_cloud_download_mode.xml
new file mode 100644
index 0000000000..dd95be3da2
--- /dev/null
+++ b/android/KMAPro/kMAPro/src/main/res/drawable/ic_cloud_download_mode.xml
@@ -0,0 +1,6 @@
+
+
+
+
\ No newline at end of file
diff --git a/android/KMAPro/kMAPro/src/main/res/drawable/ic_content_add_mode.xml b/android/KMAPro/kMAPro/src/main/res/drawable/ic_content_add_mode.xml
new file mode 100644
index 0000000000..9f14c6d6db
--- /dev/null
+++ b/android/KMAPro/kMAPro/src/main/res/drawable/ic_content_add_mode.xml
@@ -0,0 +1,4 @@
+
+
\ No newline at end of file
diff --git a/android/KMAPro/kMAPro/src/main/res/drawable/ic_folder_open_mode.xml b/android/KMAPro/kMAPro/src/main/res/drawable/ic_folder_open_mode.xml
new file mode 100644
index 0000000000..00896d44cb
--- /dev/null
+++ b/android/KMAPro/kMAPro/src/main/res/drawable/ic_folder_open_mode.xml
@@ -0,0 +1,4 @@
+
+
\ No newline at end of file
diff --git a/android/KMAPro/kMAPro/src/main/res/drawable/ic_keyboard_default.png b/android/KMAPro/kMAPro/src/main/res/drawable/ic_keyboard_default.png
new file mode 100644
index 0000000000..1f08b2e9e3
Binary files /dev/null and b/android/KMAPro/kMAPro/src/main/res/drawable/ic_keyboard_default.png differ
diff --git a/android/KMAPro/kMAPro/src/main/res/drawable/ic_keyboard_on_screen.png b/android/KMAPro/kMAPro/src/main/res/drawable/ic_keyboard_on_screen.png
new file mode 100644
index 0000000000..c8707e943d
Binary files /dev/null and b/android/KMAPro/kMAPro/src/main/res/drawable/ic_keyboard_on_screen.png differ
diff --git a/android/KMAPro/kMAPro/src/main/res/drawable/ic_keyboard_physical.png b/android/KMAPro/kMAPro/src/main/res/drawable/ic_keyboard_physical.png
new file mode 100644
index 0000000000..4ef7ec9fe1
Binary files /dev/null and b/android/KMAPro/kMAPro/src/main/res/drawable/ic_keyboard_physical.png differ
diff --git a/android/KMAPro/kMAPro/src/main/res/drawable/ic_more_vert_24.xml b/android/KMAPro/kMAPro/src/main/res/drawable/ic_more_vert_24.xml
new file mode 100644
index 0000000000..f79e4779fa
--- /dev/null
+++ b/android/KMAPro/kMAPro/src/main/res/drawable/ic_more_vert_24.xml
@@ -0,0 +1,10 @@
+
+
+
+
\ No newline at end of file
diff --git a/android/KMAPro/kMAPro/src/main/res/drawable/ic_palette.xml b/android/KMAPro/kMAPro/src/main/res/drawable/ic_palette.xml
new file mode 100644
index 0000000000..f701027ccf
--- /dev/null
+++ b/android/KMAPro/kMAPro/src/main/res/drawable/ic_palette.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/android/KMAPro/kMAPro/src/main/res/drawable/ic_rocket.png b/android/KMAPro/kMAPro/src/main/res/drawable/ic_rocket.png
new file mode 100644
index 0000000000..695eaac970
Binary files /dev/null and b/android/KMAPro/kMAPro/src/main/res/drawable/ic_rocket.png differ
diff --git a/android/KMAPro/kMAPro/src/main/res/drawable/ic_settings_mode.xml b/android/KMAPro/kMAPro/src/main/res/drawable/ic_settings_mode.xml
new file mode 100644
index 0000000000..63dacb5436
--- /dev/null
+++ b/android/KMAPro/kMAPro/src/main/res/drawable/ic_settings_mode.xml
@@ -0,0 +1,4 @@
+
+
\ No newline at end of file
diff --git a/android/KMAPro/kMAPro/src/main/res/drawable/ic_warning.xml b/android/KMAPro/kMAPro/src/main/res/drawable/ic_warning.xml
new file mode 100644
index 0000000000..7b9ff21539
--- /dev/null
+++ b/android/KMAPro/kMAPro/src/main/res/drawable/ic_warning.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/KMAPro/kMAPro/src/main/res/drawable/keyman_logo_mode.xml b/android/KMAPro/kMAPro/src/main/res/drawable/keyman_logo_mode.xml
new file mode 100644
index 0000000000..63182c6c53
--- /dev/null
+++ b/android/KMAPro/kMAPro/src/main/res/drawable/keyman_logo_mode.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+ -
+
+
+
+
\ No newline at end of file
diff --git a/android/KMAPro/kMAPro/src/main/res/drawable/keyman_logo_splash_dark_mode.png b/android/KMAPro/kMAPro/src/main/res/drawable/keyman_logo_splash_dark_mode.png
new file mode 100644
index 0000000000..0ed8dd4e0d
Binary files /dev/null and b/android/KMAPro/kMAPro/src/main/res/drawable/keyman_logo_splash_dark_mode.png differ
diff --git a/android/KMAPro/kMAPro/src/main/res/drawable/spacebar_key_bg.xml b/android/KMAPro/kMAPro/src/main/res/drawable/spacebar_key_bg.xml
new file mode 100644
index 0000000000..0b55d9e593
--- /dev/null
+++ b/android/KMAPro/kMAPro/src/main/res/drawable/spacebar_key_bg.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/KMAPro/kMAPro/src/main/res/layout/activity_adjust_keyboard_height.xml b/android/KMAPro/kMAPro/src/main/res/layout/activity_adjust_keyboard_height.xml
index 8541398a57..4c43d82269 100644
--- a/android/KMAPro/kMAPro/src/main/res/layout/activity_adjust_keyboard_height.xml
+++ b/android/KMAPro/kMAPro/src/main/res/layout/activity_adjust_keyboard_height.xml
@@ -7,7 +7,11 @@
android:layout_height="match_parent"
android:orientation="vertical">
-
+
+ app:tint="@color/iconColor"
+ />
+ app:layout_constraintEnd_toEndOf="parent"
+ app:tint="@color/iconColor"/>
diff --git a/android/KMAPro/kMAPro/src/main/res/layout/activity_main.xml b/android/KMAPro/kMAPro/src/main/res/layout/activity_main.xml
index ffcd4a2421..7c3e004b0d 100644
--- a/android/KMAPro/kMAPro/src/main/res/layout/activity_main.xml
+++ b/android/KMAPro/kMAPro/src/main/res/layout/activity_main.xml
@@ -1,42 +1,127 @@
-
+ android:fitsSystemWindows="true">
-
-
-
+ android:layout_height="match_parent"
+ android:layout_above="@+id/KMKeyboard"
+ android:orientation="vertical"
+ android:background="@color/toolbarColor"
+ tools:context=".MainActivity"
+ android:fitsSystemWindows="true">
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ android:fadeScrollbars="false"
+ android:fitsSystemWindows="true"
+ app:itemTextAppearance="@style/NavigationDrawerItemText"
+ app:menu="@menu/nav_menu" />
-
-
-
-
+
diff --git a/android/KMAPro/kMAPro/src/main/res/layout/activity_splash_screen.xml b/android/KMAPro/kMAPro/src/main/res/layout/activity_splash_screen.xml
index c6cde0e259..128cadc6e1 100644
--- a/android/KMAPro/kMAPro/src/main/res/layout/activity_splash_screen.xml
+++ b/android/KMAPro/kMAPro/src/main/res/layout/activity_splash_screen.xml
@@ -1,19 +1,18 @@
-
-
+
\ No newline at end of file
diff --git a/android/KMAPro/kMAPro/src/main/res/layout/check_chrome_webview_layout.xml b/android/KMAPro/kMAPro/src/main/res/layout/check_chrome_webview_layout.xml
index ea5a9220d5..7ceb1bad59 100644
--- a/android/KMAPro/kMAPro/src/main/res/layout/check_chrome_webview_layout.xml
+++ b/android/KMAPro/kMAPro/src/main/res/layout/check_chrome_webview_layout.xml
@@ -3,20 +3,23 @@
android:id="@+id/checkWebViewChromeLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:background="@android:color/white"
+ android:paddingVertical="4dp"
+ android:background="?android:attr/windowBackground"
+ android:gravity="center_vertical"
android:orientation="horizontal">
diff --git a/android/KMAPro/kMAPro/src/main/res/layout/get_started_list_layout.xml b/android/KMAPro/kMAPro/src/main/res/layout/get_started_list_layout.xml
index 0965cef422..3b3d3ed344 100644
--- a/android/KMAPro/kMAPro/src/main/res/layout/get_started_list_layout.xml
+++ b/android/KMAPro/kMAPro/src/main/res/layout/get_started_list_layout.xml
@@ -11,7 +11,8 @@
+ android:layout_height="wrap_content"
+ android:padding="3dp"/>
+ style="?android:attr/actionButtonStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/checkbox_margin"
+ android:layout_centerVertical="true"
+ android:contentDescription="@string/image_view"
+ app:tint="@color/iconColor"/>
+ android:layout_marginStart="@dimen/checkbox_margin_left"
+ android:src="@drawable/ic_action_get_started"
+ app:tint="@color/iconColor"/>
+ android:src="@drawable/ic_action_close"
+ app:tint="@color/iconColor"/>
diff --git a/android/KMAPro/kMAPro/src/main/res/layout/list_row_layout1.xml b/android/KMAPro/kMAPro/src/main/res/layout/list_row_layout1.xml
new file mode 100644
index 0000000000..0724afa829
--- /dev/null
+++ b/android/KMAPro/kMAPro/src/main/res/layout/list_row_layout1.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/android/KMAPro/kMAPro/src/main/res/layout/nav_checkbox_item.xml b/android/KMAPro/kMAPro/src/main/res/layout/nav_checkbox_item.xml
new file mode 100644
index 0000000000..5ec549a98b
--- /dev/null
+++ b/android/KMAPro/kMAPro/src/main/res/layout/nav_checkbox_item.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
diff --git a/android/KMAPro/kMAPro/src/main/res/layout/nav_divider.xml b/android/KMAPro/kMAPro/src/main/res/layout/nav_divider.xml
new file mode 100644
index 0000000000..192100c926
--- /dev/null
+++ b/android/KMAPro/kMAPro/src/main/res/layout/nav_divider.xml
@@ -0,0 +1,10 @@
+
+
diff --git a/android/KMAPro/kMAPro/src/main/res/layout/nav_header.xml b/android/KMAPro/kMAPro/src/main/res/layout/nav_header.xml
new file mode 100644
index 0000000000..093647f9ad
--- /dev/null
+++ b/android/KMAPro/kMAPro/src/main/res/layout/nav_header.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/KMAPro/kMAPro/src/main/res/layout/nav_right_icon_item.xml b/android/KMAPro/kMAPro/src/main/res/layout/nav_right_icon_item.xml
new file mode 100644
index 0000000000..312b033bfd
--- /dev/null
+++ b/android/KMAPro/kMAPro/src/main/res/layout/nav_right_icon_item.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/KMAPro/kMAPro/src/main/res/layout/nav_switch_item.xml b/android/KMAPro/kMAPro/src/main/res/layout/nav_switch_item.xml
new file mode 100644
index 0000000000..db85b1f0e4
--- /dev/null
+++ b/android/KMAPro/kMAPro/src/main/res/layout/nav_switch_item.xml
@@ -0,0 +1,10 @@
+
+
diff --git a/android/KMAPro/kMAPro/src/main/res/layout/preference_add_icon_layout.xml b/android/KMAPro/kMAPro/src/main/res/layout/preference_add_icon_layout.xml
index 1a8b8af5ad..3b8a933111 100644
--- a/android/KMAPro/kMAPro/src/main/res/layout/preference_add_icon_layout.xml
+++ b/android/KMAPro/kMAPro/src/main/res/layout/preference_add_icon_layout.xml
@@ -1,5 +1,7 @@
+ android:src="@drawable/ic_content_add"
+ app:tint="@color/iconColor"/>
diff --git a/android/KMAPro/kMAPro/src/main/res/layout/preference_duration_icon_layout.xml b/android/KMAPro/kMAPro/src/main/res/layout/preference_duration_icon_layout.xml
index c8fd3a48f9..fff11ef254 100644
--- a/android/KMAPro/kMAPro/src/main/res/layout/preference_duration_icon_layout.xml
+++ b/android/KMAPro/kMAPro/src/main/res/layout/preference_duration_icon_layout.xml
@@ -1,5 +1,7 @@
+ android:src="@drawable/ic_action_timelapse"
+ app:tint="@color/iconColor"/>
diff --git a/android/KMAPro/kMAPro/src/main/res/layout/preference_file_browser_layout.xml b/android/KMAPro/kMAPro/src/main/res/layout/preference_file_browser_layout.xml
index 9483748278..e81b4fa133 100644
--- a/android/KMAPro/kMAPro/src/main/res/layout/preference_file_browser_layout.xml
+++ b/android/KMAPro/kMAPro/src/main/res/layout/preference_file_browser_layout.xml
@@ -1,5 +1,7 @@
+ android:src="@drawable/ic_folder_open"
+ app:tint="@color/iconColor"/>
diff --git a/android/KMAPro/kMAPro/src/main/res/layout/preference_height_icon_layout.xml b/android/KMAPro/kMAPro/src/main/res/layout/preference_height_icon_layout.xml
index be0e7ac155..fe2561723c 100644
--- a/android/KMAPro/kMAPro/src/main/res/layout/preference_height_icon_layout.xml
+++ b/android/KMAPro/kMAPro/src/main/res/layout/preference_height_icon_layout.xml
@@ -1,14 +1,15 @@
+ android:src="@drawable/ic_height" app:tint="@color/iconColor"/>
+ android:src="@drawable/ic_keyboard" app:tint="@color/iconColor"/>
\ No newline at end of file
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 7074c696de..880989b6a0 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
@@ -1,5 +1,6 @@
+ android:src="@drawable/ic_action_forward" app:tint="@color/iconColor"/>
diff --git a/android/KMAPro/kMAPro/src/main/res/layout/preference_translate_icon_layout.xml b/android/KMAPro/kMAPro/src/main/res/layout/preference_translate_icon_layout.xml
index d66b445010..0ad237e47f 100644
--- a/android/KMAPro/kMAPro/src/main/res/layout/preference_translate_icon_layout.xml
+++ b/android/KMAPro/kMAPro/src/main/res/layout/preference_translate_icon_layout.xml
@@ -1,5 +1,7 @@
+ android:src="@drawable/ic_translate"
+ app:tint="@color/iconColor"/>
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 cfd36374de..19a407e958 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
@@ -1,11 +1,12 @@
+ android:paddingTop="25dp">
+ android:src="@drawable/textsize_decrease"
+ app:tint="@color/iconColor"/>
+ android:src="@drawable/textsize_increase"
+ app:tint="@color/iconColor"/>
diff --git a/android/KMAPro/kMAPro/src/main/res/menu/main.xml b/android/KMAPro/kMAPro/src/main/res/menu/main.xml
index 682d98179b..6a74a945f5 100644
--- a/android/KMAPro/kMAPro/src/main/res/menu/main.xml
+++ b/android/KMAPro/kMAPro/src/main/res/menu/main.xml
@@ -4,64 +4,10 @@
xmlns:tools="http://schemas.android.com/tools"
tools:context=".MainActivity">
-
-
-
-
-
-
-
+ android:id="@+id/action_sidebar"
+ android:icon="@drawable/ic_action_overflow_mode_small"
+ android:title="@string/menu_title"
+ app:showAsAction="always" />
-
-
\ No newline at end of file
diff --git a/android/KMAPro/kMAPro/src/main/res/menu/nav_menu.xml b/android/KMAPro/kMAPro/src/main/res/menu/nav_menu.xml
new file mode 100644
index 0000000000..8c7c957f99
--- /dev/null
+++ b/android/KMAPro/kMAPro/src/main/res/menu/nav_menu.xml
@@ -0,0 +1,106 @@
+
+
diff --git a/android/KMAPro/kMAPro/src/main/res/values-b+es+419/strings.xml b/android/KMAPro/kMAPro/src/main/res/values-b+es+419/strings.xml
index 988c58f648..bd728975d2 100644
--- a/android/KMAPro/kMAPro/src/main/res/values-b+es+419/strings.xml
+++ b/android/KMAPro/kMAPro/src/main/res/values-b+es+419/strings.xml
@@ -3,8 +3,27 @@
Compartir
+ Abrir el cajón de navegación
+ Cerrar el cajón de navegación
+ Show sidebar menu
Navegador web
+
+ Teclados e idiomas
+ Preferencias
+ Ayuda
+
+ Mostrar nombre del teclado en la barra espaciadora
+ Cuando se conecta el teclado físico
+ Permitir que se envíen los informes de errores al equipo Keyman
+ Aprende cómo utilizar Keyman
+ No hay teclado activo
+ Acerca del teclado activo
+ No hay teclado activo
+ Configuración actual: %1$s
+ Aprende cómo utilizar el %1$s
+ Ayuda de %1$s
+ Ayuda de teclado activa
Tamaño del texto
@@ -19,8 +38,11 @@
Instalar actualizaciones
Versión: %1$s
-
- Keyman requiere Chrome versión 57 o posterior.
+
+
+ Keyman requiere Chrome versión 57 o posterior.
+
+ Keyman requiere Chrome versión %1$s o posterior.
Actualizar Chrome
@@ -42,6 +64,8 @@
Agrega un teclado para tu idioma
+ Teclados del sistema
+
Habilitar Keyman como teclado de sistema
Establecer teclado como teclado predeterminado
@@ -50,10 +74,13 @@
Mostrar \"%1$s\" al inicio
- Para instalar los paquetes del teclado, permite que Keyman acceda al almacenamiento externo.
+
+ Para instalar los paquetes del teclado, permite que Keyman acceda al almacenamiento externo.
- Se ha denegado la solicitud de permiso de almacenamiento. La instalación del paquete de teclado podría fallar
- Falló la solicitud de permiso de almacenamiento. Seleccione Configuración de Keyman - Instalar desde archivo local
+
+ Se ha denegado la solicitud de permiso de almacenamiento. La instalación del paquete de teclado podría fallar
+
+ Falló la solicitud de permiso de almacenamiento. Seleccione Configuración de Keyman - Instalar desde archivo local
Configuración
@@ -87,6 +114,10 @@
Idioma y teclado en la barra espaciadora
No mostrar leyenda en la barra espaciadora
+
+ Mostrar el teclado en pantalla
+
+ Cuando se conecta el teclado físico
Vibrar al escribir
@@ -96,11 +127,21 @@
Cuando está desactivado, solo se muestra cuando el texto predictivo está habilitado
- Permitir el envío de informes de fallas a través de la red
+ Informes de errores automáticos
- Cuando esté activado, se enviarán los informes de fallas
+ Permitir que se envíen los informes de errores al equipo Keyman
- Cuando esté desactivado, no se enviarán los informes de fallas
+ Los informes de error no se envían al equipo Keyman
+
+ Guía de usuario
+ Actualizar teclados
+ Idiomas instalados
+ Show "Get Started" on startup
+ Tema
+ Choose theme
+ Modo oscuro
+ Modo claro
+ Predeterminada del sistema
Instalar desde keyman.com
@@ -120,9 +161,11 @@
Ya están instalados todos los idiomas
- Arrastra el teclado para ajustar la altura. Rota el dispositivo para ajustar el modo vertical y horizontal.
+ Arrastre el teclado para ajustar la altura. Gire el dispositivo para ajustar el modo vertical y horizontal.
Restaurar valores predeterminados
+
+ %1$d%% Modo vertical | %2$d%% Modo horizontal
Tiempo de retraso: %1$.1f segundos
@@ -166,7 +209,10 @@
Metadatos inválidos/ausentes en el paquete
- El teclado requiere una versión más reciente de Keyman
+
+ El teclado requiere una versión más reciente de Keyman
No se puede iniciar el navegador web
+
+ Menú
diff --git a/android/KMAPro/kMAPro/src/main/res/values-night-v23/styles.xml b/android/KMAPro/kMAPro/src/main/res/values-night-v23/styles.xml
new file mode 100644
index 0000000000..eaeeaa087d
--- /dev/null
+++ b/android/KMAPro/kMAPro/src/main/res/values-night-v23/styles.xml
@@ -0,0 +1,6 @@
+
+
+
+
\ No newline at end of file
diff --git a/android/KMAPro/kMAPro/src/main/res/values-night/colors.xml b/android/KMAPro/kMAPro/src/main/res/values-night/colors.xml
new file mode 100644
index 0000000000..ed19414c07
--- /dev/null
+++ b/android/KMAPro/kMAPro/src/main/res/values-night/colors.xml
@@ -0,0 +1,18 @@
+
+
+ #131313
+
+ #FFFFFF
+ @color/ms_black
+ #212121
+ @color/ms_black
+
+ #929396
+
+ #353535
+ @color/ms_black
+
+ @color/ms_black
+ #FFFFFF
+
+
\ No newline at end of file
diff --git a/android/KMAPro/kMAPro/src/main/res/values-night/styles.xml b/android/KMAPro/kMAPro/src/main/res/values-night/styles.xml
new file mode 100644
index 0000000000..d13099886e
--- /dev/null
+++ b/android/KMAPro/kMAPro/src/main/res/values-night/styles.xml
@@ -0,0 +1,65 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/KMAPro/kMAPro/src/main/res/values-v23/styles.xml b/android/KMAPro/kMAPro/src/main/res/values-v23/styles.xml
new file mode 100644
index 0000000000..0217445182
--- /dev/null
+++ b/android/KMAPro/kMAPro/src/main/res/values-v23/styles.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/KMAPro/kMAPro/src/main/res/values/colors.xml b/android/KMAPro/kMAPro/src/main/res/values/colors.xml
index 2415183a56..c2e03b8373 100644
--- a/android/KMAPro/kMAPro/src/main/res/values/colors.xml
+++ b/android/KMAPro/kMAPro/src/main/res/values/colors.xml
@@ -13,17 +13,27 @@
#00735E
#0AB8A0
#79C3DA
- #000000
+ #FFFFFF
#6D6C6F
#929396
#C9CACA
#DADDDC
+
@color/analagous_5
@color/analagous_6
@color/complementary_6
+ #000
+ @color/neutral_1
+ @color/neutral_1
+ @color/neutral_2
#FFFFFFCF
+ @color/neutral_1
@color/complementary_1
+ #FFFFFF
+ @color/keyman_blue
+ #000000
+
diff --git a/android/KMAPro/kMAPro/src/main/res/values/dimens.xml b/android/KMAPro/kMAPro/src/main/res/values/dimens.xml
index 60e89def04..d690612371 100644
--- a/android/KMAPro/kMAPro/src/main/res/values/dimens.xml
+++ b/android/KMAPro/kMAPro/src/main/res/values/dimens.xml
@@ -18,10 +18,12 @@
350dp
8dp
8dp
- 15dp
+ 19dp
10dp
32dp
16dp
0dp
24sp
+ 12dp
+ 8dp
diff --git a/android/KMAPro/kMAPro/src/main/res/values/strings.xml b/android/KMAPro/kMAPro/src/main/res/values/strings.xml
index 0423bdc015..4e16f9f7ef 100644
--- a/android/KMAPro/kMAPro/src/main/res/values/strings.xml
+++ b/android/KMAPro/kMAPro/src/main/res/values/strings.xml
@@ -6,9 +6,34 @@
Share
+ Open navigation drawer
+ Close navigation drawer
+ Show sidebar menu
+
Web Browser
+
+ Keyboards and Languages
+ Preferences
+ Help
+
+
+
+ Show keyboard name on spacebar
+ When physical keyboard connected
+
+ Allow error reports to be sent to Keyman team
+
+ Learn how to use Keyman
+ No active keyboard
+ About active keyboard
+ No active keyboard
+ Current setting: %1$s
+ Learn how to use the %1$s
+ %1$s help
+ Active keyboard help
+
Text Size
@@ -73,6 +98,9 @@
Add a keyboard for your language
+
+ System keyboards
+
Enable Keyman as system-wide keyboard
@@ -102,12 +130,12 @@
- - Installed languages
+ - Installed languages (%1$d)
- Installed languages (%1$d)
- Install Keyboard or Dictionary
+ Install keyboard or dictionary
Display language
@@ -147,7 +175,7 @@
Do not show caption on spacebar
- Show On-Screen Keyboard
+ Show on-screen keyboard
When physical keyboard is connected
@@ -165,13 +193,24 @@
When off, only shown when predictive text is enabled
- Allow sending crash reports over network
+ Automatic error reporting
- When on, crash reports will be sent
+ Allow error reports to be sent to Keyman team
- When off, crash reports won\'t be sent
+ Error reports are not sent to Keyman team
+
+
+ User guide
+ Update keyboards
+ Installed languages
+ Show "Get Started" on startup
+ Theme
+ Choose theme
+ Dark mode
+ Light mode
+ System default
Install from keyman.com
@@ -278,4 +317,7 @@
Unable to launch web browser
+
+
+ Menu
diff --git a/android/KMAPro/kMAPro/src/main/res/values/styles.xml b/android/KMAPro/kMAPro/src/main/res/values/styles.xml
index ae386e0262..9366cd1441 100644
--- a/android/KMAPro/kMAPro/src/main/res/values/styles.xml
+++ b/android/KMAPro/kMAPro/src/main/res/values/styles.xml
@@ -6,7 +6,7 @@
Base application theme, dependent on API level. This theme is replaced
by AppTheme.Base from res/values-vXX/styles.xml on newer devices.
-->
-
-
+
+
+
+
@@ -36,6 +55,17 @@
- @android:color/white
- @android:color/black
- @android:color/black
+ - @color/toolbarColor
+
+ - true
+
-
+
+
+
\ No newline at end of file
diff --git a/android/KMEA/app/src/main/res/values-b+es+419/strings.xml b/android/KMEA/app/src/main/res/values-b+es+419/strings.xml
index 06413a82e2..d39720de8a 100644
--- a/android/KMEA/app/src/main/res/values-b+es+419/strings.xml
+++ b/android/KMEA/app/src/main/res/values-b+es+419/strings.xml
@@ -61,7 +61,8 @@
%1$s (Actualización disponible)
- Actualizaciones disponibles para el teclado %1$s: %2$s
+
+ Actualizaciones disponibles para el teclado %1$s: %2$s
Actualizaciones disponibles para el diccionario %1$s: %2$s
@@ -73,24 +74,30 @@
[nuevo] %1$s
- Escanea este código par cargar este\nteclado en otro dispositivo
+
+ Escanea este código par cargar este\nteclado en otro dispositivo
Bienvenido a %1$s
- Se necesita la librería FileProvider para ver el archivo de ayuda: %1$s
-
- Error fatal del teclado con %1$s:%2$s para el idioma %3$s. Cargando teclado predeterminado.
+
+ Se necesita la librería FileProvider para ver el archivo de ayuda: %1$s
+
+
+ Keyman encountered an error and will restart
- Keyman requiere que WebView esté instalado.\"
+
+ Keyman requiere que WebView esté instalado.\"
- Keyman requiere que WebView esté habilitado.\"
+
+Keyman requiere que WebView esté habilitado.\"
Instalar WebView
Habilitar WebView
Buscando diccionario asociado para descargar
- No se pudo conectar al servidor de Keyman para buscar un diccionario asociado para descargar
+
+ No se pudo conectar al servidor de Keyman para buscar un diccionario asociado para descargar
¿Quieres descargar la última versión de este diccionario?
@@ -100,7 +107,8 @@
La actualización del catálogo inició en segundo plano
- El catálogo aún se está descargando; inténtalo de nuevo en un momento.
+
+ El catálogo aún se está descargando; inténtalo de nuevo en un momento.
Buscando recurso
diff --git a/android/Tests/KeyboardHarness/readme.md b/android/Tests/KeyboardHarness/readme.md
index 898d992638..6d63e5d883 100644
--- a/android/Tests/KeyboardHarness/readme.md
+++ b/android/Tests/KeyboardHarness/readme.md
@@ -13,15 +13,9 @@ and consists of these engineering keyboards:
test9469.kmp is another engineering keyboard to show non-printing characters on the OSK.
### Compiling From Command Line
-1. Launch a command prompt to the `android/` folder
-2. Compile KMEA. This will build and copy `keyman-engine.aar` to the Samples and Test projects
- ```
- cd KMEA
- ./build.sh
- ```
+1. Launch a command prompt to the `android/Tests/KeyboardHarness` folder
3. Compile KeyboardHarness
```
- cd ../Tests/KeyboardHarness
./build.sh
```
diff --git a/common/include/km_vkey.h b/common/include/km_vkey.h
new file mode 100644
index 0000000000..78096be75b
--- /dev/null
+++ b/common/include/km_vkey.h
@@ -0,0 +1,155 @@
+/*
+ * Keyman is copyright (C) SIL International. MIT License.
+ * Create Date: 2 Jul 2026
+ * Author: Sabine Schmitt
+ * VKCodes for keyman
+ */
+
+#pragma once
+
+#define VK_LBUTTON 0x01
+#define VK_RBUTTON 0x02
+#define VK_CANCEL 0x03
+#define VK_MBUTTON 0x04
+
+#define VK_BACKSPACE 0x08
+#define VK_BACK 0x08
+#define VK_TAB 0x09
+
+#define VK_CLEAR 0x0C
+#define VK_RETURN 0x0D
+#define VK_ENTER 0x0D
+
+#define VK_SHIFT 0x10
+#define VK_CONTROL 0x11
+#define VK_MENU 0x12
+#define VK_PAUSE 0x13
+#define VK_CAPITAL 0x14
+#define VK_KANA 0x15
+#define VK_HANGEUL 0x15
+#define VK_HANGUL 0x15
+#define VK_IME_ON 0x16
+#define VK_JUNJA 0x17
+#define VK_FINAL 0x18
+#define VK_HANJA 0x19
+#define VK_KANJI 0x19
+#define VK_IME_OFF 0x1A
+#define VK_ESCAPE 0x1B
+#define VK_CONVERT 0x1C
+#define VK_NONCONVERT 0x1D
+#define VK_ACCEPT 0x1E
+#define VK_MODECHANGE 0x1F
+#define VK_SPACE 0x20
+#define VK_PRIOR 0x21
+#define VK_NEXT 0x22
+#define VK_END 0x23
+#define VK_HOME 0x24
+#define VK_LEFT 0x25
+#define VK_UP 0x26
+#define VK_RIGHT 0x27
+#define VK_DOWN 0x28
+#define VK_SELECT 0x29
+#define VK_PRINT 0x2A
+#define VK_EXECUTE 0x2B
+#define VK_SNAPSHOT 0x2C
+#define VK_INSERT 0x2D
+#define VK_DELETE 0x2E
+
+#define VK_KEY_0 0x30
+#define VK_KEY_1 0x31
+#define VK_KEY_2 0x32
+#define VK_KEY_3 0x33
+#define VK_KEY_4 0x34
+#define VK_KEY_5 0x35
+#define VK_KEY_6 0x36
+#define VK_KEY_7 0x37
+#define VK_KEY_8 0x38
+#define VK_KEY_9 0x39
+
+#define VK_KEY_A 0x41
+#define VK_KEY_B 0x42
+#define VK_KEY_C 0x43
+#define VK_KEY_D 0x44
+#define VK_KEY_E 0x45
+#define VK_KEY_F 0x46
+#define VK_KEY_G 0x47
+#define VK_KEY_H 0x48
+#define VK_KEY_I 0x49
+#define VK_KEY_J 0x4A
+#define VK_KEY_K 0x4B
+#define VK_KEY_L 0x4C
+#define VK_KEY_M 0x4D
+#define VK_KEY_N 0x4E
+#define VK_KEY_O 0x4F
+#define VK_KEY_P 0x50
+#define VK_KEY_Q 0x51
+#define VK_KEY_R 0x52
+#define VK_KEY_S 0x53
+#define VK_KEY_T 0x54
+#define VK_KEY_U 0x55
+#define VK_KEY_V 0x56
+#define VK_KEY_W 0x57
+#define VK_KEY_X 0x58
+#define VK_KEY_Y 0x59
+#define VK_KEY_Z 0x5A
+
+#define VK_NUMPAD0 0x60
+#define VK_NUMPAD1 0x61
+#define VK_NUMPAD2 0x62
+#define VK_NUMPAD3 0x63
+#define VK_NUMPAD4 0x64
+#define VK_NUMPAD5 0x65
+#define VK_NUMPAD6 0x66
+#define VK_NUMPAD7 0x67
+#define VK_NUMPAD8 0x68
+#define VK_NUMPAD9 0x69
+#define VK_NUMPAD_MULTIPLY 0x6A
+#define VK_NUMPAD_ADD 0x6B
+
+#define VK_NUMPAD_SUBTRACT 0x6D
+#define VK_NUMPAD_DECIMAL 0x6E
+#define VK_DECIMAL 0x6E
+#define VK_NUMPAD_DIVIDE 0x6F
+#define VK_DIVIDE 0x6F
+
+#define VK_NUMLOCK 0x90
+
+#define VK_LSHIFT 0xA0
+#define VK_RSHIFT 0xA1
+#define VK_LCONTROL 0xA2
+#define VK_RCONTROL 0xA3
+#define VK_LMENU 0xA4
+#define VK_RMENU 0xA5
+
+#define VK_OEM_1 0xBA
+#define VK_COLON 0xBA
+#define VK_SEMICOLON 0xBA
+#define VK_OEM_PLUS 0xBB
+#define VK_EQUAL 0xBB
+#define VK_OEM_COMMA 0xBC
+#define VK_COMMA 0xBC
+#define VK_OEM_MINUS 0xBD
+#define VK_HYPHEN 0xBD
+#define VK_MINUS 0xBD
+#define VK_OEM_PERIOD 0xBE
+#define VK_PERIOD 0xBE
+#define VK_OEM_2 0xBF
+#define VK_SLASH 0xBF
+#define VK_OEM_3 0xC0
+#define VK_ACCENT 0xC0
+#define VK_GRAVE 0xC0
+#define VK_LEFT_BRACKET 0xDB
+#define VK_LBRKT 0xDB
+#define VK_OEM_4 0xDB
+#define VK_BACKSLASH 0xDC
+#define VK_BKSLASH 0xDC
+#define VK_OEM_5 0xDC
+#define VK_RIGHT_BRACKET 0xDD
+#define VK_RBRKT 0xDD
+#define VK_OEM_6 0xDD
+#define VK_QUOTE 0xDE
+#define VK_OEM_7 0xDE
+#define VK_xDF 0xDF
+#define VK_OEM_8 0xDF
+#define VK_OEM_102 0xE2
+#define VK_OEM_CLEAR 0xFE
diff --git a/common/web/sentry-manager/src/index.ts b/common/web/sentry-manager/src/index.ts
index 92085e7869..a87694e4e0 100644
--- a/common/web/sentry-manager/src/index.ts
+++ b/common/web/sentry-manager/src/index.ts
@@ -42,8 +42,19 @@ export class KeymanSentryManager {
switch(location.protocol) {
case 'http:':
return 'http://' + location.host + '/' + filename;
+ case 'https:':
+ // Keyman for Android uses https://appassets.androidplatform.net
+ // to point to local files. See #16146.
+ if(this.keymanPlatform == 'android') {
+ return 'file:///' + filename;
+ } else {
+ return 'https://' + location.host + '/' + filename;
+ }
case 'file:':
return 'file:///' + filename;
+ // Used within the iOS Keyman engine. See #16136.
+ case 'keyman-engine:':
+ return 'file:///' + filename;
default:
return null;
}
diff --git a/core/src/action.cpp b/core/src/action.cpp
index ca6b0b30d8..f76634ea0c 100644
--- a/core/src/action.cpp
+++ b/core/src/action.cpp
@@ -103,6 +103,10 @@ bool km::core::action_item_list_to_actions_object(
break;
case KM_CORE_IT_PERSIST_OPT:
{
+ assert(action_items->option != nullptr);
+ if(action_items->option == nullptr) {
+ return false;
+ }
// TODO: lowpri: replace existing item if already present in options vector?
km::core::option opt(static_cast(action_items->option->scope),
action_items->option->key,
diff --git a/core/src/kmx/kmx_base.h b/core/src/kmx/kmx_base.h
index e798ff56b7..30b9ab5c75 100644
--- a/core/src/kmx/kmx_base.h
+++ b/core/src/kmx/kmx_base.h
@@ -1,6 +1,7 @@
#pragma once
#include "keyman_core.h"
+#include "km_vkey.h"
#if defined(_WIN32) || defined(_WIN64)
#define snprintf _snprintf
diff --git a/core/src/kmx/kmx_conversion.h b/core/src/kmx/kmx_conversion.h
index 803033e100..3ef4d0e413 100644
--- a/core/src/kmx/kmx_conversion.h
+++ b/core/src/kmx/kmx_conversion.h
@@ -6,20 +6,6 @@ namespace km {
namespace core {
namespace kmx {
-#define VK_SPACE 0x20
-#define VK_COLON 0xBA
-#define VK_EQUAL 0xBB
-#define VK_COMMA 0xBC
-#define VK_HYPHEN 0xBD
-#define VK_PERIOD 0xBE
-#define VK_SLASH 0xBF
-#define VK_ACCENT 0xC0
-#define VK_LBRKT 0xDB
-#define VK_BKSLASH 0xDC
-#define VK_RBRKT 0xDD
-#define VK_QUOTE 0xDE
-#define VK_xDF 0xDF
-
/**
* This maps an ASCII character corresponding to a US EN key cap to a
* Virtual Key and correctly sets the Shift BIT in the ShiftFlag DWORD.
diff --git a/core/src/kmx/kmx_processor.cpp b/core/src/kmx/kmx_processor.cpp
index 7ce8fad6ba..b4280384ca 100644
--- a/core/src/kmx/kmx_processor.cpp
+++ b/core/src/kmx/kmx_processor.cpp
@@ -6,7 +6,6 @@
using namespace km::core;
using namespace kmx;
-
static KMX_BOOL ContextItemsFromAppContext(KMX_WCHAR *buf, km_core_context_item** outPtr)
{
assert(buf);
diff --git a/core/src/state.cpp b/core/src/state.cpp
index a07fd40a98..bebd9d5e90 100644
--- a/core/src/state.cpp
+++ b/core/src/state.cpp
@@ -45,12 +45,8 @@ actions::actions(actions const &other)
size_t opt_index = 0;
for (auto &item : *this) {
if (item.type == KM_CORE_IT_PERSIST_OPT) {
- if (opt_index < _option_items_stack.size()) {
- item.option = &_option_items_stack[opt_index++];
- } else {
- // no matching item in the stack; clear pointer.
- item.option = nullptr;
- }
+ assert(opt_index < _option_items_stack.size());
+ item.option = &_option_items_stack[opt_index++];
}
}
}
diff --git a/core/tests/unit/api/state_api.tests.cpp b/core/tests/unit/api/state_api.tests.cpp
index 334f1b0e91..0e300004b4 100644
--- a/core/tests/unit/api/state_api.tests.cpp
+++ b/core/tests/unit/api/state_api.tests.cpp
@@ -364,7 +364,12 @@ TEST(StateApiTests, TestStateApi) {
if (test_clone_2->actions().back().type == KM_CORE_IT_END) {
test_clone_2->actions().pop_back();
}
- km_core_state_queue_action_items(test_clone_2, &action_clone);
+ km_core_action_item action_clone_queue[] = {
+ action_clone,
+ {KM_CORE_IT_END}
+ };
+
+ km_core_state_queue_action_items(test_clone_2, action_clone_queue);
test_clone_2->actions().commit();
km_core_action_item action_tp3 = {KM_CORE_IT_PERSIST_OPT, {0,}, };
diff --git a/developer/src/kmanalyze/kmanalyze.cpp b/developer/src/kmanalyze/kmanalyze.cpp
index 6ec751cd45..fb12cae1be 100644
--- a/developer/src/kmanalyze/kmanalyze.cpp
+++ b/developer/src/kmanalyze/kmanalyze.cpp
@@ -7,6 +7,7 @@
#include
#include
#include
+#include "../../../common/include/km_vkey.h"
BOOL LoadKeyboard(LPSTR fileName, LPKEYBOARD *lpKeyboard);
@@ -600,21 +601,6 @@ void PrintTree(LPKEYBOARD kbd, GROUPTREE *t, int depth) {
}
}
-
-// MapVirtualKeys is copied from syskbd.cpp (keyman32)
-#define VK_COLON 0xBA
-#define VK_EQUAL 0xBB
-#define VK_COMMA 0xBC
-#define VK_HYPHEN 0xBD
-#define VK_PERIOD 0xBE
-#define VK_SLASH 0xBF
-#define VK_ACCENT 0xC0
-#define VK_LBRKT 0xDB
-#define VK_BKSLASH 0xDC
-#define VK_RBRKT 0xDD
-#define VK_QUOTE 0xDE
-#define VK_xDF 0xDF
-
WCHAR MapVirtualKeys(WORD keyCode, UINT shiftFlags)
{
char shiftedDigit[] = ")!@#$%^&*(";
diff --git a/developer/src/kmc-model/package.json b/developer/src/kmc-model/package.json
index 52ef40c425..3a3bafdb6c 100644
--- a/developer/src/kmc-model/package.json
+++ b/developer/src/kmc-model/package.json
@@ -39,7 +39,7 @@
"@keymanapp/models-templates": "*",
"c8": "^7.12.0",
"chalk": "^2.4.2",
- "esbuild": "^0.25.0"
+ "esbuild": "^0.28.1"
},
"mocha": {
"spec": "build/test/**/*.tests.js",
diff --git a/developer/src/kmc/package.json b/developer/src/kmc/package.json
index 4f15f5e98c..bd6c22159a 100644
--- a/developer/src/kmc/package.json
+++ b/developer/src/kmc/package.json
@@ -54,7 +54,7 @@
"devDependencies": {
"@sentry/cli": "^2.31.0",
"c8": "^7.12.0",
- "esbuild": "^0.25.0",
+ "esbuild": "^0.28.1",
"typescript": "^5.4.5"
},
"mocha": {
diff --git a/developer/src/kmcmplib/src/CharToKeyConversion.cpp b/developer/src/kmcmplib/src/CharToKeyConversion.cpp
index 0c2b652d09..fe87d146e3 100644
--- a/developer/src/kmcmplib/src/CharToKeyConversion.cpp
+++ b/developer/src/kmcmplib/src/CharToKeyConversion.cpp
@@ -1,26 +1,7 @@
#include "pch.h"
#include "CharToKeyConversion.h"
-
-/* Following VKeys come from Windows */
-
-#define VK_COLON 0xBA
-#define VK_EQUAL 0xBB
-#define VK_COMMA 0xBC
-#define VK_HYPHEN 0xBD
-#define VK_PERIOD 0xBE
-#define VK_SLASH 0xBF
-#define VK_ACCENT 0xC0
-#define VK_LBRKT 0xDB
-#define VK_BKSLASH 0xDC
-#define VK_RBRKT 0xDD
-#define VK_QUOTE 0xDE
-#define VK_xDF 0xDF
-
-#define VK_NUMPAD0 0x60
-#define VK_NUMPAD9 0x69
-#define VK_SPACE 0x20
-
+#include "km_vkey.h"
/* Following code lifted from syskbd.cpp and tweaked for compiler use. Todo: consolidate */
diff --git a/developer/src/kmcmplib/src/Compiler.cpp b/developer/src/kmcmplib/src/Compiler.cpp
index fb6817e57b..f5c57a1d5a 100644
--- a/developer/src/kmcmplib/src/Compiler.cpp
+++ b/developer/src/kmcmplib/src/Compiler.cpp
@@ -77,8 +77,9 @@
#include "cp1252.h"
#include "virtualcharkeys.h"
-#include "../../../../common/include/vkeys.h"
-#include "../../../../common/include/keymanversion.h"
+#include "km_vkey.h"
+#include "vkeys.h"
+#include "keymanversion.h"
#include "crc32.h"
#include "debugstore.h"
diff --git a/developer/src/kmdecomp/savekeyboard.cpp b/developer/src/kmdecomp/savekeyboard.cpp
index 00949d0630..25e06c646d 100644
--- a/developer/src/kmdecomp/savekeyboard.cpp
+++ b/developer/src/kmdecomp/savekeyboard.cpp
@@ -33,6 +33,7 @@
#include "../../../common/include/kmx_file.h"
#include "../../../common/windows/cpp/include/legacy_kmx_memory.h"
#include "../../../common/include/vkeys.h"
+#include "../../../common/include/km_vkey.h"
#define SSN__PREFIX L"&"
diff --git a/ios/engine/KMEI/KeymanEngine/es-419.lproj/Localizable.strings b/ios/engine/KMEI/KeymanEngine/es-419.lproj/Localizable.strings
index 756eb0a49b..045de26e42 100644
--- a/ios/engine/KMEI/KeymanEngine/es-419.lproj/Localizable.strings
+++ b/ios/engine/KMEI/KeymanEngine/es-419.lproj/Localizable.strings
@@ -173,7 +173,7 @@
"menu-picker-title" = "Teclados";
/* Primary text for the Settings menu option to report keyboard crashes */
-"menu-settings-error-report" = "Permitir reporte de errores";
+"menu-settings-error-report" = "Permitir el envío de informes de errores";
/* Secondary text for the Settings menu option to report keyboard crashes */
"menu-settings-error-report-description" = "Puede requerir \"acceso completo\"";
diff --git a/linux/keyman-config/locale/es_419.po b/linux/keyman-config/locale/es_419.po
index 89269a5003..5cbb8daacc 100644
--- a/linux/keyman-config/locale/es_419.po
+++ b/linux/keyman-config/locale/es_419.po
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: keyman\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2020-08-19 19:17+0200\n"
-"PO-Revision-Date: 2023-06-19 17:25\n"
+"POT-Creation-Date: 2025-12-18 09:43+0700\n"
+"PO-Revision-Date: 2026-08-12 14:15\n"
"Last-Translator: \n"
"Language-Team: Spanish, Latin America\n"
"Language: es_419\n"
@@ -17,316 +17,425 @@ msgstr ""
"X-Crowdin-File: /master/linux/keyman-config.pot\n"
"X-Crowdin-File-ID: 504\n"
-#: keyman_config/__init__.py:68
-msgid "Neither sentry-sdk nor raven is available. Not enabling Sentry error reporting."
-msgstr "Ni sentry-sdk ni raven están disponibles. No se habilita la notificación de errores de Sentry."
-
-#: keyman_config/downloadkeyboard.py:23
+#: keyman_config/downloadkeyboard.py:24
msgid "Download Keyman keyboards"
msgstr "Descargar teclados de Keyman"
-#: keyman_config/downloadkeyboard.py:37 keyman_config/keyboard_details.py:49
-#: keyman_config/keyboard_details.py:340 keyman_config/view_installed.py:205
+#: keyman_config/downloadkeyboard.py:42
+msgid "_Back"
+msgstr ""
+
+#: keyman_config/downloadkeyboard.py:43
+msgid "Back to search"
+msgstr ""
+
+#: keyman_config/downloadkeyboard.py:48 keyman_config/keyboard_details.py:103
+#: keyman_config/keyboard_details.py:186 keyman_config/view_installed.py:174
msgid "_Close"
msgstr "Ce_rrar"
-#: keyman_config/install_kmp.py:99
+#: keyman_config/downloadkeyboard.py:49
+msgid "Close dialog"
+msgstr "Cerrar diálogo"
+
+#: keyman_config/downloadkeyboard.py:76
+msgid "Downloading kmp file failed"
+msgstr "Error al descargar el archivo kmp"
+
+#: keyman_config/downloadkeyboard.py:79
+msgid "Downloading keyboard file failed"
+msgstr "Error al descargar el archivo de teclado"
+
+#: keyman_config/get_kmp.py:34
+msgid "System"
+msgstr "Sistema"
+
+#: keyman_config/get_kmp.py:36
+msgid "Shared"
+msgstr "Compartido"
+
+#: keyman_config/get_kmp.py:38
+msgid "User"
+msgstr "Usuario"
+
+#: keyman_config/get_kmp.py:39
+msgid "Unknown"
+msgstr "Desconocido"
+
+#: keyman_config/install_kmp.py:87
msgid "You do not have permissions to install the keyboard files to the shared area /usr/local/share/keyman"
msgstr "No tienes permisos para instalar los archivos de teclado en el área compartida /usr/local/share/keyman"
-#: keyman_config/install_kmp.py:103
+#: keyman_config/install_kmp.py:91
msgid "You do not have permissions to install the documentation to the shared documentation area /usr/local/share/doc/keyman"
msgstr "No tienes permisos para instalar la documentación en el área compartida de documentación /usr/local/share/doc/keyman"
-#: keyman_config/install_kmp.py:107
+#: keyman_config/install_kmp.py:95
msgid "You do not have permissions to install the font files to the shared font area /usr/local/share/fonts"
msgstr "No tienes permisos para instalar los archivos de fuentes en el área compartida de fuentes /usr/local/share/fonts"
-#: keyman_config/install_kmp.py:179
+#: keyman_config/install_kmp.py:113
#, python-brace-format
-msgid "install_kmp.py: error: No kmp.json or kmp.inf found in {package}"
-msgstr "install_kmp.py: error: no se encontró ningún kmp.json o kmp.inf en {package}"
+msgid "File {kmpfile} doesn't exist"
+msgstr "El archivo {kmpfile} no existe"
-#: keyman_config/install_kmp.py:246
+#: keyman_config/install_kmp.py:132
#, python-brace-format
-msgid "install_kmp.py: error: No kmp.json or kmp.inf found in {packageFile}"
-msgstr "install_kmp.py: error: no se encontró ningún kmp.json o kmp.inf en {packageFile}"
+msgid "No kmp.json or kmp.inf found in {packageFile}"
+msgstr ""
-#: keyman_config/install_window.py:54
+#: keyman_config/install_kmp.py:159
+#, python-brace-format
+msgid "{packageFile} requires Keyman {keymanVersion} or higher"
+msgstr ""
+
+#: keyman_config/install_kmp.py:307
+msgid "Please use fcitx5-configtool to add the keyboard to the desired group"
+msgstr ""
+
+#: keyman_config/install_window.py:62
#, python-brace-format
msgid "Installing keyboard/package {keyboardid}"
msgstr "Instalando teclado/paquete {keyboardid}"
-#: keyman_config/install_window.py:72 keyman_config/install_window.py:93
-msgid "Keyboard is installed already"
-msgstr "El teclado ya está instalado"
-
-#: keyman_config/install_window.py:74
-#, python-brace-format
-msgid "The {name} keyboard is already installed at version {version}. Do you want to uninstall then reinstall it?"
-msgstr "El teclado {name} ya está instalado en la versión {version}. ¿Deseas desinstalarlo y luego reinstalarlo?"
-
#: keyman_config/install_window.py:95
+msgid "Details"
+msgstr "Detalles"
+
+#: keyman_config/install_window.py:98
+msgid "README"
+msgstr "LÉAME"
+
+#: keyman_config/install_window.py:125
+#, python-brace-format
+msgid "The file '{kmpfile}' is not a Keyman keyboard package!"
+msgstr ""
+
+#. i18n: The words in braces (e.g. `{name}`) are placeholders. Don't translate them!
+#: keyman_config/install_window.py:141
#, python-brace-format
msgid "The {name} keyboard is already installed with a newer version {installedversion}. Do you want to uninstall it and install the older version {version}?"
msgstr "El teclado {name} ya está instalado en una versión más nueva {installedversion}. ¿Deseas desinstalarlo e instalar la versión más vieja {version}?"
-#: keyman_config/install_window.py:128
-msgid "Keyboard layouts: "
-msgstr "Distribuciones de teclado: "
+#: keyman_config/install_window.py:161
+msgid "Keyboard is installed already"
+msgstr "El teclado ya está instalado"
-#: keyman_config/install_window.py:147
-msgid "Fonts: "
-msgstr "Fuentes: "
-
-#: keyman_config/install_window.py:167 keyman_config/keyboard_details.py:96
-msgid "Package version: "
-msgstr "Versión del paquete: "
-
-#: keyman_config/install_window.py:179
-msgid "Author: "
-msgstr "Autor: "
-
-#: keyman_config/install_window.py:197
-msgid "Website: "
-msgstr "Página web: "
-
-#: keyman_config/install_window.py:211
-msgid "Copyright: "
-msgstr "Derechos de autor: "
-
-#: keyman_config/install_window.py:245
-msgid "Details"
-msgstr "Detalles"
-
-#: keyman_config/install_window.py:248
-msgid "README"
-msgstr "LÉAME"
-
-#: keyman_config/install_window.py:256 keyman_config/view_installed.py:200
+#: keyman_config/install_window.py:170
msgid "_Install"
msgstr "_Instalar"
-#: keyman_config/install_window.py:260
+#: keyman_config/install_window.py:174
msgid "_Cancel"
msgstr "_Cancelar"
-#: keyman_config/install_window.py:305
+#: keyman_config/install_window.py:230
+msgid "Keyboard layouts: "
+msgstr "Distribuciones de teclado: "
+
+#: keyman_config/install_window.py:250
+msgid "Fonts: "
+msgstr "Fuentes: "
+
+#: keyman_config/install_window.py:272 keyman_config/keyboard_details.py:81
+msgid "Package version: "
+msgstr "Versión del paquete: "
+
+#: keyman_config/install_window.py:288
+msgid "Author: "
+msgstr "Autor: "
+
+#: keyman_config/install_window.py:306
+msgid "Website: "
+msgstr "Página web: "
+
+#: keyman_config/install_window.py:321
+msgid "Copyright: "
+msgstr "Derechos de autor: "
+
+#: keyman_config/install_window.py:384
#, python-brace-format
msgid "Keyboard {name} installed"
msgstr "Teclado {name} instalado"
-#: keyman_config/install_window.py:310 keyman_config/install_window.py:315
+#: keyman_config/install_window.py:397 keyman_config/install_window.py:402
#, python-brace-format
msgid "Keyboard {name} could not be installed."
msgstr "No se pudo instalar el teclado {name}."
-#: keyman_config/install_window.py:311
+#: keyman_config/install_window.py:398
msgid "Error Message:"
msgstr "Mensajes de error:"
-#: keyman_config/install_window.py:316
+#: keyman_config/install_window.py:403
msgid "Warning Message:"
msgstr "Mensaje de advertencia:"
-#: keyman_config/keyboard_details.py:37
+#: keyman_config/keyboard_details.py:44
#, python-brace-format
msgid "{name} keyboard"
msgstr "Teclado {name}"
-#: keyman_config/keyboard_details.py:53
+#: keyman_config/keyboard_details.py:77
+msgid "Package name: "
+msgstr "Nombre del paquete: "
+
+#: keyman_config/keyboard_details.py:79
+msgid "Package id: "
+msgstr "Nombre del paquete: "
+
+#: keyman_config/keyboard_details.py:84
+msgid "Package description: "
+msgstr "Descripción del paquete: "
+
+#: keyman_config/keyboard_details.py:87
+msgid "Package author: "
+msgstr "Autor del paquete: "
+
+#: keyman_config/keyboard_details.py:90
+msgid "Package copyright: "
+msgstr "Derechos de autor del paquete: "
+
+#: keyman_config/keyboard_details.py:131
+msgid "Keyboard filename: "
+msgstr "Nombre de archivo del teclado: "
+
+#: keyman_config/keyboard_details.py:139
+msgid "Keyboard name: "
+msgstr "Nombre del teclado: "
+
+#: keyman_config/keyboard_details.py:140
+msgid "Keyboard id: "
+msgstr "Nombre del teclado: "
+
+#: keyman_config/keyboard_details.py:141
+msgid "Keyboard version: "
+msgstr "Versión del teclado: "
+
+#: keyman_config/keyboard_details.py:143
+msgid "Keyboard author: "
+msgstr "Autor del teclado: "
+
+#: keyman_config/keyboard_details.py:146
+msgid "Keyboard license: "
+msgstr "Licencia del teclado: "
+
+#: keyman_config/keyboard_details.py:148
+msgid "Keyboard description: "
+msgstr "Descripción del teclado: "
+
+#: keyman_config/keyboard_details.py:191
msgid "ERROR: Keyboard metadata is damaged.\n"
"Please \"Uninstall\" and then \"Install\" the keyboard."
msgstr "ERROR: Los metadatos del teclado están dañados.\n"
"Por favor \"Desinstalar\" y luego \"Instalar\" el teclado."
-#: keyman_config/keyboard_details.py:74
-msgid "Package name: "
-msgstr "Nombre del paquete: "
-
-#: keyman_config/keyboard_details.py:85
-msgid "Package id: "
-msgstr "Nombre del paquete: "
-
-#: keyman_config/keyboard_details.py:108
-msgid "Package description: "
-msgstr "Descripción del paquete: "
-
-#: keyman_config/keyboard_details.py:121
-msgid "Package author: "
-msgstr "Autor del paquete: "
-
-#: keyman_config/keyboard_details.py:133
-msgid "Package copyright: "
-msgstr "Derechos de autor del paquete: "
-
-#: keyman_config/keyboard_details.py:174
-msgid "Keyboard filename: "
-msgstr "Nombre de archivo del teclado: "
-
-#: keyman_config/keyboard_details.py:187
-msgid "Keyboard name: "
-msgstr "Nombre del teclado: "
-
-#: keyman_config/keyboard_details.py:198
-msgid "Keyboard id: "
-msgstr "Nombre del teclado: "
-
-#: keyman_config/keyboard_details.py:209
-msgid "Keyboard version: "
-msgstr "Versión del teclado: "
-
-#: keyman_config/keyboard_details.py:221
-msgid "Keyboard author: "
-msgstr "Autor del teclado: "
-
-#: keyman_config/keyboard_details.py:232
-msgid "Keyboard license: "
-msgstr "Licencia del teclado: "
-
-#: keyman_config/keyboard_details.py:243
-msgid "Keyboard description: "
-msgstr "Descripción del teclado: "
-
-#: keyman_config/keyboard_details.py:334
+#: keyman_config/keyboard_details.py:220
#, python-brace-format
msgid "Scan this code to load this keyboard\n"
"on another device or share online"
msgstr "Escanea este código para cargar este teclado\n"
"en otro dispositivo o compártelo en línea"
-#: keyman_config/options.py:24
-#, python-brace-format
-msgid "{packageId} Settings"
-msgstr "Configuración de {packageId}"
-
-#: keyman_config/view_installed.py:30
-msgid "Keyman Configuration"
-msgstr "Configuración de Keyman"
-
-#: keyman_config/view_installed.py:60
-msgid "Choose a kmp file..."
-msgstr "Elige un archivo kmp..."
-
-#. i18n: file type in file selection dialog
-#: keyman_config/view_installed.py:65
-msgid "KMP files"
-msgstr "Archivos KMP"
-
#. i18n: column header in table displaying installed keyboards
-#: keyman_config/view_installed.py:141
+#: keyman_config/keyboard_layouts_widget.py:37
msgid "Icon"
msgstr "Ícono"
#. i18n: column header in table displaying installed keyboards
-#: keyman_config/view_installed.py:145
+#: keyman_config/keyboard_layouts_widget.py:41
msgid "Name"
msgstr "Nombre"
#. i18n: column header in table displaying installed keyboards
-#: keyman_config/view_installed.py:148
+#: keyman_config/keyboard_layouts_widget.py:44
msgid "Version"
msgstr "Versión"
-#: keyman_config/view_installed.py:161
+#. i18n: column header in table displaying installed keyboards
+#: keyman_config/keyboard_layouts_widget.py:47
+msgid "Area"
+msgstr ""
+
+#: keyman_config/keyboard_layouts_widget.py:65
msgid "_Uninstall"
msgstr "De_sinstalar"
-#: keyman_config/view_installed.py:162 keyman_config/view_installed.py:304
+#: keyman_config/keyboard_layouts_widget.py:66 keyman_config/keyboard_layouts_widget.py:125
msgid "Uninstall keyboard"
msgstr "Desinstalar teclado"
-#: keyman_config/view_installed.py:167
+#: keyman_config/keyboard_layouts_widget.py:71
msgid "_About"
msgstr "So_bre"
-#: keyman_config/view_installed.py:168 keyman_config/view_installed.py:306
+#: keyman_config/keyboard_layouts_widget.py:72 keyman_config/keyboard_layouts_widget.py:127
msgid "About keyboard"
msgstr "Sobre el teclado"
-#: keyman_config/view_installed.py:173
+#: keyman_config/keyboard_layouts_widget.py:77
msgid "_Help"
msgstr "A_yuda"
-#: keyman_config/view_installed.py:174 keyman_config/view_installed.py:305
+#: keyman_config/keyboard_layouts_widget.py:78 keyman_config/keyboard_layouts_widget.py:126
msgid "Help for keyboard"
msgstr "Ayuda para el teclado"
-#: keyman_config/view_installed.py:179
+#: keyman_config/keyboard_layouts_widget.py:83
msgid "_Options"
msgstr "_Opciones"
-#: keyman_config/view_installed.py:180 keyman_config/view_installed.py:307
+#: keyman_config/keyboard_layouts_widget.py:84 keyman_config/keyboard_layouts_widget.py:128
msgid "Settings for keyboard"
msgstr "Configuración del teclado"
-#: keyman_config/view_installed.py:190
-msgid "_Refresh"
-msgstr "Ac_tualizar"
-
-#: keyman_config/view_installed.py:191
-msgid "Refresh keyboard list"
-msgstr "Actualizar lista de teclados"
-
-#: keyman_config/view_installed.py:195
-msgid "_Download"
-msgstr "_Descargar"
-
-#: keyman_config/view_installed.py:196
-msgid "Download and install a keyboard from the Keyman website"
-msgstr "Descargar e instalar un teclado desde la página web de Keyman"
-
-#: keyman_config/view_installed.py:201
-msgid "Install a keyboard from a file"
-msgstr "Instalar un teclado desde un archivo"
-
-#: keyman_config/view_installed.py:206
-msgid "Close window"
-msgstr "Cerrar ventana"
-
-#: keyman_config/view_installed.py:278
+#: keyman_config/keyboard_layouts_widget.py:97
#, python-brace-format
msgid "Uninstall keyboard {package}"
msgstr "Desinstalar teclado {package}"
-#: keyman_config/view_installed.py:280
+#: keyman_config/keyboard_layouts_widget.py:99
#, python-brace-format
msgid "Help for keyboard {package}"
msgstr "Ayuda para el teclado {package}"
-#: keyman_config/view_installed.py:282
+#: keyman_config/keyboard_layouts_widget.py:101
#, python-brace-format
msgid "About keyboard {package}"
msgstr "Sobre el teclado {package}"
-#: keyman_config/view_installed.py:284
+#: keyman_config/keyboard_layouts_widget.py:103
#, python-brace-format
msgid "Settings for keyboard {package}"
msgstr "Configuración del teclado {package}"
-#: keyman_config/view_installed.py:349
+#: keyman_config/keyboard_layouts_widget.py:141
msgid "Uninstall keyboard package?"
msgstr "¿Desinstalar paquete del teclado?"
-#: keyman_config/view_installed.py:351
+#: keyman_config/keyboard_layouts_widget.py:142
#, python-brace-format
-msgid "Are you sure that you want to uninstall the {keyboard} keyboard and its fonts?"
-msgstr "¿Estás seguro de que deseas desinstalar el teclado {keyboard} y sus fuentes?"
+msgid "Are you sure that you want to uninstall the {keyboard} keyboard?"
+msgstr ""
-#: keyman_config/welcome.py:22
+#: keyman_config/keyboard_layouts_widget.py:149
+msgid "The following fonts will also be uninstalled:\n"
+msgstr ""
+
+#: keyman_config/keyboard_layouts_widget.py:160
+msgid "Uninstalling keyboard failed.\n\n"
+"Error message: "
+msgstr ""
+
+#: keyman_config/keyboard_options_view.py:26
+#, python-brace-format
+msgid "{packageId} Settings"
+msgstr "Configuración de {packageId}"
+
+#: keyman_config/options_widget.py:16
+msgid "General"
+msgstr "General"
+
+#: keyman_config/options_widget.py:24
+msgid "Automatically report errors to keyman.com"
+msgstr "Reportar errores a keyman.com automáticamente"
+
+#: keyman_config/options_widget.py:39
+msgid "Simulate AltGr with Ctrl + Alt"
+msgstr ""
+
+#: keyman_config/sentry_handling.py:55 keyman_config/sentry_handling.py:58
+msgid "Neither sentry-sdk nor raven is available. Not enabling Sentry error reporting."
+msgstr "Ni sentry-sdk ni raven están disponibles. No se habilita los informes de errores de Sentry."
+
+#: keyman_config/uninstall_kmp.py:85
+#, python-format
+msgid "You do not have permission to uninstall the files in %s. You need to run this with `sudo`."
+msgstr ""
+
+#: keyman_config/uninstall_kmp.py:136
+msgid "Please use fcitx5-configtool to remove the keyboard from the group"
+msgstr ""
+
+#: keyman_config/view_installed.py:38
+msgid "Keyman Configuration"
+msgstr "Configuración de Keyman"
+
+#: keyman_config/view_installed.py:69
+msgid "Choose a kmp file..."
+msgstr "Elige un archivo kmp..."
+
+#. i18n: file type in file selection dialog
+#: keyman_config/view_installed.py:74
+msgid "KMP files"
+msgstr "Archivos KMP"
+
+#: keyman_config/view_installed.py:107
+msgid "ibus-daemon is not running. Try to start ibus-daemon now?"
+msgstr ""
+
+#: keyman_config/view_installed.py:109
+msgid "If you just recently installed Keyman you might have to reboot or logout and login again."
+msgstr ""
+
+#: keyman_config/view_installed.py:117
+msgid "Unable to start ibus-daemon. Please reboot or logout and login again."
+msgstr ""
+
+#: keyman_config/view_installed.py:151
+msgid "Keyboard Layouts"
+msgstr "Distribuciones de teclado"
+
+#: keyman_config/view_installed.py:152
+msgid "Options"
+msgstr "Opciones"
+
+#: keyman_config/view_installed.py:159
+msgid "_Install keyboard..."
+msgstr ""
+
+#: keyman_config/view_installed.py:160
+msgid "Install a keyboard from a file"
+msgstr "Instalar un teclado desde un archivo"
+
+#: keyman_config/view_installed.py:164
+msgid "_Download keyboard..."
+msgstr ""
+
+#: keyman_config/view_installed.py:165
+msgid "Download and install a keyboard from the Keyman website"
+msgstr "Descargar e instalar un teclado desde la página web de Keyman"
+
+#: keyman_config/view_installed.py:169
+msgid "_Refresh"
+msgstr "Ac_tualizar"
+
+#: keyman_config/view_installed.py:170
+msgid "Refresh keyboard list"
+msgstr "Actualizar lista de teclados"
+
+#: keyman_config/view_installed.py:175
+msgid "Close window"
+msgstr "Cerrar ventana"
+
+#: keyman_config/welcome.py:25
#, python-brace-format
msgid "{name} installed"
msgstr "{name} instalado"
-#: keyman_config/welcome.py:40
+#: keyman_config/welcome.py:43
msgid "Open in _Web browser"
msgstr "Abrir en navegador _web"
-#: keyman_config/welcome.py:42
+#: keyman_config/welcome.py:45
msgid "Open in the default web browser to do things like printing"
msgstr "Abrir en el navegador web predeterminado para hacer cosas como imprimir"
-#: keyman_config/welcome.py:45
+#: keyman_config/welcome.py:48
msgid "_OK"
msgstr "_Aceptar"
+#: km-config:53
+msgid "Missing requirements. Please install python3-fonttools."
+msgstr "Requisitos faltantes. Por favor, instale python3-fonttools."
+
diff --git a/linux/mcompile/keymap/keymap.h b/linux/mcompile/keymap/keymap.h
index 74e67352b0..f39d501d70 100644
--- a/linux/mcompile/keymap/keymap.h
+++ b/linux/mcompile/keymap/keymap.h
@@ -15,6 +15,7 @@
#include
#include "km_u16.h"
#include
+#include "km_vkey.h"
enum ShiftState {
Base = 0, // 0
@@ -29,39 +30,6 @@ enum ShiftState {
ShftXxxx = Shft | Xxxx, // 9
};
-#define VK_SPACE 0x20
-#define VK_COLON 0xBA
-#define VK_EQUAL 0xBB
-#define VK_COMMA 0xBC
-#define VK_HYPHEN 0xBD
-#define VK_PERIOD 0xBE
-#define VK_SLASH 0xBF
-#define VK_ACCENT 0xC0
-#define VK_LBRKT 0xDB
-#define VK_BKSLASH 0xDC
-#define VK_RBRKT 0xDD
-#define VK_QUOTE 0xDE
-#define VK_xDF 0xDF
-#define VK_OEM_102 0xE2 // "<>" or "\|" on RT 102-key kbd.
-
-#define VK_DIVIDE 0x6F
-#define VK_CANCEL 3
-#define VK_DECIMAL 0x2E
-
-#define VK_OEM_CLEAR 0xFE
-#define VK_LSHIFT 0xA0
-#define VK_RSHIFT 0xA1
-#define VK_LCONTROL 0xA2
-#define VK_RCONTROL 0xA3
-#define VK_LMENU 0xA4
-#define VK_RMENU 0xA5
-
-#define VK_SHIFT 0x10
-#define VK_CONTROL 0x11
-#define VK_MENU 0x12
-#define VK_PAUSE 0x13
-#define VK_CAPITAL 0x14
-
typedef std::vector vec_string_1D;
typedef std::vector vec_dword_1D;
typedef std::vector > vec_dword_2D;
diff --git a/mac/Keyman4MacIM/Keyman4MacIM/es-419.lproj/Localizable.strings b/mac/Keyman4MacIM/Keyman4MacIM/es-419.lproj/Localizable.strings
index c6b546cb61..4fcccc1115 100644
--- a/mac/Keyman4MacIM/Keyman4MacIM/es-419.lproj/Localizable.strings
+++ b/mac/Keyman4MacIM/Keyman4MacIM/es-419.lproj/Localizable.strings
@@ -31,6 +31,9 @@
/* Message displayed in Configuration window when keyboard metadata cannot be loaded */
"message-error-unknown-metadata" = "desconocido";
+/* Configuration window section heading when keyboard has no package name */
+"unknown-package-name" = "Unnamed Keyboard Package";
+
/* Button text to acknowledge that .kmp file could not be read */
"button-keyboard-file-unreadable" = "Aceptar";
diff --git a/mac/mcompile/keymap.h b/mac/mcompile/keymap.h
index 99e563b4c0..d09645f747 100644
--- a/mac/mcompile/keymap.h
+++ b/mac/mcompile/keymap.h
@@ -6,8 +6,8 @@
#include
#include
#include
-
#include "../../common/include/km_u16.h"
+#include "km_vkey.h"
enum ShiftState {
Base = 0, // 0
@@ -22,39 +22,6 @@ enum ShiftState {
ShftXxxx = Shft | Xxxx, // 9
};
-#define VK_SPACE 0x20
-#define VK_COLON 0xBA
-#define VK_EQUAL 0xBB
-#define VK_COMMA 0xBC
-#define VK_HYPHEN 0xBD
-#define VK_PERIOD 0xBE
-#define VK_SLASH 0xBF
-#define VK_ACCENT 0xC0
-#define VK_LBRKT 0xDB
-#define VK_BKSLASH 0xDC
-#define VK_RBRKT 0xDD
-#define VK_QUOTE 0xDE
-#define VK_xDF 0xDF
-#define VK_OEM_102 0xE2 // "<>" or "\|" on RT 102-key kbd.
-
-#define VK_DIVIDE 0x6F
-#define VK_CANCEL 3
-#define VK_DECIMAL 0x2E
-
-#define VK_OEM_CLEAR 0xFE
-#define VK_LSHIFT 0xA0
-#define VK_RSHIFT 0xA1
-#define VK_LCONTROL 0xA2
-#define VK_RCONTROL 0xA3
-#define VK_LMENU 0xA4
-#define VK_RMENU 0xA5
-
-#define VK_SHIFT 0x10
-#define VK_CONTROL 0x11
-#define VK_MENU 0x12
-#define VK_PAUSE 0x13
-#define VK_CAPITAL 0x14
-
typedef std::vector vec_string_1D;
typedef std::vector vec_dword_1D;
typedef std::vector > vec_dword_2D;
diff --git a/package-lock.json b/package-lock.json
index 5ac21c94d1..f0e9ed18aa 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -58,7 +58,7 @@
"ajv-cli": "^5.0.0",
"ajv-formats": "^2.1.1",
"chai": "^5.1.0",
- "esbuild": "^0.25.0",
+ "esbuild": "^0.28.1",
"eslint": "^8.39.0",
"eslint-config-love": "^53.0.0",
"eslint-plugin-import": "^2.27.5",
@@ -345,7 +345,7 @@
"devDependencies": {
"@sentry/cli": "^2.31.0",
"c8": "^7.12.0",
- "esbuild": "^0.25.0",
+ "esbuild": "^0.28.1",
"typescript": "^5.4.5"
}
},
@@ -876,7 +876,7 @@
"@keymanapp/models-templates": "*",
"c8": "^7.12.0",
"chalk": "^2.4.2",
- "esbuild": "^0.25.0"
+ "esbuild": "^0.28.1"
}
},
"developer/src/kmc-model-info": {
@@ -1398,10 +1398,299 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/@esbuild/aix-ppc64": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.1.tgz",
+ "integrity": "sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "aix"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.1.tgz",
+ "integrity": "sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm64": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.1.tgz",
+ "integrity": "sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-x64": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.1.tgz",
+ "integrity": "sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-arm64": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.1.tgz",
+ "integrity": "sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-x64": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.1.tgz",
+ "integrity": "sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.1.tgz",
+ "integrity": "sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-x64": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.1.tgz",
+ "integrity": "sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.1.tgz",
+ "integrity": "sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm64": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.1.tgz",
+ "integrity": "sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ia32": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.1.tgz",
+ "integrity": "sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-loong64": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.1.tgz",
+ "integrity": "sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-mips64el": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.1.tgz",
+ "integrity": "sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==",
+ "cpu": [
+ "mips64el"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ppc64": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.1.tgz",
+ "integrity": "sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-riscv64": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.1.tgz",
+ "integrity": "sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-s390x": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.1.tgz",
+ "integrity": "sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-x64": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.1.tgz",
+ "integrity": "sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
"node_modules/@esbuild/netbsd-arm64": {
- "version": "0.25.0",
- "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.0.tgz",
- "integrity": "sha512-RuG4PSMPFfrkH6UwCAqBzauBWTygTvb1nxWasEJooGSJ/NwRw7b2HOwyRTQIU97Hq37l3npXoZGYMy3b3xYvPw==",
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.1.tgz",
+ "integrity": "sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==",
"cpu": [
"arm64"
],
@@ -1415,6 +1704,142 @@
"node": ">=18"
}
},
+ "node_modules/@esbuild/netbsd-x64": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.1.tgz",
+ "integrity": "sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-arm64": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.1.tgz",
+ "integrity": "sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-x64": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.1.tgz",
+ "integrity": "sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openharmony-arm64": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.1.tgz",
+ "integrity": "sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/sunos-x64": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.1.tgz",
+ "integrity": "sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-arm64": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.1.tgz",
+ "integrity": "sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-ia32": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.1.tgz",
+ "integrity": "sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-x64": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.1.tgz",
+ "integrity": "sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
"node_modules/@eslint-community/eslint-utils": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz",
@@ -4309,6 +4734,490 @@
"node": ">=18.0.0"
}
},
+ "node_modules/@web/dev-server-esbuild/node_modules/@esbuild/aix-ppc64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz",
+ "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "aix"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@web/dev-server-esbuild/node_modules/@esbuild/android-arm": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz",
+ "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@web/dev-server-esbuild/node_modules/@esbuild/android-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz",
+ "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@web/dev-server-esbuild/node_modules/@esbuild/android-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz",
+ "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@web/dev-server-esbuild/node_modules/@esbuild/darwin-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz",
+ "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@web/dev-server-esbuild/node_modules/@esbuild/darwin-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz",
+ "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@web/dev-server-esbuild/node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz",
+ "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@web/dev-server-esbuild/node_modules/@esbuild/freebsd-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz",
+ "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@web/dev-server-esbuild/node_modules/@esbuild/linux-arm": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz",
+ "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@web/dev-server-esbuild/node_modules/@esbuild/linux-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz",
+ "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@web/dev-server-esbuild/node_modules/@esbuild/linux-ia32": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz",
+ "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@web/dev-server-esbuild/node_modules/@esbuild/linux-loong64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz",
+ "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@web/dev-server-esbuild/node_modules/@esbuild/linux-mips64el": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz",
+ "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==",
+ "cpu": [
+ "mips64el"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@web/dev-server-esbuild/node_modules/@esbuild/linux-ppc64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz",
+ "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@web/dev-server-esbuild/node_modules/@esbuild/linux-riscv64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz",
+ "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@web/dev-server-esbuild/node_modules/@esbuild/linux-s390x": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz",
+ "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@web/dev-server-esbuild/node_modules/@esbuild/linux-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz",
+ "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@web/dev-server-esbuild/node_modules/@esbuild/netbsd-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz",
+ "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@web/dev-server-esbuild/node_modules/@esbuild/netbsd-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz",
+ "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@web/dev-server-esbuild/node_modules/@esbuild/openbsd-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz",
+ "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@web/dev-server-esbuild/node_modules/@esbuild/openbsd-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz",
+ "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@web/dev-server-esbuild/node_modules/@esbuild/openharmony-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz",
+ "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@web/dev-server-esbuild/node_modules/@esbuild/sunos-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz",
+ "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@web/dev-server-esbuild/node_modules/@esbuild/win32-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz",
+ "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@web/dev-server-esbuild/node_modules/@esbuild/win32-ia32": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz",
+ "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@web/dev-server-esbuild/node_modules/@esbuild/win32-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz",
+ "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@web/dev-server-esbuild/node_modules/esbuild": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz",
+ "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "optionalDependencies": {
+ "@esbuild/aix-ppc64": "0.25.12",
+ "@esbuild/android-arm": "0.25.12",
+ "@esbuild/android-arm64": "0.25.12",
+ "@esbuild/android-x64": "0.25.12",
+ "@esbuild/darwin-arm64": "0.25.12",
+ "@esbuild/darwin-x64": "0.25.12",
+ "@esbuild/freebsd-arm64": "0.25.12",
+ "@esbuild/freebsd-x64": "0.25.12",
+ "@esbuild/linux-arm": "0.25.12",
+ "@esbuild/linux-arm64": "0.25.12",
+ "@esbuild/linux-ia32": "0.25.12",
+ "@esbuild/linux-loong64": "0.25.12",
+ "@esbuild/linux-mips64el": "0.25.12",
+ "@esbuild/linux-ppc64": "0.25.12",
+ "@esbuild/linux-riscv64": "0.25.12",
+ "@esbuild/linux-s390x": "0.25.12",
+ "@esbuild/linux-x64": "0.25.12",
+ "@esbuild/netbsd-arm64": "0.25.12",
+ "@esbuild/netbsd-x64": "0.25.12",
+ "@esbuild/openbsd-arm64": "0.25.12",
+ "@esbuild/openbsd-x64": "0.25.12",
+ "@esbuild/openharmony-arm64": "0.25.12",
+ "@esbuild/sunos-x64": "0.25.12",
+ "@esbuild/win32-arm64": "0.25.12",
+ "@esbuild/win32-ia32": "0.25.12",
+ "@esbuild/win32-x64": "0.25.12"
+ }
+ },
"node_modules/@web/dev-server-esbuild/node_modules/parse5": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz",
@@ -6683,9 +7592,9 @@
"license": "MIT"
},
"node_modules/esbuild": {
- "version": "0.25.0",
- "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.0.tgz",
- "integrity": "sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw==",
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.1.tgz",
+ "integrity": "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
@@ -6696,439 +7605,32 @@
"node": ">=18"
},
"optionalDependencies": {
- "@esbuild/aix-ppc64": "0.25.0",
- "@esbuild/android-arm": "0.25.0",
- "@esbuild/android-arm64": "0.25.0",
- "@esbuild/android-x64": "0.25.0",
- "@esbuild/darwin-arm64": "0.25.0",
- "@esbuild/darwin-x64": "0.25.0",
- "@esbuild/freebsd-arm64": "0.25.0",
- "@esbuild/freebsd-x64": "0.25.0",
- "@esbuild/linux-arm": "0.25.0",
- "@esbuild/linux-arm64": "0.25.0",
- "@esbuild/linux-ia32": "0.25.0",
- "@esbuild/linux-loong64": "0.25.0",
- "@esbuild/linux-mips64el": "0.25.0",
- "@esbuild/linux-ppc64": "0.25.0",
- "@esbuild/linux-riscv64": "0.25.0",
- "@esbuild/linux-s390x": "0.25.0",
- "@esbuild/linux-x64": "0.25.0",
- "@esbuild/netbsd-arm64": "0.25.0",
- "@esbuild/netbsd-x64": "0.25.0",
- "@esbuild/openbsd-arm64": "0.25.0",
- "@esbuild/openbsd-x64": "0.25.0",
- "@esbuild/sunos-x64": "0.25.0",
- "@esbuild/win32-arm64": "0.25.0",
- "@esbuild/win32-ia32": "0.25.0",
- "@esbuild/win32-x64": "0.25.0"
- }
- },
- "node_modules/esbuild/node_modules/@esbuild/aix-ppc64": {
- "version": "0.25.0",
- "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.0.tgz",
- "integrity": "sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ==",
- "cpu": [
- "ppc64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "aix"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/esbuild/node_modules/@esbuild/android-arm": {
- "version": "0.25.0",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.0.tgz",
- "integrity": "sha512-PTyWCYYiU0+1eJKmw21lWtC+d08JDZPQ5g+kFyxP0V+es6VPPSUhM6zk8iImp2jbV6GwjX4pap0JFbUQN65X1g==",
- "cpu": [
- "arm"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "android"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/esbuild/node_modules/@esbuild/android-arm64": {
- "version": "0.25.0",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.0.tgz",
- "integrity": "sha512-grvv8WncGjDSyUBjN9yHXNt+cq0snxXbDxy5pJtzMKGmmpPxeAmAhWxXI+01lU5rwZomDgD3kJwulEnhTRUd6g==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "android"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/esbuild/node_modules/@esbuild/android-x64": {
- "version": "0.25.0",
- "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.0.tgz",
- "integrity": "sha512-m/ix7SfKG5buCnxasr52+LI78SQ+wgdENi9CqyCXwjVR2X4Jkz+BpC3le3AoBPYTC9NHklwngVXvbJ9/Akhrfg==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "android"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/esbuild/node_modules/@esbuild/darwin-arm64": {
- "version": "0.25.0",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.0.tgz",
- "integrity": "sha512-mVwdUb5SRkPayVadIOI78K7aAnPamoeFR2bT5nszFUZ9P8UpK4ratOdYbZZXYSqPKMHfS1wdHCJk1P1EZpRdvw==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/esbuild/node_modules/@esbuild/darwin-x64": {
- "version": "0.25.0",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.0.tgz",
- "integrity": "sha512-DgDaYsPWFTS4S3nWpFcMn/33ZZwAAeAFKNHNa1QN0rI4pUjgqf0f7ONmXf6d22tqTY+H9FNdgeaAa+YIFUn2Rg==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/esbuild/node_modules/@esbuild/freebsd-arm64": {
- "version": "0.25.0",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.0.tgz",
- "integrity": "sha512-VN4ocxy6dxefN1MepBx/iD1dH5K8qNtNe227I0mnTRjry8tj5MRk4zprLEdG8WPyAPb93/e4pSgi1SoHdgOa4w==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "freebsd"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/esbuild/node_modules/@esbuild/freebsd-x64": {
- "version": "0.25.0",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.0.tgz",
- "integrity": "sha512-mrSgt7lCh07FY+hDD1TxiTyIHyttn6vnjesnPoVDNmDfOmggTLXRv8Id5fNZey1gl/V2dyVK1VXXqVsQIiAk+A==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "freebsd"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/esbuild/node_modules/@esbuild/linux-arm": {
- "version": "0.25.0",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.0.tgz",
- "integrity": "sha512-vkB3IYj2IDo3g9xX7HqhPYxVkNQe8qTK55fraQyTzTX/fxaDtXiEnavv9geOsonh2Fd2RMB+i5cbhu2zMNWJwg==",
- "cpu": [
- "arm"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/esbuild/node_modules/@esbuild/linux-arm64": {
- "version": "0.25.0",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.0.tgz",
- "integrity": "sha512-9QAQjTWNDM/Vk2bgBl17yWuZxZNQIF0OUUuPZRKoDtqF2k4EtYbpyiG5/Dk7nqeK6kIJWPYldkOcBqjXjrUlmg==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/esbuild/node_modules/@esbuild/linux-ia32": {
- "version": "0.25.0",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.0.tgz",
- "integrity": "sha512-43ET5bHbphBegyeqLb7I1eYn2P/JYGNmzzdidq/w0T8E2SsYL1U6un2NFROFRg1JZLTzdCoRomg8Rvf9M6W6Gg==",
- "cpu": [
- "ia32"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/esbuild/node_modules/@esbuild/linux-loong64": {
- "version": "0.25.0",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.0.tgz",
- "integrity": "sha512-fC95c/xyNFueMhClxJmeRIj2yrSMdDfmqJnyOY4ZqsALkDrrKJfIg5NTMSzVBr5YW1jf+l7/cndBfP3MSDpoHw==",
- "cpu": [
- "loong64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/esbuild/node_modules/@esbuild/linux-mips64el": {
- "version": "0.25.0",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.0.tgz",
- "integrity": "sha512-nkAMFju7KDW73T1DdH7glcyIptm95a7Le8irTQNO/qtkoyypZAnjchQgooFUDQhNAy4iu08N79W4T4pMBwhPwQ==",
- "cpu": [
- "mips64el"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/esbuild/node_modules/@esbuild/linux-ppc64": {
- "version": "0.25.0",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.0.tgz",
- "integrity": "sha512-NhyOejdhRGS8Iwv+KKR2zTq2PpysF9XqY+Zk77vQHqNbo/PwZCzB5/h7VGuREZm1fixhs4Q/qWRSi5zmAiO4Fw==",
- "cpu": [
- "ppc64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/esbuild/node_modules/@esbuild/linux-riscv64": {
- "version": "0.25.0",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.0.tgz",
- "integrity": "sha512-5S/rbP5OY+GHLC5qXp1y/Mx//e92L1YDqkiBbO9TQOvuFXM+iDqUNG5XopAnXoRH3FjIUDkeGcY1cgNvnXp/kA==",
- "cpu": [
- "riscv64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/esbuild/node_modules/@esbuild/linux-s390x": {
- "version": "0.25.0",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.0.tgz",
- "integrity": "sha512-XM2BFsEBz0Fw37V0zU4CXfcfuACMrppsMFKdYY2WuTS3yi8O1nFOhil/xhKTmE1nPmVyvQJjJivgDT+xh8pXJA==",
- "cpu": [
- "s390x"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/esbuild/node_modules/@esbuild/linux-x64": {
- "version": "0.25.0",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.0.tgz",
- "integrity": "sha512-9yl91rHw/cpwMCNytUDxwj2XjFpxML0y9HAOH9pNVQDpQrBxHy01Dx+vaMu0N1CKa/RzBD2hB4u//nfc+Sd3Cw==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/esbuild/node_modules/@esbuild/netbsd-x64": {
- "version": "0.25.0",
- "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.0.tgz",
- "integrity": "sha512-jl+qisSB5jk01N5f7sPCsBENCOlPiS/xptD5yxOx2oqQfyourJwIKLRA2yqWdifj3owQZCL2sn6o08dBzZGQzA==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "netbsd"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/esbuild/node_modules/@esbuild/openbsd-arm64": {
- "version": "0.25.0",
- "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.0.tgz",
- "integrity": "sha512-21sUNbq2r84YE+SJDfaQRvdgznTD8Xc0oc3p3iW/a1EVWeNj/SdUCbm5U0itZPQYRuRTW20fPMWMpcrciH2EJw==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "openbsd"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/esbuild/node_modules/@esbuild/openbsd-x64": {
- "version": "0.25.0",
- "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.0.tgz",
- "integrity": "sha512-2gwwriSMPcCFRlPlKx3zLQhfN/2WjJ2NSlg5TKLQOJdV0mSxIcYNTMhk3H3ulL/cak+Xj0lY1Ym9ysDV1igceg==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "openbsd"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/esbuild/node_modules/@esbuild/sunos-x64": {
- "version": "0.25.0",
- "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.0.tgz",
- "integrity": "sha512-bxI7ThgLzPrPz484/S9jLlvUAHYMzy6I0XiU1ZMeAEOBcS0VePBFxh1JjTQt3Xiat5b6Oh4x7UC7IwKQKIJRIg==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "sunos"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/esbuild/node_modules/@esbuild/win32-arm64": {
- "version": "0.25.0",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.0.tgz",
- "integrity": "sha512-ZUAc2YK6JW89xTbXvftxdnYy3m4iHIkDtK3CLce8wg8M2L+YZhIvO1DKpxrd0Yr59AeNNkTiic9YLf6FTtXWMw==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/esbuild/node_modules/@esbuild/win32-ia32": {
- "version": "0.25.0",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.0.tgz",
- "integrity": "sha512-eSNxISBu8XweVEWG31/JzjkIGbGIJN/TrRoiSVZwZ6pkC6VX4Im/WV2cz559/TXLcYbcrDN8JtKgd9DJVIo8GA==",
- "cpu": [
- "ia32"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/esbuild/node_modules/@esbuild/win32-x64": {
- "version": "0.25.0",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.0.tgz",
- "integrity": "sha512-ZENoHJBxA20C2zFzh6AI4fT6RraMzjYw4xKWemRTRmRVtN9c5DcH9r/f2ihEkMjOW5eGgrwCslG/+Y/3bL+DHQ==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">=18"
+ "@esbuild/aix-ppc64": "0.28.1",
+ "@esbuild/android-arm": "0.28.1",
+ "@esbuild/android-arm64": "0.28.1",
+ "@esbuild/android-x64": "0.28.1",
+ "@esbuild/darwin-arm64": "0.28.1",
+ "@esbuild/darwin-x64": "0.28.1",
+ "@esbuild/freebsd-arm64": "0.28.1",
+ "@esbuild/freebsd-x64": "0.28.1",
+ "@esbuild/linux-arm": "0.28.1",
+ "@esbuild/linux-arm64": "0.28.1",
+ "@esbuild/linux-ia32": "0.28.1",
+ "@esbuild/linux-loong64": "0.28.1",
+ "@esbuild/linux-mips64el": "0.28.1",
+ "@esbuild/linux-ppc64": "0.28.1",
+ "@esbuild/linux-riscv64": "0.28.1",
+ "@esbuild/linux-s390x": "0.28.1",
+ "@esbuild/linux-x64": "0.28.1",
+ "@esbuild/netbsd-arm64": "0.28.1",
+ "@esbuild/netbsd-x64": "0.28.1",
+ "@esbuild/openbsd-arm64": "0.28.1",
+ "@esbuild/openbsd-x64": "0.28.1",
+ "@esbuild/openharmony-arm64": "0.28.1",
+ "@esbuild/sunos-x64": "0.28.1",
+ "@esbuild/win32-arm64": "0.28.1",
+ "@esbuild/win32-ia32": "0.28.1",
+ "@esbuild/win32-x64": "0.28.1"
}
},
"node_modules/escalade": {
@@ -13145,6 +13647,490 @@
"fsevents": "~2.3.3"
}
},
+ "node_modules/tsx/node_modules/@esbuild/aix-ppc64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz",
+ "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "aix"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/android-arm": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz",
+ "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/android-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz",
+ "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/android-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz",
+ "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/darwin-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz",
+ "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/darwin-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz",
+ "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz",
+ "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/freebsd-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz",
+ "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/linux-arm": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz",
+ "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/linux-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz",
+ "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/linux-ia32": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz",
+ "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/linux-loong64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz",
+ "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/linux-mips64el": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz",
+ "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==",
+ "cpu": [
+ "mips64el"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/linux-ppc64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz",
+ "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/linux-riscv64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz",
+ "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/linux-s390x": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz",
+ "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/linux-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz",
+ "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/netbsd-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz",
+ "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/netbsd-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz",
+ "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/openbsd-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz",
+ "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/openbsd-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz",
+ "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/openharmony-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz",
+ "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/sunos-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz",
+ "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/win32-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz",
+ "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/win32-ia32": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz",
+ "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/win32-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz",
+ "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/esbuild": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz",
+ "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "optionalDependencies": {
+ "@esbuild/aix-ppc64": "0.25.12",
+ "@esbuild/android-arm": "0.25.12",
+ "@esbuild/android-arm64": "0.25.12",
+ "@esbuild/android-x64": "0.25.12",
+ "@esbuild/darwin-arm64": "0.25.12",
+ "@esbuild/darwin-x64": "0.25.12",
+ "@esbuild/freebsd-arm64": "0.25.12",
+ "@esbuild/freebsd-x64": "0.25.12",
+ "@esbuild/linux-arm": "0.25.12",
+ "@esbuild/linux-arm64": "0.25.12",
+ "@esbuild/linux-ia32": "0.25.12",
+ "@esbuild/linux-loong64": "0.25.12",
+ "@esbuild/linux-mips64el": "0.25.12",
+ "@esbuild/linux-ppc64": "0.25.12",
+ "@esbuild/linux-riscv64": "0.25.12",
+ "@esbuild/linux-s390x": "0.25.12",
+ "@esbuild/linux-x64": "0.25.12",
+ "@esbuild/netbsd-arm64": "0.25.12",
+ "@esbuild/netbsd-x64": "0.25.12",
+ "@esbuild/openbsd-arm64": "0.25.12",
+ "@esbuild/openbsd-x64": "0.25.12",
+ "@esbuild/openharmony-arm64": "0.25.12",
+ "@esbuild/sunos-x64": "0.25.12",
+ "@esbuild/win32-arm64": "0.25.12",
+ "@esbuild/win32-ia32": "0.25.12",
+ "@esbuild/win32-x64": "0.25.12"
+ }
+ },
"node_modules/tsx/node_modules/fsevents": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
diff --git a/package.json b/package.json
index 9982c65a33..bee0177980 100644
--- a/package.json
+++ b/package.json
@@ -16,7 +16,7 @@
"ajv-cli": "^5.0.0",
"ajv-formats": "^2.1.1",
"chai": "^5.1.0",
- "esbuild": "^0.25.0",
+ "esbuild": "^0.28.1",
"eslint": "^8.39.0",
"eslint-config-love": "^53.0.0",
"eslint-plugin-import": "^2.27.5",
diff --git a/web/docs/engine/guide/adding-keyboards.md b/web/docs/engine/guide/adding-keyboards.md
index 957df80270..f294bee24b 100644
--- a/web/docs/engine/guide/adding-keyboards.md
+++ b/web/docs/engine/guide/adding-keyboards.md
@@ -8,7 +8,7 @@ There are multiple ways to add and install keyboards into your KeymanWeb install
The most efficient way to utilize a keyboard is to obtain a local copy of it and place this copy in a static location on your website. Once this is done, it can be directly linked into KeymanWeb as follows.
-```typescript
+```js
await keyman.addKeyboards({
id:'us', // The keyboard's unique identification code.
name:'English', // The keyboard's user-readable name.
@@ -22,7 +22,7 @@ await keyman.addKeyboards({
Custom fonts may also be utilized via the `language.font` property. For example:
-```typescript
+```js
font:{
family:'LaoWeb',
source:['../font/saysettha_web.ttf','../font/saysettha_web.woff','../font/saysettha_web.eot']
@@ -33,7 +33,7 @@ font:{
To obtain the default Keyman keyboard for a given language, call the following function.
-```typescript
+```js
await keyman.addKeyboardsForLanguage('Dzongkha');
```
@@ -41,7 +41,7 @@ This example would find the default keyboard for the Dzongkha language. This met
Alternatively, languages may be looked up via their BCP 47 language code as follows:
-```typescript
+```js
await keyman.addKeyboards('@he');
```
@@ -51,7 +51,7 @@ The `@` prefix indicates the use of the BCP 47 language code, which in this case
To obtain a specific keyboard by name or by keyboard name and language code as a pair, see the following:
-```typescript
+```js
await keyman.addKeyboards('french', 'sil_euro_latin@sv', 'sil_euro_latin@no');
```
diff --git a/web/docs/engine/guide/examples/control-by-control.md b/web/docs/engine/guide/examples/control-by-control.md
index b3b0b9b3a5..af41cf0cee 100644
--- a/web/docs/engine/guide/examples/control-by-control.md
+++ b/web/docs/engine/guide/examples/control-by-control.md
@@ -11,7 +11,7 @@ to open the test page.
Include the following script in the HEAD of your page:
-```js
+```html