diff --git a/web/src/engine/src/core-processor/coreKeyboardProcessor.ts b/web/src/engine/src/core-processor/coreKeyboardProcessor.ts index ca11aecd96..1b810ac715 100644 --- a/web/src/engine/src/core-processor/coreKeyboardProcessor.ts +++ b/web/src/engine/src/core-processor/coreKeyboardProcessor.ts @@ -14,23 +14,37 @@ import { export class CoreKeyboardInterface implements KeyboardMinimalInterface { public activeKeyboard: Keyboard; - - constructor() { - } } +/** + * Implements the core keyboard processing engine that interacts with the + * shared Keyman Core component which handles .kmx keyboards. + */ export class CoreKeyboardProcessor extends EventEmitter implements KeyboardProcessor { private _newLayerStore: MutableSystemStore = new MutableSystemStore(0, 'default'); private _oldLayerStore: MutableSystemStore = new MutableSystemStore(0, 'default'); private _layerStore: MutableSystemStore = new MutableSystemStore(0, 'default'); private _keyboardInterface: CoreKeyboardInterface = new CoreKeyboardInterface(); + /** + * Initialize the core processor with the provided base path. + * Sets up the necessary environment for processing keyboard events. + * + * @param {string} basePath The path for the core processor resources, i.e. where the + * km-core.js file is located. + * @returns {Promise} A promise that resolves when initialization is complete. + */ public async init(basePath: string): Promise { await KM_Core.createCoreProcessor(basePath); } - // Tracks the simulated value for supported state keys, allowing the OSK to mirror a physical keyboard for them. - // Using the exact keyCode name from the Codes definitions will allow for certain optimizations elsewhere in the code. + /** + * Tracks the simulated value for supported state keys, allowing the OSK to + * mirror a physical keyboard for them. Uses the exact keyCode name from the + * Codes definitions to enable certain optimizations elsewhere in the code. + * + * @type {StateKeyMap} + */ public stateKeys: StateKeyMap = { "K_CAPS": false, "K_NUMLOCK": false, @@ -38,47 +52,96 @@ export class CoreKeyboardProcessor extends EventEmitter implements Key } /** - * Indicates the device (platform) to be used for non-keystroke events, - * such as those sent to `begin postkeystroke` and `begin newcontext` - * entry points. - */ + * Indicates the device (platform) to be used for non-keystroke events. + * Used for events such as those sent to `begin postkeystroke` and + * `begin newcontext` entry points. + * + * @type {DeviceSpec} + */ public contextDevice: DeviceSpec; + /** + * Optional handler for beep events triggered by the processor. + * Allows custom handling of beep feedback, such as for alerts or errors. + * + * @type {BeepHandler} + */ public beepHandler?: BeepHandler; - // Tracks the most recent modifier state information in order to quickly detect changes - // in keyboard state not otherwise captured by the hosting page in the browser. - // Needed for AltGr simulation. + /** + * Bitfield representing the most recent modifier state (Alt, Ctrl, Shift, etc.) + * as observed or simulated by the processor. Used to quickly detect changes in + * modifier state not otherwise captured by the hosting page (important for AltGr). + * + * @type {number} + */ public modStateFlags: number = 0; + + /** + * Stores the identifier for the base keyboard layout in use. + * Used to determine the default layout for key mapping and processing. + * + * @type {string} + */ public baseLayout: string; + /** + * The currently active keyboard. + * + * @type {Keyboard} + */ public get activeKeyboard(): Keyboard { return this.keyboardInterface.activeKeyboard; } - public set activeKeyboard(keyboard: Keyboard) { this.keyboardInterface.activeKeyboard = keyboard; } + /** + * The keyboard interface used by the processor. + * Provides access to the keyboard interface implementation. + * + * @type {KeyboardMinimalInterface} + */ get keyboardInterface(): KeyboardMinimalInterface { return this._keyboardInterface; } + /** + * The store representing the currently active keyboard layer. + * + * @type {MutableSystemStore} + */ public get layerStore(): MutableSystemStore { // TODO-web-core: link to .kmx layer store return this._layerStore; } + /** + * A writable store used when transitioning to a new layer + * + * @type {MutableSystemStore} + */ public get newLayerStore(): MutableSystemStore { // TODO-web-core: link to .kmx new-layer store return this._newLayerStore; } + /** + * A store representing the previously active layer + * + * @type {MutableSystemStore} + */ public get oldLayerStore(): MutableSystemStore { // TODO-web-core: link to .kmx old-layer store return this._oldLayerStore; } + /** + * Identifier of the currently active layer. + * + * @type {string} + */ public get layerId(): string { return this._layerStore.value; } @@ -86,11 +149,6 @@ export class CoreKeyboardProcessor extends EventEmitter implements Key this._layerStore.set(value); } - public processPostKeystroke(device: DeviceSpec, textStore: TextStore): ProcessorAction { - // TODO-web-core: Implement this method - return null; - } - /** * Retrieve context including deadkeys from TextStore and apply to Core's context * @@ -169,6 +227,15 @@ export class CoreKeyboardProcessor extends EventEmitter implements Key } } + /** + * Processes a keystroke event and updates the text store accordingly. + * Handles the main logic for interpreting and applying keyboard input. + * + * @param {KeyEvent} keyEvent The key event to process. + * @param {TextStore} textStore The current text store context. + * + * @returns {ProcessorAction} The resulting processor action. + */ public processKeystroke(keyEvent: KeyEvent, textStore: TextStore): ProcessorAction { const preInput = SyntheticTextStore.from(textStore, true); @@ -204,29 +271,72 @@ export class CoreKeyboardProcessor extends EventEmitter implements Key } /** - * Select the OSK's next keyboard layer based upon layer switching keys as a default - * The next layer will be determined from the key name unless otherwise specifed + * Processes post-keystroke actions for the given device and text store. + * Handles any actions that should occur after a keystroke is processed. * - * @param {string} keyName key identifier - * @return {boolean} return true if keyboard layer changed + * @param {DeviceSpec} device The device specification. + * @param {TextStore} textStore The current text store context. + * + * @returns {ProcessorAction} The resulting processor action, or null if not applicable. + */ + public processPostKeystroke(device: DeviceSpec, textStore: TextStore): ProcessorAction { + // TODO-web-core: Implement this method + return null; + } + + /** + * Determines if the given key event is a modifier key press. + * Returns true if the event corresponds to a modifier key, otherwise false. + * + * @param {KeyEvent} keyEvent The key event to evaluate. + * @param {TextStore} textStore The current text store context. + * @param {boolean} isKeyDown Indicates if the key event is a key down event. + * + * @returns {boolean} True if the event is a modifier key press, false otherwise. + */ + public doModifierPress(keyEvent: KeyEvent, textStore: TextStore, isKeyDown: boolean): boolean { + // TODO-web-core: Implement this method + return false; + } + + /** + * Resets the keyboard context, optionally using the provided text store. + * Clears or reinitializes the context for subsequent keyboard processing. + * + * @param {TextStore} [textStore] - The optional text store to use for resetting context. + */ + public resetContext(textStore?: TextStore): void {} + + /** + * Finalizes the processor action and applies any final changes to the text store. + * Ensures that all necessary updates are completed after processing a key event. + * + * @param {ProcessorAction} data The processor action to finalize. + * @param {TextStore} textStore The text store to update. + */ + public finalizeProcessorAction(data: ProcessorAction, textStore: TextStore): void { } + + /** + * Selects the next keyboard layer based on the provided key event. + * Determines and applies the appropriate layer switch for the OSK. + * + * @param {KeyEvent} keyEvent The key event used to determine the next layer. + * + * @returns {boolean} True if the keyboard layer changed, false otherwise. */ public selectLayer(keyEvent: KeyEvent): boolean { // TODO-web-core: Implement this method return false; } - // Returns true if the key event is a modifier press, allowing keyPress to return selectively - // in those cases. - public doModifierPress(Levent: KeyEvent, textStore: TextStore, isKeyDown: boolean): boolean { - // TODO-web-core: Implement this method - return false; - } - - public resetContext(textStore?: TextStore): void {} - + /** + * Sets the numeric layer for the given device. + * Switches the keyboard to a numeric input layer if supported. + * + * @param {DeviceSpec} device - The device for which to set the numeric layer. + */ public setNumericLayer(device: DeviceSpec): void {} - public finalizeProcessorAction(data: ProcessorAction, textStore: TextStore): void {} /** @internal */ public unitTestEndPoints = { diff --git a/web/src/engine/src/keyboard/keyboards/keyboardProcessor.ts b/web/src/engine/src/keyboard/keyboards/keyboardProcessor.ts index 334e585705..331ea0a1bb 100644 --- a/web/src/engine/src/keyboard/keyboards/keyboardProcessor.ts +++ b/web/src/engine/src/keyboard/keyboards/keyboardProcessor.ts @@ -17,67 +17,196 @@ export interface EventMap { export type BeepHandler = (textStore: TextStore) => void; +/** + * Interface for the keyboard processing engine used by the web runtime to + * translate low-level key events and device/context signals into high-level + * keyboard actions and text-store updates. + * + * This interface extends an EventEmitter of EventMap and centralizes responsibilities + * such as: + * - tracking simulated state keys so the on‑screen keyboard (OSK) can mirror hardware state, + * - selecting and switching keyboard layers for the OSK, + * - handling modifier simulation (including AltGr), + * - routing non-keystroke device/context events, + * - coordinating mutable layer state stores, + * - performing keystroke and post‑keystroke processing and finalizing ProcessorAction + * results against a TextStore. + */ export interface KeyboardProcessor extends EventEmitter { // public static readonly DEFAULT_OPTIONS: ProcessorInitOptions = { // baseLayout: 'us', // defaultOutputRules: new DefaultRules() // }; - // Tracks the simulated value for supported state keys, allowing the OSK to mirror a physical keyboard for them. - // Using the exact keyCode name from the Codes definitions will allow for certain optimizations elsewhere in the code. + /** + * Tracks the simulated values for supported state keys (e.g. CapsLock, NumLock) + * so that the OSK can reflect a physical keyboard's state. Keys are referenced + * using the exact keyCode names from the Codes definitions to allow for optimized + * handling elsewhere. + * + * @type {StateKeyMap} + */ stateKeys: StateKeyMap; /** - * Indicates the device (platform) to be used for non-keystroke events, - * such as those sent to `begin postkeystroke` and `begin newcontext` - * entry points. - */ + * Indicates the device/platform to be used for non-keystroke events (for example, + * events dispatched to "begin postkeystroke" and "begin newcontext" entry points). + * This lets the processor adapt behavior or rule evaluation to the active device. + * + * @type {DeviceSpec} + */ contextDevice: DeviceSpec; + /** + * Optional handler used to produce an audible beep or other feedback when a rule + * or keyboard action requests it. + * + * @type {BeepHandler | undefined} + */ beepHandler?: BeepHandler; + + /** + * Stores the identifier for the base physical layout in use (for + * example "us"). Used to determine the default layout for key mapping + * and processing. + * + * @type {string} + */ baseLayout: string; - // Tracks the most recent modifier state information in order to quickly detect changes - // in keyboard state not otherwise captured by the hosting page in the browser. - // Needed for AltGr simulation. + /** + * Bitfield representing the most recent modifier state (Alt, Ctrl, Shift, etc.) + * as observed or simulated by the processor. Used to quickly detect changes in + * modifier state not otherwise captured by the hosting page (important for AltGr). + * + * @type {number} + */ modStateFlags: number; + /** + * The currently active Keyboard instance. Implementations provide a getter and + * setter to change the active keyboard at runtime. Setting a new keyboard should + * update any associated stores and clear or reinitialize processor state as needed. + * + * @type {Keyboard} + */ get activeKeyboard(): Keyboard; set activeKeyboard(keyboard: Keyboard); + /** + * Read-only minimal interface for interacting with the current keyboard. + * + * @type {KeyboardMinimalInterface} + */ get keyboardInterface(): KeyboardMinimalInterface + /** + * The store representing the currently active keyboard layer. + * + * @type {MutableSystemStore} + */ get layerStore(): MutableSystemStore; + /** + * A writable store used when transitioning to a new layer; allows + * accumulation of changes before committing them to layerStore. + * + * @type {MutableSystemStore} + */ get newLayerStore(): MutableSystemStore; + /** + * A store representing the previously active layer; useful for reverting or + * comparing layer states when switching layers. + * + * @type {MutableSystemStore} + */ get oldLayerStore(): MutableSystemStore; + /** + * Identifier of the currently active layer. Implementations provide getter and + * setter access. Setting this value should trigger the appropriate layer store + * updates and notify any listeners of the change. + * + * @type {string} + */ get layerId(): string; set layerId(value: string); - processPostKeystroke(device: DeviceSpec, textStore: TextStore): ProcessorAction; - + /** + * Process a keystroke, i.e. the `begin Unicode` group. + * Evaluates keyboard rules, updates internal state, and returns a ProcessorAction + * describing the changes to apply to the TextStore. + * + * @param {KeyEvent} keyEvent The key event to process. + * @param {TextStore} textStore The text store representing current context. + * + * @returns {ProcessorAction} The resulting processor action. + */ processKeystroke(keyEvent: KeyEvent, textStore: TextStore): ProcessorAction; /** - * Select the OSK's next keyboard layer based upon layer switching keys as a default - * The next layer will be determined from the key name unless otherwise specifed + * Processes the `begin PostKeystroke` group. + * This is used to evaluate rules that run after a keystroke has been + * processed and can produce actions that affect the TextStore, layer state, + * or other side effects. * - * @param {string} keyName key identifier - * @return {boolean} return true if keyboard layer changed + * @param {DeviceSpec} device The device context. + * @param {TextStore} textStore The text store representing current context. + * + * @returns {ProcessorAction} The resulting processor action. + */ + processPostKeystroke(device: DeviceSpec, textStore: TextStore): ProcessorAction; + + /** + * Determines if the given key event is a modifier key press. + * Returns true if the event corresponds to a modifier key, otherwise false. + * + * @param {KeyEvent} keyEvent The key event to evaluate. + * @param {TextStore} textStore The current text store context. + * @param {boolean} isKeyDown Indicates if the key event is a key down event. + * + * @returns {boolean} True if the event is a modifier key press, false otherwise. + */ + doModifierPress(keyEvent: KeyEvent, textStore: TextStore, isKeyDown: boolean): boolean; + + /** + * Resets the processor's context to a clean state. + * May clear stores, simulated state keys, and modifier flags. If a TextStore is provided, + * it will also be reset. + * + * @param {TextStore} [textStore] - The optional text store to use for resetting context. + */ + resetContext(textStore?: TextStore): void; + + /** + * Finalizes the processor action and applies any final changes to the text store. + * Ensures that all necessary updates are completed after processing a key event. + * + * @param {ProcessorAction} data The processor action to finalize. + * @param {TextStore} textStore The text store to update. + */ + finalizeProcessorAction(data: ProcessorAction, textStore: TextStore): void; + + /** + * Selects the OSK's next keyboard layer based upon layer switching keys. + * By default, the next layer is determined from the key name unless otherwise + * specified. Returns true if the active keyboard layer changed. + * + * @param {KeyEvent} keyEvent - Key identifier or event used to determine layer change. + * + * @returns {boolean} True if the keyboard layer changed. */ selectLayer(keyEvent: KeyEvent): boolean; - // Returns true if the key event is a modifier press, allowing keyPress to return selectively - // in those cases. - doModifierPress(Levent: KeyEvent, textStore: TextStore, isKeyDown: boolean): boolean; - - resetContext(textStore?: TextStore): void; - + /** + * + * Select the numeric layer if the provided device contains one. + * + * @param {DeviceSpec} device - The device for which the numeric layer should be set. + */ setNumericLayer(device: DeviceSpec): void; - finalizeProcessorAction(data: ProcessorAction, textStore: TextStore): void; } diff --git a/web/src/test/manual/build.sh b/web/src/test/manual/build.sh index 6e524895f9..eef52ac19a 100755 --- a/web/src/test/manual/build.sh +++ b/web/src/test/manual/build.sh @@ -19,7 +19,7 @@ builder_parse "$@" DEST="web/build/test-resources" builder_describe_outputs \ - build "${KEYMAN_ROOT}/${DEST}/sentry-manager.js" + build "/${DEST}/sentry-manager.js" #### Build action definitions #### @@ -30,7 +30,7 @@ function do_copy() { cp "${KEYMAN_ROOT}/common/web/sentry-manager/build/lib/index.js" "${KEYMAN_ROOT}/${DEST}/sentry-manager.js" cp "${KEYMAN_ROOT}/common/web/sentry-manager/build/lib/index.js.map" "${KEYMAN_ROOT}/${DEST}/sentry-manager.js.map" - cp "${KEYMAN_ROOT}/common/web/types/tests/fixtures/kmx/khmer_angkor.kmx" "${KEYMAN_ROOT}/${DEST}/" + cp "${KEYMAN_ROOT}/common/web/types/tests/fixtures/kmx/khmer_angkor.kmx" "${KEYMAN_ROOT}/${DEST}/keyboards/" # copy common test (resources) keyboards cp -f "${KEYMAN_ROOT}/common/test/keyboards/platform-rules/platformtest.js" "${KEYMAN_ROOT}/${DEST}/keyboards/" diff --git a/web/src/test/manual/web/kmxkeyboard.html b/web/src/test/manual/web/kmxkeyboard.html index f0d23eaea6..8510324f9f 100644 --- a/web/src/test/manual/web/kmxkeyboard.html +++ b/web/src/test/manual/web/kmxkeyboard.html @@ -45,7 +45,7 @@ id: 'km', name: 'Khmer' }, - filename: '/build/test-resources/khmer_angkor.kmx' + filename: '/build/test-resources/keyboards/khmer_angkor.kmx' }); }); });