From e9066b3d52cff0be51eb68cb570797f2ca098a24 Mon Sep 17 00:00:00 2001 From: Joshua Horton Date: Wed, 5 Feb 2025 14:00:41 +0700 Subject: [PATCH 01/76] change(ios): relocates the "enter foreground" kbd-reload trigger Addresses part of #12216 --- .../Classes/Keyboard/InputViewController.swift | 2 +- .../Classes/Keyboard/KeymanWebViewController.swift | 8 -------- .../Classes/MainViewController/MainViewController.swift | 8 ++++++++ 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Keyboard/InputViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/Keyboard/InputViewController.swift index 16acd10f9e..98f6290276 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Keyboard/InputViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Keyboard/InputViewController.swift @@ -589,7 +589,7 @@ open class InputViewController: UIInputViewController, KeymanWebDelegate { } // KeymanWebViewController maintenance methods - func reload() { + public func reload() { keymanWeb.reloadKeyboard() } diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Keyboard/KeymanWebViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/Keyboard/KeymanWebViewController.swift index a0c8c6d5e4..72822bbbfc 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Keyboard/KeymanWebViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Keyboard/KeymanWebViewController.swift @@ -72,14 +72,6 @@ class KeymanWebViewController: UIViewController { super.init(nibName: nil, bundle: nil) _ = view - - NotificationCenter.default.addObserver( - forName: UIApplication.willEnterForegroundNotification, - object: nil, - queue: OperationQueue.main - ) { _ in - self.reloadKeyboard() - } } required init?(coder aDecoder: NSCoder) { diff --git a/ios/keyman/Keyman/Keyman/Classes/MainViewController/MainViewController.swift b/ios/keyman/Keyman/Keyman/Classes/MainViewController/MainViewController.swift index 35ee78c5db..de6f00da63 100644 --- a/ios/keyman/Keyman/Keyman/Classes/MainViewController/MainViewController.swift +++ b/ios/keyman/Keyman/Keyman/Classes/MainViewController/MainViewController.swift @@ -112,6 +112,14 @@ class MainViewController: UIViewController, TextViewDelegate, UIActionSheetDeleg forName: Notifications.keyboardRemoved, observer: self, function: MainViewController.keyboardRemoved) + + NotificationCenter.default.addObserver( + forName: UIApplication.willEnterForegroundNotification, + object: nil, + queue: OperationQueue.main + ) { _ in + Manager.shared.inputViewController.reload() + } // Unfortunately, it's the main app with the file definitions. // We have to gerry-rig this so that the framework-based SettingsViewController From b8aaab6ea21caafc8cb4784664c93ca6280f737c Mon Sep 17 00:00:00 2001 From: Joshua Horton Date: Wed, 5 Feb 2025 14:01:18 +0700 Subject: [PATCH 02/76] change(ios): relocates keyboard hide+show notification handling Addresses part of #12216 --- .../Keyboard/InputViewController.swift | 10 +++++++- .../Keyboard/KeymanWebViewController.swift | 20 --------------- .../KMEI/KeymanEngine/Classes/Manager.swift | 25 +++++++++++++++++++ 3 files changed, 34 insertions(+), 21 deletions(-) diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Keyboard/InputViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/Keyboard/InputViewController.swift index 98f6290276..87794aac3b 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Keyboard/InputViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Keyboard/InputViewController.swift @@ -206,7 +206,7 @@ open class InputViewController: UIInputViewController, KeymanWebDelegate { keymanWeb = KeymanWebViewController(storage: Storage.active) super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) - var message = self.hasFullAccess ? "hasFullAccess: true" : "hasFullAccess: false" + let message = self.hasFullAccess ? "hasFullAccess: true" : "hasFullAccess: false" os_log("%{public}s", log: KeymanEngineLogger.settings, type: .default, message) SentryManager.breadcrumb(message) @@ -623,10 +623,18 @@ open class InputViewController: UIInputViewController, KeymanWebDelegate { func showHelpBubble() { keymanWeb.showHelpBubble() } + + func dismissHelpBubble() { + keymanWeb.dismissHelpBubble() + } func showHelpBubble(afterDelay delay: TimeInterval) { keymanWeb.showHelpBubble(afterDelay: delay) } + + internal func enforceKeyboardSize() { + keymanWeb.resizeKeyboard() + } func clearText() { setContextState(text: nil, range: NSRange(location: 0, length: 0)) diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Keyboard/KeymanWebViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/Keyboard/KeymanWebViewController.swift index 72822bbbfc..c0e7cefd0c 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Keyboard/KeymanWebViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Keyboard/KeymanWebViewController.swift @@ -146,11 +146,6 @@ class KeymanWebViewController: UIViewController { view = webView - NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillShow), - name: UIResponder.keyboardWillShowNotification, object: nil) - NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillHide), - name: UIResponder.keyboardWillHideNotification, object: nil) - reloadKeyboard() } @@ -982,18 +977,3 @@ extension KeymanWebViewController { return keyboardMenuView != nil } } - -// MARK: - Keyboard Notifications -extension KeymanWebViewController { - @objc func keyboardWillShow(_ notification: Notification) { - resizeKeyboard() - - if Manager.shared.isKeymanHelpOn { - showHelpBubble(afterDelay: 1.5) - } - } - - @objc func keyboardWillHide(_ notification: Notification) { - dismissHelpBubble() - } -} diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift b/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift index 4ab0da660b..25b1f143cb 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift @@ -239,10 +239,35 @@ public class Manager: NSObject, UIGestureRecognizerDelegate { SentryManager.capture(error, message:message) } } + + // Keep these within our singleton Manager instance. As we never remove these, + // setting them within something prone to replacement (like the keyboard when in + // app-extension mode) can lead to memory leaks. See #12216. + NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillShow), // + name: UIResponder.keyboardWillShowNotification, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillHide), // + name: UIResponder.keyboardWillHideNotification, object: nil) // We used to preload the old KeymanWebViewController, but now that it's embedded within the // InputViewController, that's not exactly viable. } + + // MARK: - Keyboard Notifications + // Do NOT place these anywhere within InputViewController or its children. + // So far as we can tell, the OS causes the app extension to sometimes throw + // instances away, and maintaining references via notification to them can + // cause memory leaks. Refer to #12216. + @objc func keyboardWillShow(_ notification: Notification) { + inputViewController?.enforceKeyboardSize() + + if Manager.shared.isKeymanHelpOn { + inputViewController?.showHelpBubble(afterDelay: 1.5) + } + } + + @objc func keyboardWillHide(_ notification: Notification) { + inputViewController?.dismissHelpBubble() + } // MARK: - Keyboard management From 95dae2079ea56b9e275228b12e063852d7af0436 Mon Sep 17 00:00:00 2001 From: Joshua Horton Date: Thu, 6 Feb 2025 14:34:10 +0700 Subject: [PATCH 03/76] change(web): drop 'dom' unit test concurrency when run in CI mode --- web/src/test/auto/dom/web-test-runner.CI.config.mjs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/web/src/test/auto/dom/web-test-runner.CI.config.mjs b/web/src/test/auto/dom/web-test-runner.CI.config.mjs index d18a8a9cb0..2ce3711078 100644 --- a/web/src/test/auto/dom/web-test-runner.CI.config.mjs +++ b/web/src/test/auto/dom/web-test-runner.CI.config.mjs @@ -9,5 +9,6 @@ export default { reporters: [ teamcityReporter(), /* custom-written, for CI-friendly reports */ sessionStabilityReporter({ciMode: true}) - ] + ], + concurrency: 1 } \ No newline at end of file From 3e456bbf3b0637d2abb20cee35e530124a85435e Mon Sep 17 00:00:00 2001 From: Joshua Horton Date: Fri, 7 Feb 2025 10:02:31 +0700 Subject: [PATCH 04/76] chore(web): top-level clean affects predictive-text child projects Addresses parts of #12735. --- web/build.sh | 2 +- web/src/engine/predictive-text/build.sh | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/web/build.sh b/web/build.sh index e6d9a5feb8..5dd30f338f 100755 --- a/web/build.sh +++ b/web/build.sh @@ -35,7 +35,7 @@ builder_describe "Builds engine modules for Keyman Engine for Web (KMW)." \ ":engine/keyboard-storage Subset used to collate keyboards and request them from the cloud" \ ":engine/main Builds all common code used by KMW's app/-level targets" \ ":engine/osk Builds the Web OSK module" \ - ":engine/predictive-text=src/engine/predictive-text/worker-main Builds KMW's predictive text module" \ + ":engine/predictive-text Builds KMW's predictive text module" \ ":help Online documentation" \ ":samples Builds all needed resources for the KMW sample-page set" \ ":tools Builds engine-related development resources" \ diff --git a/web/src/engine/predictive-text/build.sh b/web/src/engine/predictive-text/build.sh index 677d8b2269..015c0ea3be 100755 --- a/web/src/engine/predictive-text/build.sh +++ b/web/src/engine/predictive-text/build.sh @@ -53,7 +53,6 @@ builder_run_child_actions configure ## Build actions builder_run_child_actions build:wordbreakers - builder_run_child_actions build:templates builder_run_child_actions build:worker-thread builder_run_child_actions build:worker-main From 6cad46fcf01db149655bd64c3e18791764b70952 Mon Sep 17 00:00:00 2001 From: Joshua Horton Date: Fri, 7 Feb 2025 15:19:54 +0700 Subject: [PATCH 05/76] fix(ios): make KeyboardMenuView ref to ancestor `weak` --- .../Keyboard/InputViewController.swift | 24 +++++++++++++++---- .../Classes/Keyboard/KeyboardMenuView.swift | 4 +++- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Keyboard/InputViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/Keyboard/InputViewController.swift index 87794aac3b..7923e3eb93 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Keyboard/InputViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Keyboard/InputViewController.swift @@ -165,6 +165,8 @@ open class InputViewController: UIInputViewController, KeymanWebDelegate { // For now, should be mostly upon keymanWeb.view.heightAnchor. var portraitConstraint: NSLayoutConstraint? var landscapeConstraint: NSLayoutConstraint? + + var outerWidthConstraint: NSLayoutConstraint? private var keymanWeb: KeymanWebViewController private var swallowBackspaceTextChange: Bool = false @@ -300,6 +302,16 @@ open class InputViewController: UIInputViewController, KeymanWebDelegate { keymanWeb.shouldReload = true } } + + open override func viewDidDisappear(_ animated: Bool) { + super.viewDidDisappear(animated) + + if outerWidthConstraint != nil { + outerWidthConstraint?.isActive = false + self.inputView?.removeConstraint(self.outerWidthConstraint!) + outerWidthConstraint = nil + } + } open override func textDidChange(_ textInput: UITextInput?) { // Swallows self-triggered calls from emptying the context due to keyboard rules @@ -527,11 +539,15 @@ open class InputViewController: UIInputViewController, KeymanWebDelegate { } // These require the view to appear - parent and our relationship with it must exist! + // ... wait, is THIS possibly a leak source? private func setOuterConstraints() { - var baseWidthConstraint: NSLayoutConstraint - baseWidthConstraint = self.inputView!.widthAnchor.constraint(equalTo: parent!.view.safeAreaLayoutGuide.widthAnchor) - baseWidthConstraint.priority = UILayoutPriority(rawValue: 999) - baseWidthConstraint.isActive = true + guard outerWidthConstraint == nil else { + outerWidthConstraint!.isActive = true + return + } + outerWidthConstraint = self.inputView!.widthAnchor.constraint(equalTo: parent!.view.safeAreaLayoutGuide.widthAnchor) + outerWidthConstraint!.priority = UILayoutPriority(rawValue: 999) + outerWidthConstraint!.isActive = true } public var kmwHeight: CGFloat { diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Keyboard/KeyboardMenuView.swift b/ios/engine/KMEI/KeymanEngine/Classes/Keyboard/KeyboardMenuView.swift index 397697222c..1823b143b3 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Keyboard/KeyboardMenuView.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Keyboard/KeyboardMenuView.swift @@ -26,7 +26,9 @@ class KeyboardMenuView: UIView, UITableViewDelegate, UITableViewDataSource, UIGe private var tableView: UITableView? private let closeButtonTitle: String? - private var _inputViewController: InputViewController? + // Is populated during init from a weak ref by KeymanWebViewController, + // and instances of this class are owned by the same. + private weak var _inputViewController: InputViewController? override var inputViewController: InputViewController? { return _inputViewController } From f36c54b6c641c539ab233529b6c61ab03ea4f4ca Mon Sep 17 00:00:00 2001 From: Joshua Horton Date: Mon, 10 Feb 2025 14:55:04 +0700 Subject: [PATCH 06/76] change(ios): mitigate keyboard input-view memory leak by force-discarding WebView Related-to: #12216 --- .../Keyboard/InputViewController.swift | 60 ++++++++++++------- .../Keyboard/KeymanWebViewController.swift | 12 ++++ .../KMEI/KeymanEngine/Classes/Manager.swift | 1 + 3 files changed, 51 insertions(+), 22 deletions(-) diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Keyboard/InputViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/Keyboard/InputViewController.swift index 7923e3eb93..a51db67141 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Keyboard/InputViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Keyboard/InputViewController.swift @@ -41,6 +41,16 @@ private class CustomInputView: UIInputView, UIInputViewAudioFeedback { super.init(coder: coder) } + public func destroy() { + // In app-extension mode, there are scenarios in which this class does not properly + // deallocate! We need to help that process along. In particular, doing this allows + // us to guarantee that the WebView is allowed to be GC'd, even when Apple fails to GC + // this (`CustomInputView`) instance - which actually happens. (Refer to #12216.) + keymanWeb.removeFromParent() + keymanWeb.destroy() + keymanWeb = nil + } + public var enableInputClicksWhenVisible: Bool { get { // Implemented as noted by https://developer.apple.com/documentation/uikit/uidevice/1620050-playinputclick. @@ -98,37 +108,37 @@ private class CustomInputView: UIInputView, UIInputViewAudioFeedback { */ func keyboardHeightChanged() { os_log("CustomInputView keyboardHeightChanged", log: KeymanEngineLogger.ui, type: .info) - + // deactivate constraints for both orientations (though one should already be inactive) landscapeConstraint?.isActive = false portraitConstraint?.isActive = false - + // rebuild both portrait and landscape constraints self.buildKeyboardHeightConstraints(bannerHeight: InputViewController.topBarHeight) - + // activate constraints for the current orientation if InputViewController.isPortrait { portraitConstraint?.isActive = true } else { landscapeConstraint?.isActive = true } - + self.setNeedsLayout() } - + private func buildKeyboardHeightConstraints(bannerHeight: CGFloat) { os_log("CustomInputView buildKeyboardHeightConstraints", log: KeymanEngineLogger.ui, type: .info) let innerView = keymanWeb.view! - + // Cannot be met by the in-app keyboard, but helps to 'force' height for the system keyboard. let portraitHeightConstraint = innerView.heightAnchor.constraint(equalToConstant: bannerHeight + keymanWeb.readKeyboardHeight(isPortrait: true)!) portraitHeightConstraint.identifier = "Height constraint for portrait mode" portraitHeightConstraint.priority = .defaultHigh - + let landscapeHeightConstraint = innerView.heightAnchor.constraint(equalToConstant: bannerHeight + keymanWeb.readKeyboardHeight(isPortrait: false)!) landscapeHeightConstraint.identifier = "Height constraint for landscape mode" landscapeHeightConstraint.priority = .defaultHigh - + portraitConstraint = portraitHeightConstraint landscapeConstraint = landscapeHeightConstraint // .isActive will be set according to the current portrait/landscape perspective. @@ -165,7 +175,7 @@ open class InputViewController: UIInputViewController, KeymanWebDelegate { // For now, should be mostly upon keymanWeb.view.heightAnchor. var portraitConstraint: NSLayoutConstraint? var landscapeConstraint: NSLayoutConstraint? - + var outerWidthConstraint: NSLayoutConstraint? private var keymanWeb: KeymanWebViewController @@ -219,6 +229,12 @@ open class InputViewController: UIInputViewController, KeymanWebDelegate { fatalError("init(coder:) has not been implemented") } + deinit { + inputView?.removeFromSuperview() + (inputView as? CustomInputView)?.destroy() + inputView = nil + } + open override func updateViewConstraints() { resetKeyboardState() @@ -302,10 +318,10 @@ open class InputViewController: UIInputViewController, KeymanWebDelegate { keymanWeb.shouldReload = true } } - + open override func viewDidDisappear(_ animated: Bool) { super.viewDidDisappear(animated) - + if outerWidthConstraint != nil { outerWidthConstraint?.isActive = false self.inputView?.removeConstraint(self.outerWidthConstraint!) @@ -380,7 +396,7 @@ open class InputViewController: UIInputViewController, KeymanWebDelegate { } } } - + func deleteSelection() -> Bool { if let selected = textDocumentProxy.selectedText, selected.count > 0 { /* @@ -393,23 +409,23 @@ open class InputViewController: UIInputViewController, KeymanWebDelegate { /* We have a problem to resolve here: we cannot simply delete the selection with either .deleteBackward() or .insertText(""): - + - If there is selected text immediately following a space (U+0020), .deleteBackward() will delete that space IN ADDITION to the selected text. - Unlike .insertText("-any-string-here"), .insertText("") does nothing; it does not replace the selection with the new string. - + Our policy (#9073) on handling the backspace key when there is a text selection is to just delete the selection. We have to override the special case of space being deleted by .deleteBackward() ourselves. Additionally, the internal Web engine cannot anticipate the special case and requires precise and consistent backspace handling in line with our policy in order to keep the context on both sides synchronized. - + As .insertText() does not delete the selection if the string to be inserted is empty, we insert something that won't combine, like a ZWNJ, and then delete it. - + iOS does not allow users to select text in a way that splits character clusters. This implies that it's impossible for an inserted ZWNJ to combine with existing context, making this @@ -417,14 +433,14 @@ open class InputViewController: UIInputViewController, KeymanWebDelegate { */ textDocumentProxy.insertText("\u{200c}") textDocumentProxy.deleteBackward() - + let afterManipulation = textDocumentProxy.documentContextBeforeInput ?? "" - + // And now to finish our 'canary' check. if beforeManipulation != afterManipulation { os_log(.error, log: KeymanEngineLogger.engine, "Could not cleanly execute backspace for selected text") } - + return true } return false @@ -576,7 +592,7 @@ open class InputViewController: UIInputViewController, KeymanWebDelegate { customInputView.keyboardHeightChanged() } } - + func fixLayout() { view.setNeedsLayout() view.layoutIfNeeded() @@ -639,7 +655,7 @@ open class InputViewController: UIInputViewController, KeymanWebDelegate { func showHelpBubble() { keymanWeb.showHelpBubble() } - + func dismissHelpBubble() { keymanWeb.dismissHelpBubble() } @@ -647,7 +663,7 @@ open class InputViewController: UIInputViewController, KeymanWebDelegate { func showHelpBubble(afterDelay delay: TimeInterval) { keymanWeb.showHelpBubble(afterDelay: delay) } - + internal func enforceKeyboardSize() { keymanWeb.resizeKeyboard() } diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Keyboard/KeymanWebViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/Keyboard/KeymanWebViewController.swift index c0e7cefd0c..7e18751440 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Keyboard/KeymanWebViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Keyboard/KeymanWebViewController.swift @@ -173,6 +173,18 @@ class KeymanWebViewController: UIViewController { // MARK: - JavaScript functions extension KeymanWebViewController { + // In app-extension mode, there are scenarios in which this class does not properly + // deallocate! We need to help that process along. + func destroy() { + // Message handlers can maintain references. + if #available(iOSApplicationExtension 14.0, *) { + self.userContentController.removeAllScriptMessageHandlers() + } else { + self.userContentController.removeScriptMessageHandler(forName: keymanWebViewName) + } + view = nil + } + func languageMenuPosition(_ completion: @escaping (CGRect) -> Void) { webView!.evaluateJavaScript("langMenuPos();") { result, _ in guard let result = result as? String, !result.isEmpty else { diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift b/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift index 25b1f143cb..adfdad596c 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift @@ -165,6 +165,7 @@ public class Manager: NSObject, UIGestureRecognizerDelegate { } set(value) { + // The system keyboard will set itself here when its `viewDidAppear` triggers. _inputViewController = value } } From 342f216291a5608162098c3940f953bb44a6c493 Mon Sep 17 00:00:00 2001 From: Marc Durdin Date: Tue, 11 Feb 2025 14:14:15 +0700 Subject: [PATCH 07/76] chore: increment to alpha 19.0 --- VERSION.md | 2 +- android/docs/engine/guides/in-app/index.md | 6 ++-- android/docs/engine/index.md | 2 +- android/docs/engine/whatsnew.md | 7 +--- android/docs/help/about/whatsnew.md | 7 ++-- android/docs/help/index.md | 2 +- developer/docs/help/index.md | 4 +-- developer/docs/help/whats-new.md | 42 ++-------------------- developer/src/kmc/src/kmlmp.ts | 2 +- docs/minimum-versions.md | 2 +- docs/minimum-versions.md.in | 2 +- ios/docs/engine/index.md | 2 +- ios/docs/help/about/whatsnew.md | 7 ++-- ios/docs/help/index.md | 2 +- linux/docs/help/about/whatsnew.md | 4 +-- linux/docs/help/index.md | 2 +- mac/docs/help/about/whatsnew.md | 4 +-- mac/docs/help/index.md | 2 +- resources/build/minimum-versions.inc.sh | 2 +- web/docs/engine/guide/index.md | 6 ++-- web/docs/engine/index.md | 12 +++---- web/docs/engine/reference/core/version.md | 2 +- web/docs/engine/reference/index.md | 2 +- web/docs/engine/whats-new.md | 2 +- windows/docs/engine/api/index.md | 6 ++-- windows/docs/engine/index.md | 4 +-- windows/docs/help/about/whatsnew.md | 4 +-- windows/docs/help/index.md | 2 +- 28 files changed, 47 insertions(+), 96 deletions(-) diff --git a/VERSION.md b/VERSION.md index b9cdf8a8b6..b2c7e01f85 100644 --- a/VERSION.md +++ b/VERSION.md @@ -1 +1 @@ -18.0.189 \ No newline at end of file +19.0.1 \ No newline at end of file diff --git a/android/docs/engine/guides/in-app/index.md b/android/docs/engine/guides/in-app/index.md index d788e267bb..538094ddbb 100644 --- a/android/docs/engine/guides/in-app/index.md +++ b/android/docs/engine/guides/in-app/index.md @@ -6,8 +6,8 @@ Keyman Engine for Android allows you to use any Keyman touch keyboard in your An system keyboard app for purchase in the Play Store.
This guide will walk you through the steps for creating your first Android app with Keyman Engine for Android. -If you are not familiar with Android development, you will find the -[Android Developer online training](https://developer.android.com/training/index.html) an invaluable +If you are not familiar with Android development, you will find the +[Android Developer online training](https://developer.android.com/training/index.html) an invaluable resource, and working through some of their tutorials first will help you with the rest of this guide. ### 1. Install Free Tools @@ -131,5 +131,5 @@ And there you have it: your first Keyman Engine for Android app! ## See Also * [Guide: Build a system keyboard app](../system-keyboard/) * [Keyman Developer Documentation](/developer/17.0/) -* [Keyman Engine for Android Documentation](/developer/engine/android/18.0/) +* [Keyman Engine for Android Documentation](/developer/engine/android/19.0/) * [Android Developer Home](https://developer.android.com/index.html) diff --git a/android/docs/engine/index.md b/android/docs/engine/index.md index fa549e4b34..38f255592b 100644 --- a/android/docs/engine/index.md +++ b/android/docs/engine/index.md @@ -4,7 +4,7 @@ title: Keyman Engine for Android ## Overview -Keyman Engine for Android 18.0 is a Java library for Android 5.0 and later versions which enables a fully customisable keyboard layout, both within an app and system-wide. +Keyman Engine for Android 19.0 is a Java library for Android 5.0 and later versions which enables a fully customisable keyboard layout, both within an app and system-wide. Keyboard layouts for Keyman Engine can be created with [Keyman Developer](/developer/17.0), and a [library of existing keyboard layouts](http://keyman.com/developer/keymanweb/keyboards) is also available.

diff --git a/android/docs/engine/whatsnew.md b/android/docs/engine/whatsnew.md index af3f8932d6..5d6f717f8a 100644 --- a/android/docs/engine/whatsnew.md +++ b/android/docs/engine/whatsnew.md @@ -1,11 +1,6 @@ --- -title: What's New in Keyman Engine 18.0 for Android +title: What's New in Keyman Engine 19.0 for Android --- -## Keyman Engine for Android Breaking Changes: ## - -* Change package name from `com.tavultesoft.kmea` to `com.keyman.engine` #7881 -* Update to Java 11 #8543 - ## See Also * [Keyman Engine for Android Documentation](index) \ No newline at end of file diff --git a/android/docs/help/about/whatsnew.md b/android/docs/help/about/whatsnew.md index 0f0e891e42..d0ca85e93f 100644 --- a/android/docs/help/about/whatsnew.md +++ b/android/docs/help/about/whatsnew.md @@ -1,9 +1,6 @@ --- -title: What's New in Keyman 18.0 for Android +title: What's New in Keyman 19.0 for Android --- -Here are some of the new features we have added to Keyman 18.0 for Android: +Here are some of the new features we have added to Keyman 19.0 for Android: -* New menu to adjust longpress delay time (#12170, #12185) -* Support localizations for right-to-left languages (#12215) -* Handle additional actions for ENTER key (#12125, #12315) diff --git a/android/docs/help/index.md b/android/docs/help/index.md index 62df93195b..360af91e6d 100644 --- a/android/docs/help/index.md +++ b/android/docs/help/index.md @@ -1,5 +1,5 @@ --- -title: Keyman for Android 18.0 Help +title: Keyman for Android 19.0 Help --- ## [About Keyman](about/) diff --git a/developer/docs/help/index.md b/developer/docs/help/index.md index 9e9c250abb..d7fbc4098d 100644 --- a/developer/docs/help/index.md +++ b/developer/docs/help/index.md @@ -1,5 +1,5 @@ --- -title: Keyman Developer 18.0 User Guide +title: Keyman Developer 19.0 User Guide --- Need help using Keyman Developer to create your keyboard layouts? You'll @@ -8,7 +8,7 @@ and tutorials, and full reference information. ## Guides and Tutorials - [What is Keyman Developer?](guides/intro) -- [What's new in 18.0](whats-new) +- [What's new in 19.0](whats-new) - [Developing Keyman keyboard layouts](guides/develop) - [Testing Keyman keyboards](guides/test) - [Distributing Keyman keyboards](guides/distribute) diff --git a/developer/docs/help/whats-new.md b/developer/docs/help/whats-new.md index d1dce092c3..1005476c3d 100644 --- a/developer/docs/help/whats-new.md +++ b/developer/docs/help/whats-new.md @@ -1,43 +1,5 @@ --- -title: What's new in Keyman Developer 18.0 +title: What's new in Keyman Developer 19.0 --- -Keyman Developer 17 has a number of significant changes: - -* `kmcomp` has been removed and replaced with `kmc`. Learn more in our [kmcomp migration guide](reference/kmc/cli/kmcomp-migration). - -* Windows installer packages should now be built with - `kmc build windows-package-installer` and can no longer be built within - the Keyman Developer IDE. More details on how to use this are in the - [`kmc` reference documentation](reference/kmc/cli/reference#toc-kmc-build-windows-package-installer-additional-options). - -* `.keyboard_info` files are now generated entirely from the package and - keyboard files. Extra fields are available in packages for license file, - welcome file, typing examples, related packages (including deprecated - packages), and additional font files. Keyboards in the Keyman Cloud - keyboard repository have already been updated; if you are an author of - one of these keyboards, you should pull the changes from the repository - before submitting future updates. - -* Keyboard project files (.kpj) now have a new format which does not list - individual files, but instead includes all files in the same folder and - subfolders relative to the project file. This prevents projects from - including unrelated files, and reduces the number of files that change - when keyboards are updated. - -* Keyman Developer will now open each project in a new window, allowing - the user to rapidly switch between multiple projects at the same time. - -* The Keyman Keyboard and Lexical Model Cloud repositories have new build - scripts which run `kmc`. These run much, much faster than previously! - -* The [`&displayMap`](/developer/language/reference/displaymap) store allows - keyboard developers to specify a font mapping to PUA for the On Screen - Keyboard and Touch Layout to resolve diacritic rendering issues. - -* Keyman Developer has been updated to support Unicode 15.1. - -* Additional non-printing characters have been added to the Touch Layout Editor. - -* Virtual keys in output of rules have never worked properly or been officially - supported; Keyman Developer will now warn if you attempt to use this pattern. +Keyman Developer 19 has the following significant changes: diff --git a/developer/src/kmc/src/kmlmp.ts b/developer/src/kmc/src/kmlmp.ts index bb0cda5e53..a1346ac0d3 100644 --- a/developer/src/kmc/src/kmlmp.ts +++ b/developer/src/kmc/src/kmlmp.ts @@ -3,7 +3,7 @@ * kmlmp - Keyman Lexical Model Package Compiler */ -// Note: this is a deprecated package and will be removed in Keyman 18.0 +// Note: this is a deprecated package and will be removed in Keyman 19.0 import { Command } from 'commander'; import { KmpCompiler } from '@keymanapp/kmc-package'; diff --git a/docs/minimum-versions.md b/docs/minimum-versions.md index 174f4b518e..beb542e408 100644 --- a/docs/minimum-versions.md +++ b/docs/minimum-versions.md @@ -4,7 +4,7 @@ title: Keyman Minimum Versions Changes in [minimum-versions.inc.sh](minimum-versions.inc.sh) should manually be propagated to the linked help files below: -## Keyman 18.0 +## Keyman 19.0 Target Operating System and Platform Versions diff --git a/docs/minimum-versions.md.in b/docs/minimum-versions.md.in index ffe508e12f..41a664d454 100644 --- a/docs/minimum-versions.md.in +++ b/docs/minimum-versions.md.in @@ -4,7 +4,7 @@ title: Keyman Minimum Versions Changes in [minimum-versions.inc.sh](minimum-versions.inc.sh) should manually be propagated to the linked help files below: -## Keyman 18.0 +## Keyman 19.0 Target Operating System and Platform Versions diff --git a/ios/docs/engine/index.md b/ios/docs/engine/index.md index b87af4e260..bdba88b741 100644 --- a/ios/docs/engine/index.md +++ b/ios/docs/engine/index.md @@ -4,7 +4,7 @@ title: Keyman for iPhone and iPad Developer Support ## Overview -The Keyman Engine for iPhone and iPad 18.0 SDK is designed to provide +The Keyman Engine for iPhone and iPad 19.0 SDK is designed to provide advanced international keyboard support to iOS apps. As a developer, you simply need to use (or subclass) TextView or diff --git a/ios/docs/help/about/whatsnew.md b/ios/docs/help/about/whatsnew.md index aa4cf6b333..b123b1f23c 100644 --- a/ios/docs/help/about/whatsnew.md +++ b/ios/docs/help/about/whatsnew.md @@ -1,8 +1,5 @@ --- -title: What's New in Keyman 18.0 for iPhone and iPad +title: What's New in Keyman 19.0 for iPhone and iPad --- -Here are some of the new features we have added to Keyman for iPhone and iPad 18.0: -Additional changes: - -* Performance improvements +Here are some of the new features we have added to Keyman for iPhone and iPad 19.0: diff --git a/ios/docs/help/index.md b/ios/docs/help/index.md index 994bbbda9e..cd0f81300e 100644 --- a/ios/docs/help/index.md +++ b/ios/docs/help/index.md @@ -1,5 +1,5 @@ --- -title: Keyman for iPhone and iPad 18.0 Help +title: Keyman for iPhone and iPad 19.0 Help --- ## [About Keyman](about/) diff --git a/linux/docs/help/about/whatsnew.md b/linux/docs/help/about/whatsnew.md index 4a4d5a30e1..5521261b96 100644 --- a/linux/docs/help/about/whatsnew.md +++ b/linux/docs/help/about/whatsnew.md @@ -1,5 +1,5 @@ --- -title: What's New in Keyman 18.0 for Linux +title: What's New in Keyman 19.0 for Linux --- -Here are some of the new features we have added to Keyman for Linux 18.0: +Here are some of the new features we have added to Keyman for Linux 19.0: diff --git a/linux/docs/help/index.md b/linux/docs/help/index.md index 58d245063a..329c66f69b 100644 --- a/linux/docs/help/index.md +++ b/linux/docs/help/index.md @@ -1,5 +1,5 @@ --- -title: Keyman for Linux 18.0 Help +title: Keyman for Linux 19.0 Help --- Need help using Keyman for Linux? In time, this product documentation will grow and explain frequently asked questions. diff --git a/mac/docs/help/about/whatsnew.md b/mac/docs/help/about/whatsnew.md index 73ccb4d49b..8cc29bdadb 100644 --- a/mac/docs/help/about/whatsnew.md +++ b/mac/docs/help/about/whatsnew.md @@ -1,5 +1,5 @@ --- -title: What's New in Keyman 18.0 for macOS +title: What's New in Keyman 19.0 for macOS --- -Here are some of the new features we have added to Keyman 18.0 for macOS: +Here are some of the new features we have added to Keyman 19.0 for macOS: diff --git a/mac/docs/help/index.md b/mac/docs/help/index.md index aa507a531e..12397a903d 100644 --- a/mac/docs/help/index.md +++ b/mac/docs/help/index.md @@ -1,5 +1,5 @@ --- -title: Keyman 18.0 for macOS Help +title: Keyman 19.0 for macOS Help --- Need help using Keyman for macOS? You'll find everything you need here, including product documentation, diff --git a/resources/build/minimum-versions.inc.sh b/resources/build/minimum-versions.inc.sh index 41a7c3690f..f57ebff419 100644 --- a/resources/build/minimum-versions.inc.sh +++ b/resources/build/minimum-versions.inc.sh @@ -1,4 +1,4 @@ -# Required minimum versions as of Keyman 18.0 +# Required minimum versions as of Keyman 19.0 # # This is a list of minimum, maximum, and specific versions of any external # components or dependencies found in Keyman. diff --git a/web/docs/engine/guide/index.md b/web/docs/engine/guide/index.md index 26e2e42b47..4f59a4ebf0 100644 --- a/web/docs/engine/guide/index.md +++ b/web/docs/engine/guide/index.md @@ -1,8 +1,8 @@ --- -title: KeymanWeb 18.0 - The Guide +title: KeymanWeb - The Guide --- -Welcome to the guide for using KeymanWeb 18.0. +Welcome to the guide for using KeymanWeb. - [Getting Started with Keyman Web](get-started) - [User Interface Design](user-interface-design) @@ -10,4 +10,4 @@ Welcome to the guide for using KeymanWeb 18.0. - [Installing Keyboards](adding-keyboards) - [Additional Examples](examples/) -[Return to the main KeymanWeb 18.0 page](../) \ No newline at end of file +[Return to the main KeymanWeb page](../) \ No newline at end of file diff --git a/web/docs/engine/index.md b/web/docs/engine/index.md index 63d14e2fd1..5515112f88 100644 --- a/web/docs/engine/index.md +++ b/web/docs/engine/index.md @@ -1,27 +1,27 @@ --- -title: Keyman Engine for Web 18.0 Developer Help +title: Keyman Engine for Web 19.0 Developer Help --- -Keyman Engine for Web 18.0 is the current version of KeymanWeb and +Keyman Engine for Web 19.0 is the current version of KeymanWeb and supports touch devices with custom touch-layouts as well as desktop computer browsers. [Download](https://keyman.com/developer/keymanweb/) -: Downloading Keyman Engine for Web 18.0 +: Downloading Keyman Engine for Web [Guide](guide/) -: Keyman Engine for Web 18.0 Guide +: Keyman Engine for Web Guide [Reference](reference) -: Keyman Engine for Web 18.0 Developer Reference +: Keyman Engine for Web Developer Reference [What's New](whats-new) -: What's new in Keyman Engine for Web 18.0 +: What's new in Keyman Engine for Web 19.0 diff --git a/web/docs/engine/reference/core/version.md b/web/docs/engine/reference/core/version.md index 7df0978ac8..40b9db4529 100644 --- a/web/docs/engine/reference/core/version.md +++ b/web/docs/engine/reference/core/version.md @@ -22,7 +22,7 @@ Read only ### Return Value -`'18.0'` (for KeymanWeb 18.0) +`'19.0'` (for KeymanWeb 19.0) ## Description diff --git a/web/docs/engine/reference/index.md b/web/docs/engine/reference/index.md index 74d83c3931..f6e5a3a8de 100644 --- a/web/docs/engine/reference/index.md +++ b/web/docs/engine/reference/index.md @@ -1,5 +1,5 @@ --- -title: KeymanWeb 18.0 Reference +title: KeymanWeb Reference --- [KeymanWeb Overview](overview) diff --git a/web/docs/engine/whats-new.md b/web/docs/engine/whats-new.md index 49fa337fe0..e220fb9c13 100644 --- a/web/docs/engine/whats-new.md +++ b/web/docs/engine/whats-new.md @@ -1,3 +1,3 @@ --- -title: What's New in KeymanWeb 18.0 +title: What's New in KeymanWeb 19.0 --- diff --git a/windows/docs/engine/api/index.md b/windows/docs/engine/api/index.md index 7032aa0cd2..f95332c45a 100644 --- a/windows/docs/engine/api/index.md +++ b/windows/docs/engine/api/index.md @@ -1,13 +1,13 @@ --- -title: Keyman Engine for Windows 18.0 API +title: Keyman Engine for Windows API --- ## Introduction -The Keyman Engine for Windows 18.0 API is implemented in COM. It can be +The Keyman Engine for Windows API is implemented in COM. It can be instantiated with `CreateObject("keymanapi.Keyman")`. -> [!Note] +> [!Note] > This documentation applies to Keyman Engine for Windows versions 14.0 and up. ## Interface Hierarchy diff --git a/windows/docs/engine/index.md b/windows/docs/engine/index.md index acdd77d877..0687433412 100644 --- a/windows/docs/engine/index.md +++ b/windows/docs/engine/index.md @@ -1,8 +1,8 @@ --- -title: Keyman Engine for Windows 18.0 +title: Keyman Engine for Windows --- -Keyman Engine for Windows 18.0 gives you the tools to build a customised +Keyman Engine for Windows gives you the tools to build a customised desktop keyboarding product for Windows. **Note:** This documentation applies to Keyman Engine for Windows diff --git a/windows/docs/help/about/whatsnew.md b/windows/docs/help/about/whatsnew.md index 4f11c82ec2..ee917f3870 100644 --- a/windows/docs/help/about/whatsnew.md +++ b/windows/docs/help/about/whatsnew.md @@ -1,8 +1,8 @@ --- -title: What's New in Keyman 18.0 for Windows +title: What's New in Keyman 19.0 for Windows --- -Here are some of the new features we have added to Keyman 18.0 for Windows: +Here are some of the new features we have added to Keyman 19.0 for Windows: ## Related Topics diff --git a/windows/docs/help/index.md b/windows/docs/help/index.md index c7cccc7695..231ed5fc7d 100644 --- a/windows/docs/help/index.md +++ b/windows/docs/help/index.md @@ -1,5 +1,5 @@ --- -title: Keyman for Windows 18.0 Help +title: Keyman for Windows 19.0 Help --- Need help using Keyman for Windows? You'll find everything you need here, including product documentation, From 7ab4c67383dac487e23818b9aebd6ad753ec354b Mon Sep 17 00:00:00 2001 From: Keyman Build Agent Date: Tue, 11 Feb 2025 02:52:18 -0500 Subject: [PATCH 08/76] auto: increment master version to 19.0.2 --- HISTORY.md | 5 +++++ VERSION.md | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/HISTORY.md b/HISTORY.md index ca5fe2101f..e907cc12dd 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,5 +1,10 @@ # Keyman Version History +## 19.0.1 alpha 2025-02-11 + +* refactor(windows): rename `TKeymanMutex.MutexOwned` to `TakeOwnership` and add `ReleaseOwnership` (#13168) +* chore: increment to alpha 19.0 (#13187) + ## 18.0.188 alpha 2025-02-10 * fix(windows): check the params status flag equals ucrsUpdateReady before attempting to download the keyman setup file (#13154) diff --git a/VERSION.md b/VERSION.md index b2c7e01f85..fc1a009252 100644 --- a/VERSION.md +++ b/VERSION.md @@ -1 +1 @@ -19.0.1 \ No newline at end of file +19.0.2 \ No newline at end of file From f2747be553bfc587226f847fb4ad193e839315e7 Mon Sep 17 00:00:00 2001 From: Eberhard Beilharz Date: Tue, 11 Feb 2025 19:24:22 +0100 Subject: [PATCH 09/76] chore(linux): remove support of Ubuntu 20.04 Focal Ubuntu 20.04 Focal will reach EOL in April and GitHub removes the runner images, so we no longer will be able to build packages for Focal. --- .github/workflows/deb-packaging.yml | 2 +- linux/scripts/launchpad.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/deb-packaging.yml b/.github/workflows/deb-packaging.yml index 52c485aa65..bf1bfca2fa 100644 --- a/.github/workflows/deb-packaging.yml +++ b/.github/workflows/deb-packaging.yml @@ -116,7 +116,7 @@ jobs: strategy: fail-fast: true matrix: - dist: [focal, jammy, noble, oracular] + dist: [jammy, noble, oracular] steps: - name: Checkout diff --git a/linux/scripts/launchpad.sh b/linux/scripts/launchpad.sh index 98ab9238f0..4b89980994 100755 --- a/linux/scripts/launchpad.sh +++ b/linux/scripts/launchpad.sh @@ -33,7 +33,7 @@ else fi echo "ppa: ${ppa}" -distributions="${DIST:-focal jammy noble oracular plucky}" +distributions="${DIST:-jammy noble oracular plucky}" packageversion="${PACKAGEVERSION:-1~sil1}" BASEDIR=$(pwd) From 02a3a1a66c121c1e42535ca943ec5a8b6c6d7f17 Mon Sep 17 00:00:00 2001 From: Eberhard Beilharz Date: Tue, 11 Feb 2025 19:32:29 +0100 Subject: [PATCH 10/76] chore(linux): cleanup code Now that we no longer support Focal we can use more modern tools for packaging. --- docs/linux/README.md | 2 +- linux/debian/rules | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/docs/linux/README.md b/docs/linux/README.md index 80c1a78434..64ef6c967f 100644 --- a/docs/linux/README.md +++ b/docs/linux/README.md @@ -147,7 +147,7 @@ Run `ibus restart` after installing any of them. Keyman tries to activate the keyboard automatically. If you want to activate it for a different language, you can do so by following the steps below. -#### GNOME3 (focal and bionic default, also newer Ubuntu versions) +#### GNOME3 (Ubuntu default) - Click the connection/sound/shutdown section in the top right. Then the tools icon for Settings. diff --git a/linux/debian/rules b/linux/debian/rules index 8da37f40e4..5138b58330 100755 --- a/linux/debian/rules +++ b/linux/debian/rules @@ -66,10 +66,7 @@ override_dh_auto_install: rm $(CURDIR)/debian/keyman/usr/share/locale/*.po* # Don't call `build.sh install` - dh_auto_install does some extra smarts dh_auto_install --sourcedir=linux/keyman-config --buildsystem=pybuild $@ - # Unfortunately bash-completion 2.10 (focal) doesn't yet provide dh-sequence-bash-completion, - # which we could add as build-dependency, so we'll have to explicitly call - # dh_bash_completion here instead of using `dh $@ --with-python3 --with bash-completion` - dh_bash-completion -O--buildsystem=pybuild + dh $@ --with-python3 --with bash-completion dh_python3 -O--buildsystem=pybuild override_dh_missing: From 0e48e44d2deb33cf3ab7123279ceb46cd5f523e6 Mon Sep 17 00:00:00 2001 From: Marc Durdin Date: Wed, 12 Feb 2025 13:14:42 +0700 Subject: [PATCH 11/76] chore(developer): add INFO messages for `kmc copy` and `kmc generate` Note: these messages are in `kmc`, not in `kmc-copy` or `kmc-generate`, matching the structure for existing `INFO_BuildingProject` and related messages. Fixes: #13127 --- developer/src/kmc-copy/src/copier-messages.ts | 6 +-- .../kmc-generate/src/generator-messages.ts | 8 +--- developer/src/kmc/src/commands/copy.ts | 12 +++++ developer/src/kmc/src/commands/generate.ts | 11 ++++- .../src/messages/infrastructureMessages.ts | 45 ++++++++++++++++++- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/developer/src/kmc-copy/src/copier-messages.ts b/developer/src/kmc-copy/src/copier-messages.ts index d4751474b9..976002a35f 100644 --- a/developer/src/kmc-copy/src/copier-messages.ts +++ b/developer/src/kmc-copy/src/copier-messages.ts @@ -25,11 +25,7 @@ export class CopierMessages { o.e ?? 'unknown error' ); - static INFO_CopyingProject = SevInfo | 0x0002; - static Info_CopyingProject = (o:{type: string, id: string}) => m( - this.INFO_CopyingProject, - `Copying project of type ${def(o.type)} with id ${def(o.id)}` - ); + // unused 0x0002 (was INFO_CopyingProject, now in InfrastructureMessages) static ERROR_CannotCreateFolder = SevError | 0x0003; static Error_CannotCreateFolder = (o:{folderName:string, e: any}) => CompilerMessageSpecWithException( diff --git a/developer/src/kmc-generate/src/generator-messages.ts b/developer/src/kmc-generate/src/generator-messages.ts index 185f68cbe6..fc8283763e 100644 --- a/developer/src/kmc-generate/src/generator-messages.ts +++ b/developer/src/kmc-generate/src/generator-messages.ts @@ -7,7 +7,7 @@ import { CompilerErrorNamespace, CompilerErrorSeverity, CompilerMessageSpec as m, CompilerMessageDef as def, CompilerMessageSpecWithException } from "@keymanapp/developer-utils"; const Namespace = CompilerErrorNamespace.Generator; -const SevInfo = CompilerErrorSeverity.Info | Namespace; +// const SevInfo = CompilerErrorSeverity.Info | Namespace; // const SevHint = CompilerErrorSeverity.Hint | Namespace; const SevWarn = CompilerErrorSeverity.Warn | Namespace; const SevError = CompilerErrorSeverity.Error | Namespace; @@ -23,11 +23,7 @@ export class GeneratorMessages { o.e ?? 'unknown error', ); - static INFO_GeneratingProject = SevInfo | 0x0002; - static Info_GeneratingProject = (o:{type: string, id: string}) => m( - this.INFO_GeneratingProject, - `Generating project of type ${def(o.type)} with id ${def(o.id)}` - ); + // unused 0x0002 (was INFO_GeneratingProject, now in InfrastructureMessages) static ERROR_CannotCreateFolder = SevError | 0x0003; static Error_CannotCreateFolder = (o:{folderName:string, e: any}) => CompilerMessageSpecWithException( diff --git a/developer/src/kmc/src/commands/copy.ts b/developer/src/kmc/src/commands/copy.ts index b7d0aef12f..9cfef2b957 100644 --- a/developer/src/kmc/src/commands/copy.ts +++ b/developer/src/kmc/src/commands/copy.ts @@ -79,6 +79,18 @@ async function doCopy(callbacks: NodeCompilerCallbacks, sources: string | string return false; } + const dest = options.outPath; + callbacks.reportMessage(InfrastructureMessages.Info_CopyingProject({source, dest})); + const result = runCopier(callbacks, source, options); + if(result) { + callbacks.reportMessage(InfrastructureMessages.Info_ProjectCopiedSuccessfully({source, dest})); + } else { + callbacks.reportMessage(InfrastructureMessages.Info_ProjectNotCopiedSuccessfully({source, dest})); + } + return result; +} + +async function runCopier(callbacks: NodeCompilerCallbacks, source: string, options: CopierOptions) { const copier = new KeymanProjectCopier(); try { if(!await copier.init(callbacks, options)) { diff --git a/developer/src/kmc/src/commands/generate.ts b/developer/src/kmc/src/commands/generate.ts index b362d01125..695ced9d3c 100644 --- a/developer/src/kmc/src/commands/generate.ts +++ b/developer/src/kmc/src/commands/generate.ts @@ -113,9 +113,16 @@ async function generate(generator: KeymanCompiler, ids: string | string[], comma } const options = commanderOptionsToGeneratorOptions(id, commanderOptions); - + const outPath = options.outPath; const callbacks = new NodeCompilerCallbacks(options); - if(!await doGenerate(callbacks, generator, options)) { + callbacks.reportMessage(InfrastructureMessages.Info_GeneratingProject({id, outPath})); + + const result = await doGenerate(callbacks, generator, options); + + if(result) { + callbacks.reportMessage(InfrastructureMessages.Info_ProjectGeneratedSuccessfully({id})); + } else { + callbacks.reportMessage(InfrastructureMessages.Info_ProjectNotGeneratedSuccessfully({id})); return await exitProcess(1); } } diff --git a/developer/src/kmc/src/messages/infrastructureMessages.ts b/developer/src/kmc/src/messages/infrastructureMessages.ts index bacadca545..304e1d5e9f 100644 --- a/developer/src/kmc/src/messages/infrastructureMessages.ts +++ b/developer/src/kmc/src/messages/infrastructureMessages.ts @@ -158,5 +158,48 @@ export class InfrastructureMessages { this.ERROR_CopyRequiresOutPath, `The copy command requires the --out-path, -o parameter` ); - } + + // For this message, we override the filename with the passed-in file. A bit of a hack but does the job + static INFO_CopyingProject = SevInfo | 0x0023; + static Info_CopyingProject = (o:{source:string,dest:string}) => ({filename:o.source, ...m( + this.INFO_CopyingProject, + `Copying project '${def(o.source)}' to '${def(o.dest)}'`, + )}); + + // For this message, we override the filename with the passed-in file. A bit of a hack but does the job + static INFO_ProjectCopiedSuccessfully = SevInfo | 0x0024; + static Info_ProjectCopiedSuccessfully = (o:{source:string, dest:string}) => ({filename:o.source, ...m( + this.INFO_ProjectCopiedSuccessfully, + `'${def(o.source)}' copied to '${def(o.dest)}' successfully.`, + )}); + + // For this message, we override the filename with the passed-in file. A bit of a hack but does the job + static INFO_ProjectNotCopiedSuccessfully = SevInfo | 0x0025; + static Info_ProjectNotCopiedSuccessfully = (o:{source:string, dest:string}) => ({filename:o.source, ...m( + this.INFO_ProjectNotCopiedSuccessfully, + `'${def(o.source)}' was not successfully copied to '${def(o.dest)}'.`, + )}); + + // For this message, we override the filename with the passed-in file. A bit of a hack but does the job + static INFO_GeneratingProject = SevInfo | 0x0026; + static Info_GeneratingProject = (o:{id:string, outPath: string}) => ({filename:o.id, ...m( + this.INFO_GeneratingProject, + `Generating new project '${def(o.id)}' in '${def(o.outPath)}'.`, + )}); + + // For this message, we override the filename with the passed-in file. A bit of a hack but does the job + static INFO_ProjectGeneratedSuccessfully = SevInfo | 0x0027; + static Info_ProjectGeneratedSuccessfully = (o:{id:string}) => ({filename:o.id, ...m( + this.INFO_ProjectGeneratedSuccessfully, + `New project '${def(o.id)}' generated successfully.`, + )}); + + // For this message, we override the filename with the passed-in file. A bit of a hack but does the job + static INFO_ProjectNotGeneratedSuccessfully = SevInfo | 0x0028; + static Info_ProjectNotGeneratedSuccessfully = (o:{id:string}) => ({filename:o.id, ...m( + this.INFO_ProjectNotGeneratedSuccessfully, + `New project '${def(o.id)}' was not successfully generated.`, + )}); + +} From 37a5248810ea9d058bdc7b52c96f68289698f594 Mon Sep 17 00:00:00 2001 From: Shawn Schantz Date: Wed, 12 Feb 2025 13:33:59 +0700 Subject: [PATCH 12/76] chore(ios): increase minimum ios version to 13 --- .../KeymanEngine.xcodeproj/project.pbxproj | 24 +++++++++---------- .../Keyman/Keyman.xcodeproj/project.pbxproj | 12 +++++----- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/ios/engine/KMEI/KeymanEngine.xcodeproj/project.pbxproj b/ios/engine/KMEI/KeymanEngine.xcodeproj/project.pbxproj index 045506b9af..7b21b6bc63 100644 --- a/ios/engine/KMEI/KeymanEngine.xcodeproj/project.pbxproj +++ b/ios/engine/KMEI/KeymanEngine.xcodeproj/project.pbxproj @@ -1805,7 +1805,7 @@ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; INFOPLIST_FILE = KeymanEngineTests/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 13.2; + IPHONEOS_DEPLOYMENT_TARGET = 13; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -1849,7 +1849,7 @@ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; INFOPLIST_FILE = KeymanEngineTests/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 13.2; + IPHONEOS_DEPLOYMENT_TARGET = 13; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -1900,7 +1900,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; INFOPLIST_FILE = KeymanEngine/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 12.2; + IPHONEOS_DEPLOYMENT_TARGET = 13; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -1955,7 +1955,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; INFOPLIST_FILE = KeymanEngine/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 12.2; + IPHONEOS_DEPLOYMENT_TARGET = 13; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -2021,7 +2021,7 @@ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; INFOPLIST_FILE = "KeymanEngine/resources/Keyman.bundle/Contents/Resources/KeymanEngine-Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 12.2; + IPHONEOS_DEPLOYMENT_TARGET = 13; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -2058,7 +2058,7 @@ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; INFOPLIST_FILE = "KeymanEngine/resources/Keyman.bundle/Contents/Resources/KeymanEngine-Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 12.2; + IPHONEOS_DEPLOYMENT_TARGET = 13; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -2151,7 +2151,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; INFOPLIST_FILE = KeymanEngine/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 12.2; + IPHONEOS_DEPLOYMENT_TARGET = 13; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -2194,7 +2194,7 @@ DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 3YE4W86L3G; ENABLE_BITCODE = NO; - IPHONEOS_DEPLOYMENT_TARGET = 12.2; + IPHONEOS_DEPLOYMENT_TARGET = 13; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -2292,7 +2292,7 @@ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; INFOPLIST_FILE = KeymanEngineTests/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 13.2; + IPHONEOS_DEPLOYMENT_TARGET = 13; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -2333,7 +2333,7 @@ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; INFOPLIST_FILE = "KeymanEngine/resources/Keyman.bundle/Contents/Resources/KeymanEngine-Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 12.2; + IPHONEOS_DEPLOYMENT_TARGET = 13; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -2449,7 +2449,7 @@ DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 3YE4W86L3G; ENABLE_BITCODE = NO; - IPHONEOS_DEPLOYMENT_TARGET = 12.2; + IPHONEOS_DEPLOYMENT_TARGET = 13; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -2482,7 +2482,7 @@ DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 3YE4W86L3G; ENABLE_BITCODE = NO; - IPHONEOS_DEPLOYMENT_TARGET = 12.2; + IPHONEOS_DEPLOYMENT_TARGET = 13; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", diff --git a/ios/keyman/Keyman/Keyman.xcodeproj/project.pbxproj b/ios/keyman/Keyman/Keyman.xcodeproj/project.pbxproj index a8b4f8624b..b69c7f424d 100644 --- a/ios/keyman/Keyman/Keyman.xcodeproj/project.pbxproj +++ b/ios/keyman/Keyman/Keyman.xcodeproj/project.pbxproj @@ -1131,7 +1131,7 @@ GCC_PREFIX_HEADER = "Keyman/Keyman-Prefix.pch"; GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)"; INFOPLIST_FILE = Keyman/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 12.2; + IPHONEOS_DEPLOYMENT_TARGET = 13; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -1166,7 +1166,7 @@ GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "Keyman/Keyman-Prefix.pch"; INFOPLIST_FILE = Keyman/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 12.2; + IPHONEOS_DEPLOYMENT_TARGET = 13; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -1319,7 +1319,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; INFOPLIST_FILE = SWKeyboard/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 12.2; + IPHONEOS_DEPLOYMENT_TARGET = 13; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -1362,7 +1362,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; INFOPLIST_FILE = SWKeyboard/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 12.2; + IPHONEOS_DEPLOYMENT_TARGET = 13; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -1456,7 +1456,7 @@ GCC_PREFIX_HEADER = "Keyman/Keyman-Prefix.pch"; GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)"; INFOPLIST_FILE = Keyman/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 12.2; + IPHONEOS_DEPLOYMENT_TARGET = 13; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -1500,7 +1500,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; INFOPLIST_FILE = SWKeyboard/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 12.2; + IPHONEOS_DEPLOYMENT_TARGET = 13; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", From b2598e76ba283cac1f36c0440d0f8a44c68e9b5f Mon Sep 17 00:00:00 2001 From: Joshua Horton Date: Tue, 11 Feb 2025 15:51:26 +0700 Subject: [PATCH 13/76] chore(web): fix coverage-reporting artifact Fixes: #13195 --- web/.c8rc.json | 12 ++++++++- web/build.sh | 26 ++++++++++++++----- web/common.inc.sh | 11 -------- web/src/app/browser/.c8rc.json | 10 +++++++ web/src/engine/attachment/.c8rc.json | 2 +- web/src/engine/common/web-utils/.c8rc.json | 2 +- web/src/engine/events/.c8rc.json | 2 +- web/src/engine/interfaces/.c8rc.json | 2 +- web/src/engine/keyboard-storage/.c8rc.json | 2 +- web/src/engine/keyboard/.c8rc.json | 2 +- .../engine/osk/gesture-processor/.c8rc.json | 2 +- .../predictive-text/templates/.c8rc.json | 2 +- .../predictive-text/wordbreakers/.c8rc.json | 2 +- .../worker-main/src/node/worker.ts | 4 +++ .../predictive-text/worker-thread/.c8rc.json | 2 +- .../engine/js-processor/basic-engine.tests.js | 5 +++- .../engine/js-processor/chirality.tests.js | 5 +++- .../engine/js-processor/deadkeys.tests.js | 5 +++- .../engine/unmatched_final_group.tests.js | 5 +++- web/src/test/auto/headless/getKeymanRoot.mjs | 12 +++++++++ 20 files changed, 83 insertions(+), 32 deletions(-) create mode 100644 web/src/app/browser/.c8rc.json create mode 100644 web/src/test/auto/headless/getKeymanRoot.mjs diff --git a/web/.c8rc.json b/web/.c8rc.json index f308b48487..070ef1b1d2 100644 --- a/web/.c8rc.json +++ b/web/.c8rc.json @@ -7,7 +7,17 @@ "reporter": ["text", "text-summary"], "reports-dir": "build/coverage", "src": [ - "**/src/", + "src/**", "../common/**/src" + ], + "include": [ + "src/**" + ], + "exclude": [ + "src/test/**", + "**/src/tests/**", + "**/build/**", + "src/tools/**", + "../node_modules/*" ] } diff --git a/web/build.sh b/web/build.sh index 5dd30f338f..0dd9b5ef53 100755 --- a/web/build.sh +++ b/web/build.sh @@ -141,12 +141,26 @@ test_action() { coverage_action() { builder_echo "Creating coverage report..." - cd "$KEYMAN_ROOT" - mkdir -p web/build/coverage/tmp - find . -type f -name coverage-\*.json -print0 | xargs -0 cp -t web/build/coverage/tmp - c8 report --config web/.c8rc.json ---reporter html --clean=false --reports-dir=web/build/coverage - rm -rf web/build/coverage/tmp - cd web + + # Always ensure our collation folder is clean, just in case something went wrong on a prior run. + rm -rf build/coverage/tmp + + # Also clean out the sub-report directories + main index.html; don't keep old ones if we fail! + rm -rf build/coverage/app + rm -rf build/coverage/engine + rm -f build/coverage/index.html + + # Collate the results into a single folder + mkdir build/coverage/tmp + find . -type f -name coverage-\*.json -print0 | xargs -0 cp -t build/coverage/tmp + + # Note for maintainers: the .c8rc.json config needs to be written from the perspective of + # the working directory - including its `include` and `exclude` entries. All ignores and + # such must be redefined in whatever that top-level .c8rc.json is. + c8 report --reporter html + + # No need to keep the collation folder around afterward; we can always redo that work. + rm -rf build/coverage/tmp } builder_run_child_actions build:engine/dom-utils diff --git a/web/common.inc.sh b/web/common.inc.sh index 429c9a9a8d..05c924d6ec 100644 --- a/web/common.inc.sh +++ b/web/common.inc.sh @@ -108,22 +108,11 @@ function test-headless() { TEST_CD_REQD=true fi - if [ $TEST_CD_REQD ]; then - # The mocha config needed to live-compile TS-based tests only applies - # if the command is started within the appropriate subfolder. - pushd "${TEST_BASE}" > /dev/null - TEST_BASE= - fi - if [[ -e .c8rc.json ]]; then c8 mocha --recursive "${TEST_BASE}${TEST_FOLDER}" $TEST_OPTS else mocha --recursive "${TEST_BASE}${TEST_FOLDER}" $TEST_OPTS fi - - if [ $TEST_CD_REQD ]; then - popd > /dev/null - fi } # Runs all headless tests (written in typescript) corresponding to the diff --git a/web/src/app/browser/.c8rc.json b/web/src/app/browser/.c8rc.json new file mode 100644 index 0000000000..3e9bef1df0 --- /dev/null +++ b/web/src/app/browser/.c8rc.json @@ -0,0 +1,10 @@ +{ + "check-coverage": false, + "clean": true, + "exclude-after-remap": true, + "reporter": ["text", "text-summary"], + "reports-dir": "../../../build/coverage/raw/app/browser", + "src": [ + "src/" + ] +} \ No newline at end of file diff --git a/web/src/engine/attachment/.c8rc.json b/web/src/engine/attachment/.c8rc.json index c789716cd5..eea63036d2 100644 --- a/web/src/engine/attachment/.c8rc.json +++ b/web/src/engine/attachment/.c8rc.json @@ -3,7 +3,7 @@ "clean": true, "exclude-after-remap": true, "reporter": ["text", "text-summary"], - "reports-dir": "build/coverage", + "reports-dir": "../../../build/coverage/raw/engine/attachment", "src": [ "src/" ] diff --git a/web/src/engine/common/web-utils/.c8rc.json b/web/src/engine/common/web-utils/.c8rc.json index 16a4e93b9c..4c652ff892 100644 --- a/web/src/engine/common/web-utils/.c8rc.json +++ b/web/src/engine/common/web-utils/.c8rc.json @@ -10,7 +10,7 @@ ], "exclude-after-remap": true, "reporter": ["text", "text-summary"], - "reports-dir": "build/coverage", + "reports-dir": "../../../build/coverage/raw/raw/engine/common/web-utils", "src": [ "src/" ] diff --git a/web/src/engine/events/.c8rc.json b/web/src/engine/events/.c8rc.json index c789716cd5..65dd2858d4 100644 --- a/web/src/engine/events/.c8rc.json +++ b/web/src/engine/events/.c8rc.json @@ -3,7 +3,7 @@ "clean": true, "exclude-after-remap": true, "reporter": ["text", "text-summary"], - "reports-dir": "build/coverage", + "reports-dir": "../../../build/coverage/raw/engine/events", "src": [ "src/" ] diff --git a/web/src/engine/interfaces/.c8rc.json b/web/src/engine/interfaces/.c8rc.json index c42b096f68..ebcb215dba 100644 --- a/web/src/engine/interfaces/.c8rc.json +++ b/web/src/engine/interfaces/.c8rc.json @@ -3,7 +3,7 @@ "clean": true, "exclude-after-remap": true, "reporter": ["text", "text-summary"], - "reports-dir": "../../../build/engine/interfaces/coverage", + "reports-dir": "../../../build/coverage/raw/engine/interfaces", "src": [ "src/" ] diff --git a/web/src/engine/keyboard-storage/.c8rc.json b/web/src/engine/keyboard-storage/.c8rc.json index c789716cd5..396d0ddcc5 100644 --- a/web/src/engine/keyboard-storage/.c8rc.json +++ b/web/src/engine/keyboard-storage/.c8rc.json @@ -3,7 +3,7 @@ "clean": true, "exclude-after-remap": true, "reporter": ["text", "text-summary"], - "reports-dir": "build/coverage", + "reports-dir": "../../../build/coverage/raw/engine/keyboard-storage", "src": [ "src/" ] diff --git a/web/src/engine/keyboard/.c8rc.json b/web/src/engine/keyboard/.c8rc.json index ceca963e15..b6bd4a818f 100644 --- a/web/src/engine/keyboard/.c8rc.json +++ b/web/src/engine/keyboard/.c8rc.json @@ -8,7 +8,7 @@ ], "exclude-after-remap": true, "reporter": ["text", "text-summary"], - "reports-dir": "build/coverage", + "reports-dir": "../../../build/coverage/raw/engine/keyboard", "src": [ "src/" ] diff --git a/web/src/engine/osk/gesture-processor/.c8rc.json b/web/src/engine/osk/gesture-processor/.c8rc.json index 719cd11dae..24afdfdddf 100644 --- a/web/src/engine/osk/gesture-processor/.c8rc.json +++ b/web/src/engine/osk/gesture-processor/.c8rc.json @@ -11,7 +11,7 @@ "src/engine/headless/*" ], "reporter": ["text", "text-summary"], - "reports-dir": "build/coverage", + "reports-dir": "../../../../build/coverage/raw/engine/osk/gesture-processor", "src": [ "src/engine/headless/*" ] diff --git a/web/src/engine/predictive-text/templates/.c8rc.json b/web/src/engine/predictive-text/templates/.c8rc.json index d107a1cacb..b938594fb2 100644 --- a/web/src/engine/predictive-text/templates/.c8rc.json +++ b/web/src/engine/predictive-text/templates/.c8rc.json @@ -7,7 +7,7 @@ ], "exclude-after-remap": true, "reporter": ["text", "text-summary"], - "reports-dir": "build/coverage", + "reports-dir": "../../../../build/coverage/raw/engine/predictive-text/templates", "src": [ "src/" ] diff --git a/web/src/engine/predictive-text/wordbreakers/.c8rc.json b/web/src/engine/predictive-text/wordbreakers/.c8rc.json index 6a0e2589da..c2ea18e238 100644 --- a/web/src/engine/predictive-text/wordbreakers/.c8rc.json +++ b/web/src/engine/predictive-text/wordbreakers/.c8rc.json @@ -7,7 +7,7 @@ ], "exclude-after-remap": true, "reporter": ["text", "text-summary"], - "reports-dir": "build/coverage", + "reports-dir": "../../../../build/coverage/raw/engine/predictive-text/wordbreakers", "src": [ "src/" ] diff --git a/web/src/engine/predictive-text/worker-main/src/node/worker.ts b/web/src/engine/predictive-text/worker-main/src/node/worker.ts index 5ad04e55d0..aafb2c4869 100644 --- a/web/src/engine/predictive-text/worker-main/src/node/worker.ts +++ b/web/src/engine/predictive-text/worker-main/src/node/worker.ts @@ -6,6 +6,10 @@ import { LMLayerWorkerCode, LMLayerWorkerSourcemapComment } from "@keymanapp/lm- export default class Worker { static constructInstance(): Worker { let scriptStr = unwrap(LMLayerWorkerCode); + const srcMapUrlIndex = scriptStr.indexOf("//# sourceMappingURL") + if(srcMapUrlIndex > 0) { + scriptStr = scriptStr.substring(0, srcMapUrlIndex); + } scriptStr += '\n' + LMLayerWorkerSourcemapComment; let worker = new MappedWorker(scriptStr); diff --git a/web/src/engine/predictive-text/worker-thread/.c8rc.json b/web/src/engine/predictive-text/worker-thread/.c8rc.json index b66bb5b106..f30fb6465e 100644 --- a/web/src/engine/predictive-text/worker-thread/.c8rc.json +++ b/web/src/engine/predictive-text/worker-thread/.c8rc.json @@ -7,7 +7,7 @@ ], "exclude-after-remap": true, "reporter": ["text", "text-summary"], - "reports-dir": "build/coverage", + "reports-dir": "../../../../build/coverage/raw/engine/predictive-text/worker-thread", "src": [ "src/main" ] diff --git a/web/src/test/auto/headless/engine/js-processor/basic-engine.tests.js b/web/src/test/auto/headless/engine/js-processor/basic-engine.tests.js index 68eb5136e3..df63ec3106 100644 --- a/web/src/test/auto/headless/engine/js-processor/basic-engine.tests.js +++ b/web/src/test/auto/headless/engine/js-processor/basic-engine.tests.js @@ -9,6 +9,9 @@ import { KeyboardInterface } from 'keyman/engine/js-processor'; import { NodeKeyboardLoader } from 'keyman/engine/keyboard/node-keyboard-loader'; import { KeyboardTest, NodeProctor } from '@keymanapp/recorder-core'; +import { getKeymanRoot } from '../../getKeymanRoot.mjs'; +const KEYMAN_ROOT = getKeymanRoot(); + describe('Engine - Basic Simulation', function() { let testJSONtext = fs.readFileSync(require.resolve('@keymanapp/common-test-resources/json/engine_tests/basic_lao_simulation.json')); // Common test suite setup. @@ -25,7 +28,7 @@ describe('Engine - Basic Simulation', function() { before(async function() { // -- START: Standard Recorder-based unit test loading boilerplate -- let keyboardLoader = new NodeKeyboardLoader(new KeyboardInterface({}, MinimalKeymanGlobal)); - let keyboard = await keyboardLoader.loadKeyboardFromPath('../../../../../common/test/' + testSuite.keyboard.filename); + let keyboard = await keyboardLoader.loadKeyboardFromPath(KEYMAN_ROOT + '/common/test/' + testSuite.keyboard.filename); keyboardWithHarness = keyboardLoader.harness; keyboardWithHarness.activeKeyboard = keyboard; diff --git a/web/src/test/auto/headless/engine/js-processor/chirality.tests.js b/web/src/test/auto/headless/engine/js-processor/chirality.tests.js index c2220b7f5d..ea068e014d 100644 --- a/web/src/test/auto/headless/engine/js-processor/chirality.tests.js +++ b/web/src/test/auto/headless/engine/js-processor/chirality.tests.js @@ -10,6 +10,9 @@ import { NodeKeyboardLoader } from 'keyman/engine/keyboard/node-keyboard-loader' import { KeyboardTest, NodeProctor } from '@keymanapp/recorder-core'; import { ModifierKeyConstants } from '@keymanapp/common-types'; +import { getKeymanRoot } from '../../getKeymanRoot.mjs'; +const KEYMAN_ROOT = getKeymanRoot(); + describe('Engine - Chirality', function() { let testJSONtext = fs.readFileSync(require.resolve('@keymanapp/common-test-resources/json/engine_tests/chirality.json')); // Common test suite setup. @@ -26,7 +29,7 @@ describe('Engine - Chirality', function() { before(async function() { // -- START: Standard Recorder-based unit test loading boilerplate -- let keyboardLoader = new NodeKeyboardLoader(new KeyboardInterface({}, MinimalKeymanGlobal)); - let keyboard = await keyboardLoader.loadKeyboardFromPath('../../../../../common/test/' + testSuite.keyboard.filename); + let keyboard = await keyboardLoader.loadKeyboardFromPath(KEYMAN_ROOT + '/common/test/' + testSuite.keyboard.filename); keyboardWithHarness = keyboardLoader.harness; keyboardWithHarness.activeKeyboard = keyboard; diff --git a/web/src/test/auto/headless/engine/js-processor/deadkeys.tests.js b/web/src/test/auto/headless/engine/js-processor/deadkeys.tests.js index fd5d1457d3..ae67121dfd 100644 --- a/web/src/test/auto/headless/engine/js-processor/deadkeys.tests.js +++ b/web/src/test/auto/headless/engine/js-processor/deadkeys.tests.js @@ -9,6 +9,9 @@ import { KeyboardInterface } from 'keyman/engine/js-processor'; import { NodeKeyboardLoader } from 'keyman/engine/keyboard/node-keyboard-loader'; import { KeyboardTest, NodeProctor } from '@keymanapp/recorder-core'; +import { getKeymanRoot } from '../../getKeymanRoot.mjs'; +const KEYMAN_ROOT = getKeymanRoot(); + describe('Engine - Deadkeys', function() { let testJSONtext = fs.readFileSync(require.resolve('@keymanapp/common-test-resources/json/engine_tests/deadkeys.json')); // Common test suite setup. @@ -25,7 +28,7 @@ describe('Engine - Deadkeys', function() { before(async function() { // -- START: Standard Recorder-based unit test loading boilerplate -- let keyboardLoader = new NodeKeyboardLoader(new KeyboardInterface({}, MinimalKeymanGlobal)); - let keyboard = await keyboardLoader.loadKeyboardFromPath('../../../../../common/test/' + testSuite.keyboard.filename); + let keyboard = await keyboardLoader.loadKeyboardFromPath(KEYMAN_ROOT + '/common/test/' + testSuite.keyboard.filename); keyboardWithHarness = keyboardLoader.harness; keyboardWithHarness.activeKeyboard = keyboard; diff --git a/web/src/test/auto/headless/engine/js-processor/engine/unmatched_final_group.tests.js b/web/src/test/auto/headless/engine/js-processor/engine/unmatched_final_group.tests.js index 8bfee041eb..5ceaf40cf6 100644 --- a/web/src/test/auto/headless/engine/js-processor/engine/unmatched_final_group.tests.js +++ b/web/src/test/auto/headless/engine/js-processor/engine/unmatched_final_group.tests.js @@ -9,6 +9,9 @@ import { KeyboardInterface } from 'keyman/engine/js-processor'; import { NodeKeyboardLoader } from 'keyman/engine/keyboard/node-keyboard-loader'; import { KeyboardTest, NodeProctor } from '@keymanapp/recorder-core'; +import { getKeymanRoot } from '../../../getKeymanRoot.mjs'; +const KEYMAN_ROOT = getKeymanRoot(); + describe('Engine - Unmatched Final Groups', function() { let testJSONtext = fs.readFileSync(require.resolve('@keymanapp/common-test-resources/json/engine_tests/ghp_enter.json')); // Common test suite setup. @@ -24,7 +27,7 @@ describe('Engine - Unmatched Final Groups', function() { before(async function() { // -- START: Standard Recorder-based unit test loading boilerplate -- let keyboardLoader = new NodeKeyboardLoader(new KeyboardInterface({}, MinimalKeymanGlobal)); - const keyboard = await keyboardLoader.loadKeyboardFromPath('../../../../../common/test/' + testSuite.keyboard.filename); + const keyboard = await keyboardLoader.loadKeyboardFromPath(KEYMAN_ROOT + '/common/test/' + testSuite.keyboard.filename); keyboardWithHarness = keyboardLoader.harness; keyboardWithHarness.activeKeyboard = keyboard; diff --git a/web/src/test/auto/headless/getKeymanRoot.mjs b/web/src/test/auto/headless/getKeymanRoot.mjs new file mode 100644 index 0000000000..ce17cecd0f --- /dev/null +++ b/web/src/test/auto/headless/getKeymanRoot.mjs @@ -0,0 +1,12 @@ +import { createRequire } from 'module'; +import * as path from 'path' +const require = createRequire(import.meta.url); + +export function getKeymanRoot() { + let anchorPath = require.resolve('@keymanapp/common-test-resources/index.mjs'); + let root = path.dirname(anchorPath); + for(let i=0; i < 3; i++) { + root = path.dirname(root); + } + return root; +} \ No newline at end of file From 39c1727b6fe523c6c1f07e6bb4296f9abad2c527 Mon Sep 17 00:00:00 2001 From: Joshua Horton Date: Wed, 12 Feb 2025 15:55:23 +0700 Subject: [PATCH 14/76] change(web): use env.KEYMAN_ROOT instead of custom function --- .../engine/js-processor/basic-engine.tests.js | 5 +++-- .../headless/engine/js-processor/chirality.tests.js | 4 ++-- .../headless/engine/js-processor/deadkeys.tests.js | 4 ++-- .../engine/unmatched_final_group.tests.js | 4 ++-- web/src/test/auto/headless/getKeymanRoot.mjs | 12 ------------ 5 files changed, 9 insertions(+), 20 deletions(-) delete mode 100644 web/src/test/auto/headless/getKeymanRoot.mjs diff --git a/web/src/test/auto/headless/engine/js-processor/basic-engine.tests.js b/web/src/test/auto/headless/engine/js-processor/basic-engine.tests.js index df63ec3106..09780444d9 100644 --- a/web/src/test/auto/headless/engine/js-processor/basic-engine.tests.js +++ b/web/src/test/auto/headless/engine/js-processor/basic-engine.tests.js @@ -1,6 +1,7 @@ import { assert } from 'chai'; import fs from 'fs'; + import { createRequire } from 'module'; const require = createRequire(import.meta.url); @@ -9,8 +10,8 @@ import { KeyboardInterface } from 'keyman/engine/js-processor'; import { NodeKeyboardLoader } from 'keyman/engine/keyboard/node-keyboard-loader'; import { KeyboardTest, NodeProctor } from '@keymanapp/recorder-core'; -import { getKeymanRoot } from '../../getKeymanRoot.mjs'; -const KEYMAN_ROOT = getKeymanRoot(); +import { env } from 'node:process'; +const KEYMAN_ROOT = env.KEYMAN_ROOT; describe('Engine - Basic Simulation', function() { let testJSONtext = fs.readFileSync(require.resolve('@keymanapp/common-test-resources/json/engine_tests/basic_lao_simulation.json')); diff --git a/web/src/test/auto/headless/engine/js-processor/chirality.tests.js b/web/src/test/auto/headless/engine/js-processor/chirality.tests.js index ea068e014d..e8a3e30f9e 100644 --- a/web/src/test/auto/headless/engine/js-processor/chirality.tests.js +++ b/web/src/test/auto/headless/engine/js-processor/chirality.tests.js @@ -10,8 +10,8 @@ import { NodeKeyboardLoader } from 'keyman/engine/keyboard/node-keyboard-loader' import { KeyboardTest, NodeProctor } from '@keymanapp/recorder-core'; import { ModifierKeyConstants } from '@keymanapp/common-types'; -import { getKeymanRoot } from '../../getKeymanRoot.mjs'; -const KEYMAN_ROOT = getKeymanRoot(); +import { env } from 'node:process'; +const KEYMAN_ROOT = env.KEYMAN_ROOT; describe('Engine - Chirality', function() { let testJSONtext = fs.readFileSync(require.resolve('@keymanapp/common-test-resources/json/engine_tests/chirality.json')); diff --git a/web/src/test/auto/headless/engine/js-processor/deadkeys.tests.js b/web/src/test/auto/headless/engine/js-processor/deadkeys.tests.js index ae67121dfd..007a4ec9ec 100644 --- a/web/src/test/auto/headless/engine/js-processor/deadkeys.tests.js +++ b/web/src/test/auto/headless/engine/js-processor/deadkeys.tests.js @@ -9,8 +9,8 @@ import { KeyboardInterface } from 'keyman/engine/js-processor'; import { NodeKeyboardLoader } from 'keyman/engine/keyboard/node-keyboard-loader'; import { KeyboardTest, NodeProctor } from '@keymanapp/recorder-core'; -import { getKeymanRoot } from '../../getKeymanRoot.mjs'; -const KEYMAN_ROOT = getKeymanRoot(); +import { env } from 'node:process'; +const KEYMAN_ROOT = env.KEYMAN_ROOT; describe('Engine - Deadkeys', function() { let testJSONtext = fs.readFileSync(require.resolve('@keymanapp/common-test-resources/json/engine_tests/deadkeys.json')); diff --git a/web/src/test/auto/headless/engine/js-processor/engine/unmatched_final_group.tests.js b/web/src/test/auto/headless/engine/js-processor/engine/unmatched_final_group.tests.js index 5ceaf40cf6..fd2536ec22 100644 --- a/web/src/test/auto/headless/engine/js-processor/engine/unmatched_final_group.tests.js +++ b/web/src/test/auto/headless/engine/js-processor/engine/unmatched_final_group.tests.js @@ -9,8 +9,8 @@ import { KeyboardInterface } from 'keyman/engine/js-processor'; import { NodeKeyboardLoader } from 'keyman/engine/keyboard/node-keyboard-loader'; import { KeyboardTest, NodeProctor } from '@keymanapp/recorder-core'; -import { getKeymanRoot } from '../../../getKeymanRoot.mjs'; -const KEYMAN_ROOT = getKeymanRoot(); +import { env } from 'node:process'; +const KEYMAN_ROOT = env.KEYMAN_ROOT; describe('Engine - Unmatched Final Groups', function() { let testJSONtext = fs.readFileSync(require.resolve('@keymanapp/common-test-resources/json/engine_tests/ghp_enter.json')); diff --git a/web/src/test/auto/headless/getKeymanRoot.mjs b/web/src/test/auto/headless/getKeymanRoot.mjs deleted file mode 100644 index ce17cecd0f..0000000000 --- a/web/src/test/auto/headless/getKeymanRoot.mjs +++ /dev/null @@ -1,12 +0,0 @@ -import { createRequire } from 'module'; -import * as path from 'path' -const require = createRequire(import.meta.url); - -export function getKeymanRoot() { - let anchorPath = require.resolve('@keymanapp/common-test-resources/index.mjs'); - let root = path.dirname(anchorPath); - for(let i=0; i < 3; i++) { - root = path.dirname(root); - } - return root; -} \ No newline at end of file From ef824dcfa6dbbe13c18d6fcd7bb4420708eca0a4 Mon Sep 17 00:00:00 2001 From: Eberhard Beilharz Date: Wed, 12 Feb 2025 11:32:37 +0100 Subject: [PATCH 15/76] chore(linux): update minimum version --- docs/build/linux-ubuntu.md | 2 +- docs/minimum-versions.md | 2 +- linux/docs/help/common/index.md | 4 ++-- resources/build/minimum-versions.inc.sh | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/build/linux-ubuntu.md b/docs/build/linux-ubuntu.md index 0f531acb59..9c0b195474 100644 --- a/docs/build/linux-ubuntu.md +++ b/docs/build/linux-ubuntu.md @@ -23,7 +23,7 @@ The following projects **cannot** be built on Linux: ### System Requirements -- Minimum Ubuntu version: Ubuntu 20.04 +- Minimum Ubuntu version: Ubuntu 22.04 Other Linux distributions will also work if appropriate dependencies are installed. diff --git a/docs/minimum-versions.md b/docs/minimum-versions.md index beb542e408..4f67d5a626 100644 --- a/docs/minimum-versions.md +++ b/docs/minimum-versions.md @@ -53,7 +53,7 @@ https://help.keyman.com/developer/engine/android/latest-version/ | KEYMAN_MIN_TARGET_VERSION_ANDROID_CHROME | 53.0 | | KEYMAN_MIN_TARGET_VERSION_IOS | 12.2 | | KEYMAN_MIN_TARGET_VERSION_MAC | 10.13 | -| KEYMAN_MIN_TARGET_VERSION_UBUNTU | 20.04 | +| KEYMAN_MIN_TARGET_VERSION_UBUNTU | 22.04 | | KEYMAN_MIN_TARGET_VERSION_WEB_CHROME | 95.0 | | KEYMAN_MIN_TARGET_VERSION_WEB_FIREFOX | 79.0 | | KEYMAN_MIN_TARGET_VERSION_WEB_OPERA | 47.0 | diff --git a/linux/docs/help/common/index.md b/linux/docs/help/common/index.md index b2d606b38d..a7f9e786bf 100644 --- a/linux/docs/help/common/index.md +++ b/linux/docs/help/common/index.md @@ -59,8 +59,8 @@ no on-screen keyboard for Wayland that works with Keyman. **A.** Keyman runs on Debian, Ubuntu, Wasta Linux and can be compiled to run from source in most distributions. -**Note:** Ubuntu versions before Ubuntu 20.04 LTS are no longer supported with -Keyman 17. If you are still running an older version and require Keyman you'll +**Note:** Ubuntu versions before Ubuntu 22.04 LTS are no longer supported with +Keyman 18. If you are still running an older version and require Keyman you'll have to install an older Keyman version. ## Q. Will my existing Windows Keyman keyboard work with Keyman for Linux? diff --git a/resources/build/minimum-versions.inc.sh b/resources/build/minimum-versions.inc.sh index f57ebff419..2e1a2914b1 100644 --- a/resources/build/minimum-versions.inc.sh +++ b/resources/build/minimum-versions.inc.sh @@ -14,7 +14,7 @@ KEYMAN_MIN_TARGET_VERSION_ANDROID=5.0 # Lollipop KEYMAN_MIN_TARGET_VERSION_IOS=12.2 # iOS 12.2 KEYMAN_MIN_TARGET_VERSION_WINDOWS=10 # Windows 10 KEYMAN_MIN_TARGET_VERSION_MAC=10.13 # MacOS 10.13 (High Sierra) -KEYMAN_MIN_TARGET_VERSION_UBUNTU=20.04 # Ubuntu 20.04 Focal +KEYMAN_MIN_TARGET_VERSION_UBUNTU=22.04 # Ubuntu 22.04 Jammy KEYMAN_MIN_TARGET_VERSION_ANDROID_CHROME=53.0 # min version of Chrome for Keyman for Android # Target web browsers for KeymanWeb -- we do not have polyfills for From 53bb152bef5497e7029b303a1141963a4533e17a Mon Sep 17 00:00:00 2001 From: Eberhard Beilharz Date: Wed, 12 Feb 2025 11:47:21 +0100 Subject: [PATCH 16/76] docs(linux): update `welcome.md` --- linux/docs/help/about/welcome.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/linux/docs/help/about/welcome.md b/linux/docs/help/about/welcome.md index eacc8c449c..dfd410b46e 100644 --- a/linux/docs/help/about/welcome.md +++ b/linux/docs/help/about/welcome.md @@ -6,6 +6,6 @@ Thank you for installing Keyman for Linux. Whether you're a new or returning use ## What is Keyman for Linux? -Keyman for Linux makes it possible to type in over 1,000 languages in any Linux application. +Keyman for Linux makes it possible to type in over 2,000 languages in any Linux application. [Click here](../start/) to get started. From f1b32a1439c4943b757c75d2d456cde3523e898a Mon Sep 17 00:00:00 2001 From: Eberhard Beilharz Date: Wed, 12 Feb 2025 11:58:45 +0100 Subject: [PATCH 17/76] chore(linux): additional code cleanup after Focal removal --- linux/debian/control | 4 +-- linux/ibus-keyman/src/keymanutil.c | 5 ++-- linux/ibus-keyman/src/test/keymanutil.tests.c | 30 +++++-------------- .../keyman_config/downloadkeyboard.py | 7 +---- .../keyman_config/install_window.py | 7 +---- .../keyman_config/keyboard_options_view.py | 7 +---- linux/keyman-config/keyman_config/welcome.py | 6 +--- 7 files changed, 15 insertions(+), 51 deletions(-) diff --git a/linux/debian/control b/linux/debian/control index fbe7066f5c..e12da18aa3 100644 --- a/linux/debian/control +++ b/linux/debian/control @@ -10,7 +10,7 @@ Build-Depends: debhelper-compat (= 12), dh-python, gawk, - gir1.2-webkit2-4.1 | gir1.2-webkit2-4.0, + gir1.2-webkit2-4.1, ibus, libevdev-dev, libgtk-3-dev, @@ -88,7 +88,7 @@ Architecture: all Depends: dbus-x11, dconf-cli, - gir1.2-webkit2-4.1 | gir1.2-webkit2-4.0, + gir1.2-webkit2-4.1, keyman-engine, python3-bs4, python3-fonttools, diff --git a/linux/ibus-keyman/src/keymanutil.c b/linux/ibus-keyman/src/keymanutil.c index 9f48f0ffce..4bb0ac4078 100644 --- a/linux/ibus-keyman/src/keymanutil.c +++ b/linux/ibus-keyman/src/keymanutil.c @@ -649,9 +649,8 @@ _ptr_array_new_from_array( } // `g_ptr_array_new_from_null_terminated_array` is only available in GLib 2.76, but we're still -// stuck to 2.64 (Ubuntu 20.04 Focal) and 2.72 (Ubuntu 22.04 Jammy). Therefore we -// copy the implementation here (slightly simplified). Once we're past 2.76 we can use the GLib method -// directly. +// stuck to 2.72 (Ubuntu 22.04 Jammy). Therefore we copy the implementation here (slightly +// simplified). Once we're past 2.76 we can use the GLib method directly. GPtrArray * _g_ptr_array_new_from_null_terminated_array( gpointer *data, diff --git a/linux/ibus-keyman/src/test/keymanutil.tests.c b/linux/ibus-keyman/src/test/keymanutil.tests.c index 13636857d8..56965a41ad 100644 --- a/linux/ibus-keyman/src/test/keymanutil.tests.c +++ b/linux/ibus-keyman/src/test/keymanutil.tests.c @@ -123,22 +123,6 @@ _free_tst_kb_data(add_keyboard_data* kb_data) { G_DEFINE_AUTOPTR_CLEANUP_FUNC(add_keyboard_data, _free_tst_kb_data) -// Newer glib versions have g_assert_cmpstrv which would allow to do -// g_assert_cmpstrv(result, keyboards); -// but unfortunately Ubuntu 20.04 Focal doesn't have that, so we roll -// our own -void -_kmn_assert_cmpstrv(gchar** result, gchar** expected) { - g_assert_nonnull(result); - g_assert_nonnull(expected); - int i = 0; - for (; result[i] && expected[i]; i++) { - g_assert_cmpstr(result[i], ==, expected[i]); - } - g_assert_null(result[i]); - g_assert_null(expected[i]); -} - //---------------------------------------------------------------------------------------------- void test_keyman_put_keyboard_options_todconf__invalid() { @@ -526,7 +510,7 @@ test_keyman_set_custom_keyboards__new_key() { // Verify g_auto(GStrv) result = _get_tst_kbds_key(); g_assert_nonnull(result); - _kmn_assert_cmpstrv(result, keyboards); + g_assert_cmpstrv(result, keyboards); // Cleanup _delete_tst_kbds_key(); @@ -546,7 +530,7 @@ test_keyman_set_custom_keyboards__overwrite_key() { // Verify g_auto(GStrv) result = _get_tst_kbds_key(); g_assert_nonnull(result); - _kmn_assert_cmpstrv(result, keyboards); + g_assert_cmpstrv(result, keyboards); // Cleanup _delete_tst_kbds_key(); @@ -565,7 +549,7 @@ test_keyman_set_custom_keyboards__delete_key_NULL() { g_auto(GStrv) result = _get_tst_kbds_key(); gchar* expected[] = {NULL}; g_assert_nonnull(result); - _kmn_assert_cmpstrv(result, expected); + g_assert_cmpstrv(result, expected); // Cleanup _delete_tst_kbds_key(); @@ -585,7 +569,7 @@ test_keyman_set_custom_keyboards__delete_key_empty_array() { // Verify g_auto(GStrv) result = _get_tst_kbds_key(); g_assert_nonnull(result); - _kmn_assert_cmpstrv(result, keyboards); + g_assert_cmpstrv(result, keyboards); // Cleanup _delete_tst_kbds_key(); @@ -604,7 +588,7 @@ test_keyman_set_custom_keyboards__invalid_values() { g_auto(GStrv) result = _get_tst_kbds_key(); g_assert_nonnull(result); gchar* expected[] = {"fr:/tmp/test/test.kmx", NULL}; - _kmn_assert_cmpstrv(result, expected); + g_assert_cmpstrv(result, expected); // Cleanup _delete_tst_kbds_key(); @@ -622,7 +606,7 @@ test_keyman_get_custom_keyboards__value() { // Verify g_assert_nonnull(result); - _kmn_assert_cmpstrv(result, keyboards); + g_assert_cmpstrv(result, keyboards); // Cleanup _delete_tst_kbds_key(); @@ -640,7 +624,7 @@ test_keyman_get_custom_keyboards__invalid_values() { // Verify gchar* expected[] = {"fr:/tmp/test/test.kmx", NULL}; g_assert_nonnull(result); - _kmn_assert_cmpstrv(result, expected); + g_assert_cmpstrv(result, expected); // Cleanup _delete_tst_kbds_key(); diff --git a/linux/keyman-config/keyman_config/downloadkeyboard.py b/linux/keyman-config/keyman_config/downloadkeyboard.py index 42ff74308d..ca3b091f92 100755 --- a/linux/keyman-config/keyman_config/downloadkeyboard.py +++ b/linux/keyman-config/keyman_config/downloadkeyboard.py @@ -7,12 +7,7 @@ import urllib.parse import gi gi.require_version('Gtk', '3.0') -try: - gi.require_version('WebKit2', '4.1') -except ValueError: - # TODO: Remove once we drop support for Ubuntu 20.04 Focal - gi.require_version('WebKit2', '4.0') - +gi.require_version('WebKit2', '4.1') from gi.repository import Gtk, WebKit2 from keyman_config import KeymanComUrl, _, __releaseversion__, __tier__ diff --git a/linux/keyman-config/keyman_config/install_window.py b/linux/keyman-config/keyman_config/install_window.py index dda242c7e1..1342dd44f6 100755 --- a/linux/keyman-config/keyman_config/install_window.py +++ b/linux/keyman-config/keyman_config/install_window.py @@ -15,12 +15,7 @@ import packaging.version import gi gi.require_version('Gtk', '3.0') -try: - gi.require_version('WebKit2', '4.1') -except ValueError: - # TODO: Remove once we drop support for Ubuntu 20.04 Focal - gi.require_version('WebKit2', '4.0') - +gi.require_version('WebKit2', '4.1') from gi.repository import Gtk, WebKit2 from keyman_config import _, secure_lookup diff --git a/linux/keyman-config/keyman_config/keyboard_options_view.py b/linux/keyman-config/keyman_config/keyboard_options_view.py index 795386d7bf..38499a558a 100644 --- a/linux/keyman-config/keyman_config/keyboard_options_view.py +++ b/linux/keyman-config/keyman_config/keyboard_options_view.py @@ -8,12 +8,7 @@ from urllib.parse import parse_qsl, urlencode import gi gi.require_version('Gtk', '3.0') -try: - gi.require_version('WebKit2', '4.1') -except ValueError: - # TODO: Remove once we drop support for Ubuntu 20.04 Focal - gi.require_version('WebKit2', '4.0') - +gi.require_version('WebKit2', '4.1') from gi.repository import Gtk, WebKit2 from keyman_config import _ diff --git a/linux/keyman-config/keyman_config/welcome.py b/linux/keyman-config/keyman_config/welcome.py index af001ea464..a14d747c6a 100644 --- a/linux/keyman-config/keyman_config/welcome.py +++ b/linux/keyman-config/keyman_config/welcome.py @@ -7,11 +7,7 @@ import webbrowser import gi gi.require_version('Gtk', '3.0') -try: - gi.require_version('WebKit2', '4.1') -except ValueError: - # TODO: Remove once we drop support for Ubuntu 20.04 Focal - gi.require_version('WebKit2', '4.0') +gi.require_version('WebKit2', '4.1') from gi.repository import Gtk, WebKit2 from keyman_config import _ From bbc3142c3c05a7c8c4d0a35f6f74303a2e4fc679 Mon Sep 17 00:00:00 2001 From: Eberhard Beilharz Date: Wed, 12 Feb 2025 18:01:21 +0100 Subject: [PATCH 18/76] docs(linux): update what's new for 18.0 Fixes: #13183 --- linux/docs/help/about/whatsnew.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/linux/docs/help/about/whatsnew.md b/linux/docs/help/about/whatsnew.md index 4a4d5a30e1..35edaa60ab 100644 --- a/linux/docs/help/about/whatsnew.md +++ b/linux/docs/help/about/whatsnew.md @@ -2,4 +2,12 @@ title: What's New in Keyman 18.0 for Linux --- -Here are some of the new features we have added to Keyman for Linux 18.0: +Here are some significant changes to Keyman for Linux 18.0: + +- Minimum supported Ubuntu version is now 22.04 Jammy LTS. +- Other supported versions are Ubuntu 24.04 Noble LTS and + Ubuntu 24.10 Oracular. Users that are running an older version of + Ubuntu will have to continue using an older Keyman version. +- Keyman no longer requires a patched version of ibus as Keyman now uses + a system service to manage keystroke order (#11535). +- Added support for simulation of AltGr (right Alt) with Ctrl+Alt (#11852) From 67d26a9d3f9074c11cb4a6b5a504d346c89b9e79 Mon Sep 17 00:00:00 2001 From: Keyman Build Agent Date: Wed, 12 Feb 2025 13:01:06 -0500 Subject: [PATCH 19/76] auto: increment master version to 19.0.3 --- HISTORY.md | 4 ++++ VERSION.md | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/HISTORY.md b/HISTORY.md index e907cc12dd..544a275dea 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,5 +1,9 @@ # Keyman Version History +## 19.0.2 alpha 2025-02-12 + +* chore(linux): remove support of Ubuntu 20.04 Focal (#13203) + ## 19.0.1 alpha 2025-02-11 * refactor(windows): rename `TKeymanMutex.MutexOwned` to `TakeOwnership` and add `ReleaseOwnership` (#13168) diff --git a/VERSION.md b/VERSION.md index fc1a009252..dd7d796646 100644 --- a/VERSION.md +++ b/VERSION.md @@ -1 +1 @@ -19.0.2 \ No newline at end of file +19.0.3 \ No newline at end of file From d4f96d66b74359426021b96ad546617ba6f32eb5 Mon Sep 17 00:00:00 2001 From: Darcy Wong Date: Wed, 5 Feb 2025 10:17:25 +0700 Subject: [PATCH 20/76] fix(android): Check WebView is installed and enabled Fixes: #11560 Fixes: KEYMAN-ANDROID-5E9 Fixes: KEYMAN-ANDROID-6MA --- .../com/tavultesoft/kmapro/MainActivity.java | 102 ++++++++++++------ .../src/main/res/layout/activity_main.xml | 4 +- ...ut.xml => check_chrome_webview_layout.xml} | 8 +- .../java/com/keyman/engine/KMKeyboard.java | 19 +++- .../java/com/keyman/engine/KMManager.java | 37 +++++-- .../java/com/keyman/engine/KMTextView.java | 6 +- .../com/keyman/engine/util/WebViewUtils.java | 39 ++++++- .../KMEA/app/src/main/res/values/strings.xml | 39 +++++-- 8 files changed, 190 insertions(+), 64 deletions(-) rename android/KMAPro/kMAPro/src/main/res/layout/{update_chrome_layout.xml => check_chrome_webview_layout.xml} (86%) 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 908d2f4538..98e26959b7 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 @@ -43,7 +43,9 @@ import com.keyman.android.DownloadIntentService; import com.keyman.engine.util.KMLog; import com.keyman.engine.util.KMPLink; import com.keyman.engine.util.KMString; +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; @@ -153,6 +155,7 @@ 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); @@ -181,8 +184,8 @@ public class MainActivity extends BaseActivity implements OnKeyboardEventListene textView.setTextSize((float) textSize); textView.setSelection(textView.getText().length()); - // Use in-app keyboard WebView to check the Chrome version - checkChromeVersion(KMManager.getKMKeyboard(KeyboardType.KEYBOARD_TYPE_INAPP)); + // Check WebView enabled and Chrome version + checkWebViewAndChromeVersion(context); CheckInstallReferrer.checkGooglePlayInstallReferrer(this, context); checkGetStarted(); } @@ -770,39 +773,70 @@ public class MainActivity extends BaseActivity implements OnKeyboardEventListene KMManager.setHapticFeedback(mayHaveHapticFeedback); } - private void checkChromeVersion(WebView webView) { - if (KMManager.getEngineWebViewVersionStatus() != EngineWebViewVersionStatus.FULL) { - LinearLayout updateChromeLayout = (LinearLayout) findViewById(R.id.updateChromeLayout); - updateChromeLayout.setVisibility(View.VISIBLE); - - Button updateChromeButton = (Button)findViewById(R.id.updateChromeButton); - updateChromeButton.setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - // Launch PlayStore to update Chrome - try { - Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=com.android.chrome")); - if (intent.resolveActivity(getPackageManager()) != null) { - startActivity(intent); - } else { - intent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://play.google.com/store/apps/details?id=com.android.chrome")); - if (intent.resolveActivity(getPackageManager()) != null) { - startActivity(intent); - } else { - Toast.makeText(getApplicationContext(), getString(R.string.unable_to_open_browser), Toast.LENGTH_SHORT).show(); - } - } - } catch (android.content.ActivityNotFoundException e) { - // Link to Chrome if user is not signed in to Play Store - Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://play.google.com/store/apps/details?id=com.android.chrome")); - if (intent.resolveActivity(getPackageManager()) != null) { - startActivity(intent); - } else { - Toast.makeText(getApplicationContext(), getString(R.string.unable_to_open_browser), Toast.LENGTH_SHORT).show(); - } - } - } - }); + /** + * Check that WebView is enabled and Chrome version high enough for Keyman. + * If they're not, update the view and button to install/enable the missing app. + * @param context + */ + private void checkWebViewAndChromeVersion(Context context) { + final SystemWebViewStatus webViewStatus = WebViewUtils.getSystemWebViewStatus(context); + final boolean updateChrome = KMManager.getEngineWebViewVersionStatus() != EngineWebViewVersionStatus.FULL; + if (webViewStatus == SystemWebViewStatus.FULL && !updateChrome) { + return; } + + final TextView textView = (TextView)findViewById((R.id.kmWebViewChromeTextView)); + String buttonLabel = getString(R.string.button_update_chrome); + if (webViewStatus == SystemWebViewStatus.NOT_INSTALLED) { + textView.setText(getString(R.string.body_install_webview)); + buttonLabel = getString(R.string.label_install_webview); + } else if (webViewStatus == SystemWebViewStatus.DISABLED) { + textView.setText(R.string.body_enable_webview); + buttonLabel = getString(R.string.label_enable_webview); + } + + LinearLayout webViewChromeLayout = (LinearLayout) findViewById(R.id.checkWebViewChromeLayout); + webViewChromeLayout.setVisibility(View.VISIBLE); + + Button button = (Button)findViewById(R.id.webViewChromeButton); + button.setText(buttonLabel); + button.setOnClickListener(new View.OnClickListener() { + public void onClick(View v) { + // Initialize the intent to something (alternate Play Store link to install/update Chrome) + Intent intent = new Intent(Intent.ACTION_VIEW, + Uri.parse("https://play.google.com/store/apps/details?id=com.android.chrome")); + if (webViewStatus == SystemWebViewStatus.NOT_INSTALLED) { + // Play Store link to install WebView + intent = new Intent(Intent.ACTION_VIEW, + Uri.parse("https://play.google.com/store/apps/details?id=com.google.android.webview")); + } else if (webViewStatus == SystemWebViewStatus.DISABLED) { + // Application settings to enable WebView + intent = new Intent("android.settings.APPLICATION_DETAILS_SETTINGS", + Uri.parse("package:com.google.android.webview")); + } else if (updateChrome) { + // Primary Play Store link to install/update Chrome + intent = new Intent(Intent.ACTION_VIEW, + Uri.parse("market://details?id=com.android.chrome")); + } + + // Launch intent + try { + PackageManager packageManager = context.getPackageManager(); + if (intent.resolveActivity(packageManager) != null) { + startActivity(intent); + } else { + // (alternate Play Store link to install/update Chrome) + intent = new Intent(Intent.ACTION_VIEW, + Uri.parse("https://play.google.com/store/apps/details?id=com.android.chrome")); + + intent.resolveActivity(packageManager); + startActivity(intent); + } + } catch (android.content.ActivityNotFoundException e) { + Toast.makeText(getApplicationContext(), getString(R.string.unable_to_open_browser), Toast.LENGTH_SHORT).show(); + } + } + }); } private Uri requestPermissionIntentUri; 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 87c7fe16f9..c2911272d1 100644 --- a/android/KMAPro/kMAPro/src/main/res/layout/activity_main.xml +++ b/android/KMAPro/kMAPro/src/main/res/layout/activity_main.xml @@ -11,7 +11,7 @@ - - +