Merge pull request #12111 from keymanapp/refactor/web/12067_js-processor

refactor(web): move parts of `keyboard-processor` → `js-processor` 🏗️
This commit is contained in:
Eberhard Beilharz 2024-08-15 08:20:52 +02:00 committed by GitHub
commit be75933874
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
47 changed files with 216 additions and 238 deletions

View file

@ -17,6 +17,7 @@ BUNDLE_CMD="node ${KEYMAN_ROOT}/common/web/es-bundling/build/common-bundle.mjs"
builder_describe \
"Compiles the web-oriented utility function module." \
"@/web/src/tools/testing/recorder-core test" \
"@/web/src/engine/js-processor test" \
"@/common/web/keyman-version" \
"@/common/web/es-bundling" \
"@/common/web/types" \
@ -64,10 +65,10 @@ function do_build() {
--platform node
# Tests
builder_echo "Bundle tests"
${BUNDLE_CMD} "${KEYMAN_ROOT}/common/web/keyboard-processor/build/tests/dom/cases/domKeyboardLoader.spec.js" \
--out "${KEYMAN_ROOT}/common/web/keyboard-processor/build/tests/dom/domKeyboardLoader.spec.mjs" \
--format esm
# builder_echo "Bundle tests"
# ${BUNDLE_CMD} "${KEYMAN_ROOT}/common/web/keyboard-processor/build/tests/dom/cases/domKeyboardLoader.spec.js" \
# --out "${KEYMAN_ROOT}/common/web/keyboard-processor/build/tests/dom/domKeyboardLoader.spec.mjs" \
# --format esm
# Declaration bundling.
builder_echo "Declaration bundling"
@ -92,4 +93,4 @@ function do_test() {
builder_run_action configure do_configure
builder_run_action clean rm -rf ./build
builder_run_action build do_build
builder_run_action test do_test
# builder_run_action test do_test

View file

@ -29,18 +29,12 @@ export * from "./text/codes.js";
export * from "./text/deadkeys.js";
export { default as DefaultRules } from "./text/defaultRules.js";
export * from "./text/defaultRules.js";
export { default as KeyboardInterface } from "./text/kbdInterface.js";
export * from "./text/kbdInterface.js";
export { default as KeyboardProcessor } from "./text/keyboardProcessor.js";
export * from "./text/keyboardProcessor.js";
export { default as KeyEvent } from "./text/keyEvent.js";
export * from "./text/keyEvent.js";
export { default as KeyMapping } from "./text/keyMapping.js";
export { default as OutputTarget } from "./text/outputTarget.js";
export * from "./text/outputTarget.js";
export { default as RuleBehavior } from "./text/ruleBehavior.js";
export * from "./text/stringDivergence.js";
export * from "./text/systemStores.js";
export * from "@keymanapp/web-utils";
@ -48,4 +42,4 @@ export * from "@keymanapp/web-utils";
// Without the line below... OutputTarget would likely be aliased there, as it's
// the last `export { default as _ }` => `export * from` pairing seen above.
export default undefined;
export default undefined;

View file

@ -6,11 +6,11 @@ import type OutputTarget from "../text/outputTarget.js";
import { ModifierKeyConstants, TouchLayout } from "@keymanapp/common-types";
type TouchLayoutSpec = TouchLayout.TouchLayoutPlatform & { isDefault?: boolean};
import type { ComplexKeyboardStore } from "../text/kbdInterface.js";
import { Version, DeviceSpec } from "@keymanapp/web-utils";
import StateKeyMap from "./stateKeyMap.js";
type ComplexKeyboardStore = ( string | { t: 'd', d: number } | { ['t']: 'b' })[];
/**
* Stores preprocessed properties of a keyboard for quick retrieval later.
*/

View file

@ -1,5 +1,6 @@
import Keyboard from "./keyboard.js";
import Codes from "../text/codes.js";
import { DeviceSpec } from '@keymanapp/web-utils';
/**
* Defines members of the top-level `keyman` global object necessary to guarantee
@ -40,6 +41,8 @@ export const MinimalKeymanGlobal: KeyboardKeymanGlobal = {
export class KeyboardHarness {
public readonly _jsGlobal: any;
public readonly keymanGlobal: KeyboardKeymanGlobal;
activeDevice: DeviceSpec;
/**
* Constructs and configures a harness for receiving dynamically-loaded Keyman keyboards.

View file

@ -1,32 +1,28 @@
// TODO: Move to separate folder: 'codes'
// We should start splitting off code needed by keyboards even without a KeyboardProcessor active.
// There's an upcoming `/common/web/types` package that 'codes' and 'keyboards' may fit well within.
/*
* Keyman is copyright (C) SIL International. MIT License.
*
* Implementation of default rules
*/
import { ModifierKeyConstants} from '@keymanapp/common-types';
import Codes from "./codes.js";
import type KeyEvent from "./keyEvent.js";
import type OutputTarget from "./outputTarget.js";
// The only members referenced are to produce warning and error logs. A little abstraction
// via an optional 'logger' interface can maintain it while facilitating a the split alluded
// to above.
//
// Alternatively, we could just... not take in the parameter at all, which'd also facilitate
// the future modularization effort.
import RuleBehavior from "./ruleBehavior.js";
import { ModifierKeyConstants } from '@keymanapp/common-types';
import Codes from './codes.js';
import type KeyEvent from './keyEvent.js';
import type OutputTarget from './outputTarget.js';
export enum EmulationKeystrokes {
Enter = '\n',
Backspace = '\b'
}
export class LogMessages {
errorLog?: string;
warningLog?: string;
}
/**
* Defines a collection of static library functions that define KeymanWeb's default (implied) keyboard rule behaviors.
*/
export default class DefaultRules {
public constructor() {
}
codeForEvent(Lkc: KeyEvent) {
return Codes.keyCodes[Lkc.kName] || Lkc.Lcode;;
}
@ -35,7 +31,7 @@ export default class DefaultRules {
* Serves as a default keycode lookup table. This may be referenced safely by mnemonic handling without fear of side-effects.
* Also used by Processor.defaultRuleBehavior to generate output after filtering for special cases.
*/
public forAny(Lkc: KeyEvent, isMnemonic: boolean, ruleBehavior?: RuleBehavior) {
public forAny(Lkc: KeyEvent, isMnemonic: boolean, logMessages?: LogMessages): string {
var char = '';
// A pretty simple table of lookups, corresponding VERY closely to the original defaultKeyOutput.
@ -43,9 +39,9 @@ export default class DefaultRules {
return char;
} else if(!isMnemonic && ((char = this.forNumpadKeys(Lkc)) != null)) {
return char;
} else if((char = this.forUnicodeKeynames(Lkc, ruleBehavior)) != null) {
} else if((char = this.forUnicodeKeynames(Lkc, logMessages)) != null) {
return char;
} else if((char = this.forBaseKeys(Lkc, ruleBehavior)) != null) {
} else if((char = this.forBaseKeys(Lkc, logMessages)) != null) {
return char;
} else {
// // For headless and embeddded, we may well allow '\t'. It's DOM mode that has other uses.
@ -152,7 +148,7 @@ export default class DefaultRules {
// Test for fall back to U_xxxxxx key id
// For this first test, we ignore the keyCode and use the keyName
public forUnicodeKeynames(Lkc: KeyEvent, ruleBehavior?: RuleBehavior) {
public forUnicodeKeynames(Lkc: KeyEvent, logMessages?: LogMessages) {
const keyName = Lkc.kName;
// Test for fall back to U_xxxxxx key id
@ -169,8 +165,8 @@ export default class DefaultRules {
// Code points [U_0000 - U_001F] and [U_0080 - U_009F] refer to Unicode C0 and C1 control codes.
// Check the codePoint number and do not allow output of these codes via U_xxxxxx shortcuts.
// Also handles invalid identifiers (e.g. `U_ghij`) for which parseInt returns NaN
if(ruleBehavior) {
ruleBehavior.errorLog = ("Suppressing Unicode control code in " + keyName);
if(logMessages) {
logMessages.errorLog = ("Suppressing Unicode control code in " + keyName);
}
// We'll attempt to add valid chars
continue;
@ -185,17 +181,17 @@ export default class DefaultRules {
// Test for otherwise unimplemented keys on the the base default & shift layers.
// Those keys must be blocked by keyboard rules if intentionally unimplemented; otherwise, this function will trigger.
public forBaseKeys(Lkc: KeyEvent, ruleBehavior?: RuleBehavior) {
public forBaseKeys(Lkc: KeyEvent, logMessages?: LogMessages) {
let n = Lkc.Lcode;
let keyShiftState = Lkc.Lmodifiers;
// check if exact match to SHIFT's code. Only the 'default' and 'shift' layers should have default key outputs.
// TODO: Extend to allow AltGr as well - better mnemonic support.
if(keyShiftState == ModifierKeyConstants.K_SHIFTFLAG) {
if (keyShiftState == ModifierKeyConstants.K_SHIFTFLAG) {
keyShiftState = 1;
} else if(keyShiftState != 0) {
if(ruleBehavior) {
ruleBehavior.warningLog = "KMW only defines default key output for the 'default' and 'shift' layers!";
if(logMessages) {
logMessages.warningLog = "KMW only defines default key output for the 'default' and 'shift' layers!";
}
return null;
}
@ -216,8 +212,8 @@ export default class DefaultRules {
return keyShiftState ? '|' : '\\';
}
} catch (e) {
if(ruleBehavior) {
ruleBehavior.errorLog = "Error detected with default mapping for key: code = " + n + ", shift state = " + (keyShiftState == 1 ? 'shift' : 'default');
if(logMessages) {
logMessages.errorLog = "Error detected with default mapping for key: code = " + n + ", shift state = " + (keyShiftState == 1 ? 'shift' : 'default');
}
}

View file

@ -10,12 +10,12 @@ import type Keyboard from "../keyboards/keyboard.js";
import { type DeviceSpec } from "@keymanapp/web-utils";
import Codes from './codes.js';
import DefaultRules from './defaultRules.js';
import DefaultRules from "./defaultRules.js";
import { ActiveKeyBase } from "../index.js";
// Represents a probability distribution over a keyboard's keys.
// Defined here to avoid compilation issues.
export type KeyDistribution = {keySpec: ActiveKeyBase, p: number}[];
export type KeyDistribution = { keySpec: ActiveKeyBase, p: number }[];
/**
* A simple instance of the standard 'default rules' for keystroke processing from the
@ -188,4 +188,4 @@ export default class KeyEvent implements KeyEventSpec {
}
}
}
};
};

View file

@ -1,5 +0,0 @@
Automated tests in this subfolder and its children are designed to facilitate simple, browser-independent
unit tests that are DOM-reliant.
Tests for anything that may reasonably vary depending upon the browser used to run the code should go under
the "integrated" folder instead.

View file

@ -1,13 +0,0 @@
// @ts-check
import BASE_CONFIG from './web-test-runner.config.mjs';
import teamcityReporter from '@keymanapp/common-test-resources/test-runner-TC-reporter.mjs';
import { sessionStabilityReporter } from '@keymanapp/common-test-resources/test-runner-stability-reporter.mjs';
/** @type {import('@web/test-runner').TestRunnerConfig} */
export default {
...BASE_CONFIG,
reporters: [
teamcityReporter(), /* custom-written, for CI-friendly reports */
sessionStabilityReporter({ciMode: true})
]
}

View file

@ -1,62 +0,0 @@
// @ts-check
import { devices, playwrightLauncher } from '@web/test-runner-playwright';
import { esbuildPlugin } from '@web/dev-server-esbuild';
import { defaultReporter, summaryReporter } from '@web/test-runner';
import { LauncherWrapper, sessionStabilityReporter } from '@keymanapp/common-test-resources/test-runner-stability-reporter.mjs';
import { importMapsPlugin } from '@web/dev-server-import-maps';
import { dirname, resolve } from 'path';
import { fileURLToPath } from 'url';
const dir = dirname(fileURLToPath(import.meta.url));
const KEYMAN_ROOT = resolve(dir, '../../../../..');
/** @type {import('@web/test-runner').TestRunnerConfig} */
export default {
// debug: true,
browsers: [
new LauncherWrapper(playwrightLauncher({ product: 'chromium' })),
new LauncherWrapper(playwrightLauncher({ product: 'firefox' })),
new LauncherWrapper(playwrightLauncher({ product: 'webkit', concurrency: 1 })),
],
concurrency: 10,
nodeResolve: true,
files: [
'build/tests/dom/**/*.spec.mjs'
],
middleware: [
// Rewrites short-hand paths for test resources, making them fully relative to the repo root.
function rewriteResourcePath(context, next) {
if(context.url.startsWith('/resources/')) {
context.url = '/common/test' + context.url;
}
return next();
}
],
plugins: [
esbuildPlugin({ts: true, target: 'auto'}),
importMapsPlugin({
inject: {
importMap: {
// Redirects `eventemitter3` imports to the bundled ESM library. The standard import is an
// ESM wrapper around the CommonJS implementation, and WTR fails when it hits the CommonJS.
imports: {
'eventemitter3': '/node_modules/eventemitter3/dist/eventemitter3.esm.js'
}
}
}
})
],
reporters: [
summaryReporter({}), /* local-dev mocha-style */
sessionStabilityReporter({}),
defaultReporter({})
],
/*
Un-comment the next two lines for easy interactive debugging; it'll launch the
test page in your preferred browser.
*/
// open: true,
// manual: true,
rootDir: KEYMAN_ROOT
}

View file

@ -1,11 +0,0 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"baseUrl": "../",
"outDir": "../build/tests/",
"tsBuildInfoFile": "../build/tests/tsconfig.tsbuildinfo",
"rootDir": "./"
},
"include": [ "./dom/**/*.ts"],
"exclude": []
}

View file

@ -9,7 +9,6 @@
"references": [
{ "path": "./src/keyboards/loaders/tsconfig.dom.json" },
{ "path": "./src/keyboards/loaders/tsconfig.node.json" },
{ "path": "./tests/tsconfig.json" },
],
// Actual main-body compilation is in tsconfig.json. This config is just a wrapper
// to trigger all three components at once.

View file

@ -82,6 +82,7 @@ title: Dependency Graph
graph TD;
OSK["/web/src/engine/osk"];
KP["@keymanapp/keyboard-processor<br>(/common/web/keyboard-processor)"];
JSProc["/web/src/engine/js-processor"];
OSK-->KP;
WebUtils["@keymanapp/web-utils<br>(/common/web/utils)"];
KP---->WebUtils;
@ -107,6 +108,7 @@ graph TD;
Fully headless components`"]
direction LR
KP;
JSProc-->KP;
WebUtils;
PredText;
Gestures;
@ -127,9 +129,9 @@ graph TD;
OSK-->Gestures;
Interfaces["/web/src/engine/interfaces"];
Interfaces-->KP;
Interfaces-->JSProc;
OSK-->Interfaces;
CommonEngine["/web/src/engine/main"];
CommonEngine-->Interfaces;
CommonEngine-->Device;
CommonEngine-->KeyboardCache;
CommonEngine-->OSK;

View file

@ -27,6 +27,7 @@ builder_describe "Builds engine modules for Keyman Engine for Web (KMW)." \
":engine/dom-utils A common subset of function used for DOM calculations, layout, etc" \
":engine/events Specialized classes utilized to support KMW API events" \
":engine/element-wrappers Subset used to integrate with website elements" \
":engine/js-processor Build JS processor for KMW" \
":engine/main Builds all common code used by KMW's app/-level targets" \
":engine/osk Builds the Web OSK module" \
":engine/package-cache Subset used to collate keyboards and request them from the cloud" \
@ -57,6 +58,7 @@ builder_describe_outputs \
build:engine/dom-utils "/web/build/engine/dom-utils/obj/index.js" \
build:engine/events "/web/build/engine/events/lib/index.mjs" \
build:engine/element-wrappers "/web/build/engine/element-wrappers/lib/index.mjs" \
build:engine/js-processor "/web/build/engine/js-processor/lib/index.mjs" \
build:engine/main "/web/build/engine/main/lib/index.mjs" \
build:engine/osk "/web/build/engine/osk/lib/index.mjs" \
build:engine/package-cache "/web/build/engine/package-cache/lib/index.mjs" \

View file

@ -37,6 +37,11 @@
"types": "./build/engine/events/obj/index.d.ts",
"import": "./build/engine/events/obj/index.js"
},
"./engine/js-processor": {
"es6-bundling": "./src/engine/js-processor/src/index.ts",
"types": "./build/engine/js-processor/obj/index.d.ts",
"import": "./build/engine/js-processor/obj/index.js"
},
"./engine/package-cache": {
"es6-bundling": "./src/engine/package-cache/src/index.ts",
"types": "./build/engine/package-cache/obj/index.d.ts",

View file

@ -1,4 +1,4 @@
import { type KeyboardInterface } from '@keymanapp/keyboard-processor';
import { type KeyboardInterface } from 'keyman/engine/js-processor';
import { DesignIFrame, OutputTarget } from 'keyman/engine/element-wrappers';
// Utility object used to handle beep (keyboard error response) operations.

View file

@ -1,7 +1,8 @@
import { EngineConfiguration, InitOptionSpec, InitOptionDefaults } from "keyman/engine/main";
import { OutputTarget as DOMOutputTarget } from 'keyman/engine/element-wrappers';
import { isEmptyTransform, OutputTarget, RuleBehavior } from '@keymanapp/keyboard-processor';
import { isEmptyTransform, OutputTarget } from '@keymanapp/keyboard-processor';
import { RuleBehavior } from 'keyman/engine/js-processor';
import { AlertHost } from "./utils/alertHost.js";
import { whenDocumentReady } from "./utils/documentReady.js";

View file

@ -1,4 +1,5 @@
import { Codes, DeviceSpec, KeyEvent, KeyMapping, Keyboard, KeyboardProcessor } from '@keymanapp/keyboard-processor';
import { Codes, DeviceSpec, KeyEvent, KeyMapping, Keyboard } from '@keymanapp/keyboard-processor';
import { KeyboardProcessor } from 'keyman/engine/js-processor';
import { ModifierKeyConstants } from '@keymanapp/common-types';
import { HardKeyboard, processForMnemonicsAndLegacy } from 'keyman/engine/main';

View file

@ -1,4 +1,5 @@
import { DefaultRules, DeviceSpec, RuleBehavior } from '@keymanapp/keyboard-processor'
import { DeviceSpec, DefaultRules } from '@keymanapp/keyboard-processor'
import { RuleBehavior } from 'keyman/engine/js-processor';
import { KeymanEngine as KeymanEngineBase, KeyboardInterface } from 'keyman/engine/main';
import { AnchoredOSKView, ViewConfiguration, StaticActivator } from 'keyman/engine/osk';
import { getAbsoluteX, getAbsoluteY } from 'keyman/engine/dom-utils';

View file

@ -1,4 +1,3 @@
export { DomEventTracker } from './domEventTracker.js';
export { EmitterListenerSpy } from './emitterListenerSpy.js';
export * from './keyEventSource.interface.js';
export * from './legacyEventEmitter.js';

View file

@ -15,6 +15,7 @@ SUBPROJECT_NAME=engine/interfaces
builder_describe "Builds configuration subclasses used by the Keyman Engine for Web (KMW)." \
"@/common/web/es-bundling" \
"@/common/web/keyboard-processor" \
"@/web/src/engine/js-processor" \
"clean" \
"configure" \
"build" \

View file

@ -1,6 +1,7 @@
import { EventEmitter } from "eventemitter3";
import { type LanguageProcessorSpec , ReadySuggestions, type InvalidateSourceEnum, StateChangeHandler } from './languageProcessor.interface.js';
import { type KeyboardProcessor, type OutputTarget } from "@keymanapp/keyboard-processor";
import { type OutputTarget } from "@keymanapp/keyboard-processor";
import { type KeyboardProcessor } from 'keyman/engine/js-processor';
interface PredictionContextEventMap {
update: (suggestions: Suggestion[]) => void;

View file

@ -13,5 +13,6 @@
"references": [
{ "path": "../../../../common/web/keyboard-processor" },
{ "path": "../js-processor" }
]
}

View file

@ -0,0 +1,41 @@
#!/usr/bin/env bash
## START STANDARD BUILD SCRIPT INCLUDE
# adjust relative paths as necessary
THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
. "${THIS_SCRIPT%/*}/../../../../resources/build/builder.inc.sh"
## END STANDARD BUILD SCRIPT INCLUDE
SUBPROJECT_NAME=engine/js-processor
. "${KEYMAN_ROOT}/web/common.inc.sh"
. "${KEYMAN_ROOT}/resources/shellHelperFunctions.sh"
# ################################ Main script ################################
builder_describe "Builds configuration subclasses used by the Keyman Engine for Web (KMW)." \
"clean" \
"configure" \
"build" \
"test" \
"--ci+ Set to utilize CI-based test configurations & reporting."
builder_describe_outputs \
configure "/node_modules" \
build "/web/build/${SUBPROJECT_NAME}/lib/index.mjs"
builder_parse "$@"
#### Build action definitions ####
do_build () {
compile "${SUBPROJECT_NAME}"
${BUNDLE_CMD} "${KEYMAN_ROOT}/web/build/${SUBPROJECT_NAME}/obj/index.js" \
--out "${KEYMAN_ROOT}/web/build/${SUBPROJECT_NAME}/lib/index.mjs" \
--format esm
}
builder_run_action configure verify_npm_setup
builder_run_action clean rm -rf "${KEYMAN_ROOT}/web/build/${SUBPROJECT_NAME}"
builder_run_action build do_build

View file

@ -0,0 +1,6 @@
export { default as KeyboardProcessor } from "./keyboardProcessor.js";
export * from "./keyboardProcessor.js";
export { default as RuleBehavior } from "./ruleBehavior.js";
export * from './kbdInterface.js';
export { default as KeyboardInterface } from "./kbdInterface.js";
export * from "./systemStores.js";

View file

@ -7,18 +7,9 @@
import { type DeviceSpec } from "@keymanapp/web-utils";
import { ModifierKeyConstants } from '@keymanapp/common-types';
import Codes from "./codes.js";
import type KeyEvent from "./keyEvent.js";
import type { Deadkey } from "./deadkeys.js";
import KeyMapping from "./keyMapping.js";
import { SystemStore, MutableSystemStore, PlatformSystemStore } from "./systemStores.js";
import type { VariableStoreSerializer } from "./keyboardProcessor.js";
import type OutputTarget from "./outputTarget.js";
import { Mock } from "./outputTarget.js";
import { Codes, type KeyEvent, type Deadkey, KeyMapping, type OutputTarget, Mock, Keyboard, KeyboardHarness, KeyboardKeymanGlobal, VariableStoreDictionary } from "@keymanapp/keyboard-processor";
import RuleBehavior from "./ruleBehavior.js";
import Keyboard, { VariableStoreDictionary } from "../keyboards/keyboard.js";
import { KeyboardHarness, KeyboardKeymanGlobal } from "../keyboards/keyboardHarness.js";
import { ComplexKeyboardStore, type KeyboardStore, KeyboardStoreElement, SystemStoreIDs, SystemStore, MutableSystemStore, PlatformSystemStore, VariableStore, VariableStoreSerializer } from "./systemStores.js";
//#endregion
@ -30,20 +21,6 @@ export class KeyInformation {
modifiers: number;
}
/*
* Type alias definitions to reflect the parameters of the fullContextMatch() callback (KMW 10+).
* No constructors or methods since keyboards will not utilize the same backing prototype, and
* property names are shorthanded to promote minification.
*/
type PlainKeyboardStore = string;
export type KeyboardStoreElement = (string|StoreNonCharEntry);
export type ComplexKeyboardStore = KeyboardStoreElement[];
type KeyboardStore = PlainKeyboardStore | ComplexKeyboardStore;
export type VariableStore = {[name: string]: string};
type RuleChar = string;
class RuleDeadkey {
@ -70,7 +47,7 @@ class ContextAny {
/**
* If set to true, negates the 'any'.
*/
['n']: boolean|0|1;
['n']: boolean | 0 | 1;
}
class RuleIndex {
@ -115,7 +92,7 @@ class StoreBeep {
type ContextNonCharEntry = RuleDeadkey | ContextAny | RuleIndex | ContextEx | ContextNul;
type ContextEntry = RuleChar | ContextNonCharEntry;
type StoreNonCharEntry = RuleDeadkey | StoreBeep;
export type StoreNonCharEntry = RuleDeadkey | StoreBeep;
/**
* Cache of context storing and retrieving return values from KC
@ -183,13 +160,6 @@ class CachedContextEx {
}
};
export enum SystemStoreIDs {
TSS_LAYER = 33,
TSS_PLATFORM = 31,
TSS_NEWLAYER = 42,
TSS_OLDLAYER = 43
}
//#endregion
export default class KeyboardInterface extends KeyboardHarness {

View file

@ -1,23 +1,21 @@
/*
* Keyman is copyright (C) SIL International. MIT License.
*
* Implementation of the JavaScript keyboard processor
*/
// #region Big ol' list of imports
import { EventEmitter } from 'eventemitter3';
import Codes from "./codes.js";
import type Keyboard from "../keyboards/keyboard.js";
import { MinimalKeymanGlobal } from '../keyboards/keyboardHarness.js';
import KeyEvent from "./keyEvent.js";
import { Layouts } from "../keyboards/defaultLayouts.js";
import type { MutableSystemStore } from "./systemStores.js";
import DefaultRules, { EmulationKeystrokes } from "./defaultRules.js";
import type OutputTarget from "./outputTarget.js";
import { Mock } from "./outputTarget.js";
import KeyboardInterface, { SystemStoreIDs, VariableStore } from "./kbdInterface.js";
import RuleBehavior from "./ruleBehavior.js";
import { DeviceSpec, globalObject } from "@keymanapp/web-utils";
import { ModifierKeyConstants } from '@keymanapp/common-types';
import {
Codes, type Keyboard, MinimalKeymanGlobal, KeyEvent, Layouts,
type OutputTarget, Mock, DefaultRules, EmulationKeystrokes
} from "@keymanapp/keyboard-processor";
import RuleBehavior from "./ruleBehavior.js";
import KeyboardInterface from './kbdInterface.js';
import { DeviceSpec, globalObject } from "@keymanapp/web-utils";
import { type MutableSystemStore, SystemStoreIDs } from "./systemStores.js";
// #endregion
@ -26,11 +24,6 @@ import { ModifierKeyConstants } from '@keymanapp/common-types';
export type BeepHandler = (outputTarget: OutputTarget) => void;
export type LogMessageHandler = (str: string) => void;
export interface VariableStoreSerializer {
loadStore(keyboardID: string, storeName: string): VariableStore;
saveStore(keyboardID: string, storeName: string, storeMap: VariableStore): void;
}
export interface ProcessorInitOptions {
baseLayout?: string;
keyboardInterface?: KeyboardInterface;
@ -174,7 +167,7 @@ export default class KeyboardProcessor extends EventEmitter<EventMap> {
let isMnemonic = this.activeKeyboard && this.activeKeyboard.isMnemonic;
if(!matched) {
if((char = this.defaultRules.forAny(Lkc, isMnemonic)) != null) {
if((char = this.defaultRules.forAny(Lkc, isMnemonic, ruleBehavior)) != null) {
special = this.defaultRules.forSpecialEmulation(Lkc)
if(special == EmulationKeystrokes.Backspace) {
// A browser's default backspace may fail to delete both parts of an SMP character.
@ -277,7 +270,8 @@ export default class KeyboardProcessor extends EventEmitter<EventMap> {
const lockNames = ['CAPS', 'NUM_LOCK', 'SCROLL_LOCK'] as const;
const lockKeys = ['K_CAPS', 'K_NUMLOCK', 'K_SCROLL'] as const;
const lockModifiers = [ ModifierKeyConstants.CAPITALFLAG, ModifierKeyConstants.NUMLOCKFLAG, ModifierKeyConstants.SCROLLFLAG] as const;
const lockModifiers = [ModifierKeyConstants.CAPITALFLAG, ModifierKeyConstants.NUMLOCKFLAG, ModifierKeyConstants.SCROLLFLAG] as const;
if(!this.activeKeyboard) {
return true;
@ -329,6 +323,8 @@ export default class KeyboardProcessor extends EventEmitter<EventMap> {
const lockModifiers = [ModifierKeyConstants.CAPITALFLAG, ModifierKeyConstants.NUMLOCKFLAG, ModifierKeyConstants.SCROLLFLAG] as const;
const noLockModifers = [ModifierKeyConstants.NOTCAPITALFLAG, ModifierKeyConstants.NOTNUMLOCKFLAG, ModifierKeyConstants.NOTSCROLLFLAG] as const;
for(let i=0; i < lockKeys.length; i++) {
const key = lockKeys[i];
const flag = this.stateKeys[key];

View file

@ -1,9 +1,8 @@
///<reference types="@keymanapp/models-types" />
import KeyboardProcessor from "./keyboardProcessor.js";
import OutputTarget, { Mock, type Transcription } from "./outputTarget.js";
import { VariableStoreDictionary } from "../keyboards/keyboard.js";
import type { VariableStore } from "./kbdInterface.js";
import { OutputTarget, Mock, type Transcription, VariableStoreDictionary } from "@keymanapp/keyboard-processor";
import { type VariableStore } from "./systemStores.js";
/**
* Represents the commands and state changes that result from a matched keyboard rule.

View file

@ -1,5 +1,31 @@
import type KeyboardInterface from "./kbdInterface.js";
import { SystemStoreIDs } from "./kbdInterface.js";
import { type KeyboardHarness } from '@keymanapp/keyboard-processor';
import { StoreNonCharEntry } from './kbdInterface.js';
export enum SystemStoreIDs {
TSS_LAYER = 33,
TSS_PLATFORM = 31,
TSS_NEWLAYER = 42,
TSS_OLDLAYER = 43
}
/*
* Type alias definitions to reflect the parameters of the fullContextMatch() callback (KMW 10+).
* No constructors or methods since keyboards will not utilize the same backing prototype, and
* property names are shorthanded to promote minification.
*/
type PlainKeyboardStore = string;
export type KeyboardStoreElement = (string | StoreNonCharEntry);
export type ComplexKeyboardStore = KeyboardStoreElement[];
export type KeyboardStore = PlainKeyboardStore | ComplexKeyboardStore;
export type VariableStore = { [name: string]: string };
export interface VariableStoreSerializer {
loadStore(keyboardID: string, storeName: string): VariableStore;
saveStore(keyboardID: string, storeName: string, storeMap: VariableStore): void;
}
/**
* Defines common behaviors associated with system stores.
@ -61,9 +87,9 @@ export class MutableSystemStore extends SystemStore {
* Handles checks against the current platform.
*/
export class PlatformSystemStore extends SystemStore {
private readonly kbdInterface: KeyboardInterface;
private readonly kbdInterface: KeyboardHarness;
constructor(keyboardInterface: KeyboardInterface) {
constructor(keyboardInterface: KeyboardHarness) {
super(SystemStoreIDs.TSS_PLATFORM);
this.kbdInterface = keyboardInterface;
@ -131,4 +157,4 @@ export class PlatformSystemStore extends SystemStore {
// Everything we checked against was valid and had matches - it's a match!
return true;
}
}
}

View file

@ -0,0 +1,13 @@
{
// While the actual references themselves are headless, it compiles against the DOM-reliant OSK module.
"extends": "../../tsconfig.dom.json",
"compilerOptions": {
"baseUrl": "./",
"outDir": "../../../build/engine/js-processor/obj/",
"tsBuildInfoFile": "../../../build/engine/js-processor/obj/tsconfig.tsbuildinfo",
"rootDir": "./src"
},
"include": [ "**/*.ts" ],
}

View file

@ -18,6 +18,7 @@ builder_describe "Builds the Keyman Engine for Web's common top-level base class
"@/common/predictive-text" \
"@/web/src/engine/interfaces build" \
"@/web/src/engine/device-detect build" \
"@/web/src/engine/js-processor build" \
"@/web/src/engine/package-cache build" \
"@/web/src/engine/osk build" \
"@/developer/src/kmc-model test" \

View file

@ -1,5 +1,6 @@
import { EventEmitter } from 'eventemitter3';
import { ManagedPromise, type Keyboard, type KeyboardInterface, type OutputTarget } from '@keymanapp/keyboard-processor';
import { ManagedPromise, type Keyboard, type OutputTarget } from '@keymanapp/keyboard-processor';
import { type KeyboardInterface } from 'keyman/engine/js-processor';
import { StubAndKeyboardCache, type KeyboardStub } from 'keyman/engine/package-cache';
import { PredictionContext } from 'keyman/engine/interfaces';
import { EngineConfiguration } from './engineConfiguration.js';

View file

@ -1,6 +1,7 @@
import { EventEmitter } from "eventemitter3";
import { DeviceSpec, KeyboardProperties, ManagedPromise, OutputTarget, physicalKeyDeviceAlias, RuleBehavior, SpacebarText } from "@keymanapp/keyboard-processor";
import { DeviceSpec, KeyboardProperties, ManagedPromise, OutputTarget, physicalKeyDeviceAlias, SpacebarText } from "@keymanapp/keyboard-processor";
import { RuleBehavior } from 'keyman/engine/js-processor';
import { PathConfiguration, PathOptionDefaults, PathOptionSpec } from "keyman/engine/interfaces";
import { Device } from "keyman/engine/device-detect";
import { KeyboardStub } from "keyman/engine/package-cache";

View file

@ -1,6 +1,7 @@
import { EventEmitter } from "eventemitter3";
import { Keyboard, KeyMapping, KeyEvent, type RuleBehavior, Codes } from "@keymanapp/keyboard-processor";
import { KeyEventSourceInterface } from 'keyman/engine/events';
import { Keyboard, KeyMapping, KeyEvent, Codes } from "@keymanapp/keyboard-processor";
import { type RuleBehavior } from 'keyman/engine/js-processor';
import { KeyEventSourceInterface } from 'keyman/engine/osk';
import { ModifierKeyConstants } from '@keymanapp/common-types';
interface EventMap {

View file

@ -12,15 +12,12 @@ import {
Codes,
isEmptyTransform,
type Keyboard,
KeyboardInterface,
KeyboardProcessor,
type KeyEvent,
Mock,
type OutputTarget,
type ProcessorInitOptions,
RuleBehavior,
SystemStoreIDs,
} from "@keymanapp/keyboard-processor";
import { KeyboardInterface, KeyboardProcessor, RuleBehavior, type ProcessorInitOptions, SystemStoreIDs } from 'keyman/engine/js-processor';
import { TranscriptionCache } from "./transcriptionCache.js";
export class InputProcessor {

View file

@ -1,6 +1,5 @@
import {
KeyboardInterface as KeyboardInterfaceBase, KeyboardObject,
} from "@keymanapp/keyboard-processor";
import { KeyboardObject } from "@keymanapp/keyboard-processor";
import { KeyboardInterface as KeyboardInterfaceBase } from 'keyman/engine/js-processor';
import { KeyboardStub, RawKeyboardStub, toUnprefixedKeyboardId as unprefixed } from 'keyman/engine/package-cache';
import { ContextManagerBase } from './contextManagerBase.js';

View file

@ -1,4 +1,5 @@
import { type Keyboard, KeyboardKeymanGlobal, ProcessorInitOptions } from "@keymanapp/keyboard-processor";
import { type KeyEvent, type Keyboard, KeyboardKeymanGlobal } from "@keymanapp/keyboard-processor";
import { ProcessorInitOptions, RuleBehavior } from 'keyman/engine/js-processor';
import { DOMKeyboardLoader as KeyboardLoader } from "@keymanapp/keyboard-processor/dom-keyboard-loader";
import { InputProcessor } from './headless/inputProcessor.js';
import { OSKView } from "keyman/engine/osk";
@ -10,7 +11,7 @@ import KeyboardInterface from "./keyboardInterface.js";
import { ContextManagerBase } from "./contextManagerBase.js";
import HardKeyboardBase from "./hardKeyboard.js";
import { LegacyAPIEvents } from "./legacyAPIEvents.js";
import { KeyEventHandler, EventNames, EventListener, LegacyEventEmitter } from "keyman/engine/events";
import { EventNames, EventListener, LegacyEventEmitter } from "keyman/engine/events";
import DOMCloudRequester from "keyman/engine/package-cache/dom-requester";
import KEYMAN_VERSION from "@keymanapp/keyman-version";
@ -29,6 +30,9 @@ function determineBaseLayout(): string {
}
}
export type KeyEventFullResultCallback = (result: RuleBehavior, error?: Error) => void;
export type KeyEventFullHandler = (event: KeyEvent, callback?: KeyEventFullResultCallback) => void;
export default class KeymanEngine<
Configuration extends EngineConfiguration,
ContextManager extends ContextManagerBase<any>,
@ -47,7 +51,7 @@ export default class KeymanEngine<
protected keyEventRefocus?: () => void;
private keyEventListener: KeyEventHandler = (event, callback) => {
private keyEventListener: KeyEventFullHandler = (event, callback) => {
const outputTarget = this.contextManager.activeTarget;
if(!this.contextManager.activeKeyboard || !outputTarget) {

View file

@ -1,4 +1,4 @@
import { VariableStore, VariableStoreSerializer } from "@keymanapp/keyboard-processor";
import { VariableStore, VariableStoreSerializer } from 'keyman/engine/js-processor';
import { CookieSerializer } from "keyman/engine/dom-utils";
// While there's little reason we couldn't store all of a keyboard's store values within

View file

@ -15,5 +15,6 @@
{ "path": "../osk" },
{ "path": "../package-cache" },
{ "path": "../interfaces" },
{ "path": "../js-processor" }
]
}

View file

@ -4,6 +4,7 @@ export { default as OSKView } from './views/oskView.js';
export { default as FloatingOSKView, FloatingOSKViewConfiguration } from './views/floatingOskView.js';
export { default as AnchoredOSKView } from './views/anchoredOskView.js';
export { default as InlinedOSKView } from './views/inlinedOskView.js';
export { type KeyEventResultCallback, type KeyEventHandler, KeyEventSourceInterface } from './views/keyEventSource.interface.js';
export { BannerController } from './banner/bannerController.js';
// Is referenced by at least one desktop UI module.
export { FloatingOSKCookie as FloatingOSKViewCookie } from './views/floatingOskCookie.js';

View file

@ -1,5 +1,6 @@
import { EventEmitter } from "eventemitter3";
import { type KeyEvent, type RuleBehavior } from "@keymanapp/keyboard-processor";
import { type KeyEvent } from "@keymanapp/keyboard-processor";
import { type RuleBehavior } from 'keyman/engine/js-processor';
export type KeyEventResultCallback = (result: RuleBehavior, error?: Error) => void;
export type KeyEventHandler = (event: KeyEvent, callback?: KeyEventResultCallback) => void;

View file

@ -16,16 +16,16 @@ import {
Keyboard,
KeyboardProperties,
ManagedPromise,
type MinimalCodesInterface,
type MutableSystemStore,
type SystemStoreMutationHandler
type MinimalCodesInterface
} from '@keymanapp/keyboard-processor';
import { createUnselectableElement, getAbsoluteX, getAbsoluteY, StylesheetManager } from 'keyman/engine/dom-utils';
import { EventListener, KeyEventHandler, KeyEventSourceInterface, LegacyEventEmitter } from 'keyman/engine/events';
import { EventListener, LegacyEventEmitter } from 'keyman/engine/events';
import { type MutableSystemStore, type SystemStoreMutationHandler } from 'keyman/engine/js-processor';
import Configuration from '../config/viewConfiguration.js';
import Activator, { StaticActivator } from './activator.js';
import TouchEventPromiseMap from './touchEventPromiseMap.js';
import { KeyEventHandler, KeyEventSourceInterface } from './keyEventSource.interface.js';
import { DEFAULT_GESTURE_PARAMS, GestureParams } from '../input/gestures/specsForLayout.js';
// These will likely be eliminated from THIS file at some point.\

View file

@ -31,7 +31,7 @@ import {
import { createStyleSheet, StylesheetManager } from 'keyman/engine/dom-utils';
import { KeyEventHandler, KeyEventResultCallback } from 'keyman/engine/events';
import { KeyEventHandler, KeyEventResultCallback } from './views/keyEventSource.interface.js';
import GlobeHint from './globehint.interface.js';
import KeyboardView from './components/keyboardView.interface.js';

View file

@ -1,7 +1,8 @@
import { assert } from 'chai';
import { DOMKeyboardLoader } from '@keymanapp/keyboard-processor/dom-keyboard-loader';
import { extendString, KeyboardHarness, Keyboard, KeyboardInterface, MinimalKeymanGlobal, Mock, DeviceSpec, KeyboardKeymanGlobal } from '@keymanapp/keyboard-processor';
import { extendString, KeyboardHarness, Keyboard, MinimalKeymanGlobal, Mock, DeviceSpec, KeyboardKeymanGlobal } from '@keymanapp/keyboard-processor';
import { KeyboardInterface } from 'keyman/engine/js-processor';
declare let window: typeof globalThis;
// KeymanEngine from the web/ folder... when available.

View file

@ -4,11 +4,11 @@ import {
import {
Keyboard,
KeyboardInterface,
KeyboardProperties,
MinimalKeymanGlobal
} from '@keymanapp/keyboard-processor';
import { KeyboardInterface } from 'keyman/engine/js-processor';
import { KeyboardStub } from 'keyman/engine/package-cache';
const loader = new DOMKeyboardLoader(new KeyboardInterface(window, MinimalKeymanGlobal));

View file

@ -4,7 +4,8 @@ import sinon from 'sinon';
import { LanguageProcessor, TranscriptionCache } from 'keyman/engine/main';
import { PredictionContext } from 'keyman/engine/interfaces';
import { Worker as LMWorker } from "@keymanapp/lexical-model-layer/node";
import { DeviceSpec, KeyboardProcessor, Mock } from '@keymanapp/keyboard-processor';
import { DeviceSpec, Mock } from '@keymanapp/keyboard-processor';
import { KeyboardProcessor } from 'keyman/engine/js-processor';
function compileDummyModel(suggestionSets) {
return `

View file

@ -8,8 +8,9 @@ import {
RecordedSyntheticKeystroke
} from "./index.js";
import { KeyboardInterface, KeyEvent, KeyEventSpec, KeyboardProcessor, Mock, type OutputTarget, KeyboardHarness } from "@keymanapp/keyboard-processor";
import { KeyEvent, KeyEventSpec, Mock, type OutputTarget, KeyboardHarness } from "@keymanapp/keyboard-processor";
import { DeviceSpec } from "@keymanapp/web-utils";
import { KeyboardInterface, KeyboardProcessor } from 'keyman/engine/js-processor';
export default class NodeProctor extends Proctor {
private keyboardWithHarness: KeyboardHarness;

View file

@ -17,6 +17,7 @@
{ "path": "../../../../../common/web/keyman-version" },
{ "path": "../../../../../common/web/utils/" },
{ "path": "../../../../../common/web/keyboard-processor/" },
{ "path": "../../../../../common/web/lm-message-types" }
{ "path": "../../../../../common/web/lm-message-types" },
{ "path": "../../../engine/js-processor" }
],
}