Merge pull request #12321 from keymanapp/chore/merge-master-into-user-dict

chore: merge master into user-dict
This commit is contained in:
Marc Durdin 2024-08-30 04:33:49 +02:00 • committed by GitHub
commit 171b925281
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
216 changed files with 4648 additions and 3443 deletions

View file

@ -1,5 +1,47 @@
# Keyman Version History
## 18.0.100 alpha 2024-08-28
* fix(windows): check IM window will be in a visible location (#11967)
## 18.0.99 alpha 2024-08-27
* feat(web): import the generator for the pred-text wordbreaker's Unicode-property data-table (#10690)
* feat(web): optimize the wordbreaker data table for filesize and ease of first-load parsing (#10692)
* (#12297)
* (#12115)
## 18.0.98 alpha 2024-08-26
* change(ios): defer registration of fonts past initialization (#12190)
* refactor(ios): optimize font registration (#12210)
* test(mac): add unit tests to validate first calls to compliance check (#11724)
* fix(mac): limit short bundle version string to x.y.z format in info.plist (#12233)
* chore(developer): extend timeouts for lm compiler tests to 5 secs (#12273)
* fix(developer): find last matching key in LDML key bag when building KVK (#12278)
* chore(android): Cleanup stray debug statements in console (#12287)
* fix(developer): ensure call() detects invalid store in kmcmplib compiler (#12263)
## 18.0.97 alpha 2024-08-24
* refactor(linux): cleanup API of kvk2ldml.py (#12276)
* chore(common): adjust build settings for windows clean builds (#12264)
* chore(windows): remove remaining unused Makefiles (#12274)
* docs(developer): update build documentation to refer to build.sh (#12272)
## 18.0.96 alpha 2024-08-23
* fix(android): Fix navigation arrows in Info Activity for RTL (#12244)
* fix(web): fix documentation-keyboard spacebar-text scaling (#12232)
* fix(android): Use increment and decrement arrows on longpress delay menu (#12242)
* fix(android): Add RTL assets for adjusting keyboard height menu (#12261)
* chore(common): use `npm install` in emsdk update (#12269)
* docs: refresh windows.md (#12248)
## 18.0.95 alpha 2024-08-22
* chore(common): allow build agents to automatically select emsdk version, and enable support for 3.1.60+ (#12243)
## 18.0.94 alpha 2024-08-21
* fix(core): look for `emcc` instead of `emcc.py` (#12235)

View file

@ -1 +1 @@
18.0.95
18.0.101

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

View file

@ -23,7 +23,7 @@
android:layout_marginStart="5dp"
android:layout_marginEnd="5dp"
android:contentDescription="@string/ic_delay_time_down"
android:src="@drawable/ic_light_dialog_textsize_down" />
android:src="@drawable/ic_action_decrement" />
<!-- Seekbar range 0 to 6 corresponds to longpress duration 300 ms to 1500 ms -->
<SeekBar
@ -43,7 +43,7 @@
android:layout_marginStart="5dp"
android:layout_marginEnd="5dp"
android:contentDescription="@string/ic_delay_time_up"
android:src="@drawable/ic_light_dialog_textsize_up" />
android:src="@drawable/ic_action_increment" />
</LinearLayout>

View file

@ -1,12 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="@color/ms_white"
tools:context=".InfoActivity" >
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="@color/ms_white"
tools:context=".InfoActivity" >
<androidx.appcompat.widget.Toolbar
android:id="@+id/titlebar"
@ -15,25 +15,31 @@
android:layout_alignParentTop="true"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light">
<!-- Navigation buttons -->
<ImageButton
android:id="@+id/back_button"
style="?android:attr/actionButtonStyle"
app:tint="@color/image_button_color"
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="start"
android:contentDescription="@string/label_back"
android:src="@drawable/ic_action_back" />
android:orientation="horizontal">
<ImageButton
android:id="@+id/forward_button"
style="?android:attr/actionButtonStyle"
app:tint="@color/image_button_color"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/label_forward"
android:src="@drawable/ic_action_forward" />
<!-- Navigation buttons in a LinearLayout to mirror on RTL -->
<ImageButton
android:id="@+id/back_button"
style="?android:attr/actionButtonStyle"
app:tint="@color/image_button_color"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="start"
android:contentDescription="@string/label_back"
android:src="@drawable/ic_action_back" />
<ImageButton
android:id="@+id/forward_button"
style="?android:attr/actionButtonStyle"
app:tint="@color/image_button_color"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/label_forward"
android:src="@drawable/ic_action_forward" />
</LinearLayout>
<ImageButton
android:id="@+id/close_button"

View file

@ -117,7 +117,7 @@ function notifyHost(event, params) {
function setLongpressDelay(delay) {
if (keyman.osk) {
keyman.osk.gestureParams.longpress.waitLength = delay;
console.debug('setLongpressDelay('+delay+')');
console_debug('setLongpressDelay('+delay+')');
} else {
window.console.log('setLongpressDelay error: keyman.osk undefined');
}
@ -304,7 +304,7 @@ function updateKMSelectionRange(start, end) {
console_debug('result:\n' + build_context_string(context));
} else {
console.debug('range unchanged');
console_debug('range unchanged');
}
}

View file

@ -1 +1,2 @@
build/
build/
src/main/default/data.inc.ts

View file

@ -12,6 +12,8 @@ THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
################################ Main script ################################
# Note: the raw text files used for data.inc.ts are found within
# /resources/standards-data/unicode-character-database.
builder_describe "Builds the predictive-text wordbreaker implementation module" \
"clean" \
"configure" \
@ -20,16 +22,26 @@ builder_describe "Builds the predictive-text wordbreaker implementation module"
"--ci"
builder_describe_outputs \
configure /node_modules \
build build/obj/index.js
configure src/main/default/data.inc.ts \
build build/main/obj/index.js
builder_parse "$@"
function do_configure() {
verify_npm_setup
# This is a script used to build the data.inc.ts file needed by the
# default wordbreaker. We rarely update the backing data, but it
# is needed _before_ the `build` action's compilation step.
tsc -b tools/data-compiler/tsconfig.json
node ./build/tools/data-compiler/obj/index.js
}
function do_build() {
tsc -b
tsc -b ./tsconfig.json
# Declaration bundling.
tsc --emitDeclarationOnly --outFile ./build/lib/index.d.ts
tsc -p ./tsconfig.json --emitDeclarationOnly --outFile ./build/main/lib/index.d.ts
}
function do_test() {
@ -40,7 +52,7 @@ function do_test() {
fi
}
builder_run_action configure verify_npm_setup
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

View file

@ -14,17 +14,23 @@
],
"homepage": "https://github.com/keymanapp/keyman",
"license": "MIT",
"main": "build/obj/index.js",
"types": "build/obj/index.d.ts",
"main": "build/main/obj/index.js",
"types": "build/main/obj/index.d.ts",
"exports": {
".": {
"es6-bundling": "./src/index.ts",
"default": "./build/obj/index.js"
"es6-bundling": "./src/main/index.ts",
"default": "./build/main/obj/index.js"
},
"./lib": {
"types": "./build/lib/index.d.ts"
"types": "./build/main/lib/index.d.ts"
},
"./obj/*.js": "./build/obj/*.js"
"./obj/*.js": "./build/main/obj/*.js",
"./test-index": {
"default": "./build/main/obj/test-index.js"
},
"./README.md": {
"default": "./README.md"
}
},
"directories": {
"lib": "lib",

File diff suppressed because it is too large Load diff

View file

@ -1,4 +1,6 @@
import { WordBreakProperty, WORD_BREAK_PROPERTY, I, propertyMap } from "./data.js";
import { WordBreakProperty, propertyMap } from "./data.inc.js";
import { searchForProperty } from "./searchForProperty.js";
/**
* A set of options used to customize and extend the behavior of the default
@ -566,7 +568,7 @@ function property(character: string, options?: DefaultWordBreakerOptions): WordB
// TODO: remove dependence on character.codepointAt()?
let codepoint = character.codePointAt(0) as number;
return searchForProperty(codepoint, 0, WORD_BREAK_PROPERTY.length - 1);
return searchForProperty(codepoint);
}
function propertyVal(propName: string, options?: DefaultWordBreakerOptions) {
@ -574,35 +576,4 @@ function propertyVal(propName: string, options?: DefaultWordBreakerOptions) {
const customIndex = options?.customProperties?.findIndex(matcher) ?? -1;
return customIndex != -1 ? -customIndex - 1 : propertyMap.findIndex(matcher);
}
/**
* Binary search for the word break property of a given CODE POINT.
*
* The auto-generated data.ts master array defines a **character range**
* lookup table. If a character's codepoint is equal to or greater than
* the I.Start value for an entry and exclusively less than the next entry,
* it falls in the first entry's range bucket and is classified accordingly
* by this method.
*/
function searchForProperty(codePoint: number, left: number, right: number): WordBreakProperty {
// All items that are not found in the array are assigned the 'Other' property.
if (right < left) {
return WordBreakProperty.Other;
}
let midpoint = left + ~~((right - left) / 2);
let candidate = WORD_BREAK_PROPERTY[midpoint];
let nextRange = WORD_BREAK_PROPERTY[midpoint + 1];
let startOfNextRange = nextRange ? nextRange[I.Start] : Infinity;
if (codePoint < candidate[I.Start]) {
return searchForProperty(codePoint, left, midpoint - 1);
} else if (codePoint >= startOfNextRange) {
return searchForProperty(codePoint, midpoint + 1, right);
}
// We found it!
return candidate[I.Value];
}
}

View file

@ -0,0 +1,43 @@
import { WordBreakProperty, WORD_BREAK_PROPERTY_BMP, WORD_BREAK_PROPERTY_NON_BMP } from "./data.inc.js";
export function searchForProperty(codePoint: number): WordBreakProperty {
const bucketSize = codePoint <= 0xFFFF ? 2 : 3;
// SMP chars take a bit more space to encode.
const encodedArray = bucketSize == 2 ? WORD_BREAK_PROPERTY_BMP : WORD_BREAK_PROPERTY_NON_BMP;
return _searchForProperty(encodedArray, codePoint, bucketSize, 0, encodedArray.length / bucketSize - 1) - 0x20;
}
/**
* Binary search for the word break property of a given CODE POINT.
*
* The auto-generated data.ts master strings encode **character range**
* lookup tables. If a character's codepoint is equal to or greater than
* the start-of-range value for an entry and exclusively less than the next
* entry's start-of-range, it falls within the first entry's range bucket
* and is classified accordingly by this method.
*/
function _searchForProperty(encodedArray: string, codePoint: number, bucketSize: number, left: number, right: number): WordBreakProperty {
// All items that are not found in the array are assigned the 'Other' property.
if (right < left) { // May need special handling at end of BMP / start of non-BMP.
return WordBreakProperty.Other;
}
let midpoint = left + ~~((right - left) / 2);
let candidate = encodedArray.codePointAt(bucketSize * midpoint);
// If out-of-bounds, gives NaN.
let nextRange = encodedArray.codePointAt(bucketSize * (midpoint + 1));
let startOfNextRange = isNaN(nextRange) ? Infinity : nextRange;
if (codePoint < candidate) {
return _searchForProperty(encodedArray, codePoint, bucketSize, left, midpoint - 1);
} else if (codePoint >= startOfNextRange) {
return _searchForProperty(encodedArray, codePoint, bucketSize, midpoint + 1, right);
}
// We found it!
const propertyCode = encodedArray.charCodeAt(bucketSize * (midpoint + 1) - 1);
return propertyCode as WordBreakProperty;
}

View file

@ -0,0 +1,5 @@
// Include all standard exports.
export * from './index.js';
// Exposes some internal properties for unit-test accessibility
export { WordBreakProperty } from './default/data.inc.js';

View file

@ -0,0 +1,33 @@
/**
* Smoke-test the default
*/
import { assert } from 'chai';
import { searchForProperty } from '../build/main/obj/default/searchForProperty.js';
import { propertyMap } from '../build/main/obj/default/data.inc.js';
describe('searchForProperty', () => {
it('correctly finds character classes for standard ASCII characters', () => {
assert.equal(searchForProperty('a'.codePointAt(0)), propertyMap.indexOf('ALetter'));
assert.equal(searchForProperty('Z'.codePointAt(0)), propertyMap.indexOf('ALetter'));
assert.equal(searchForProperty("'".codePointAt(0)), propertyMap.indexOf('Single_Quote'));
assert.equal(searchForProperty('"'.codePointAt(0)), propertyMap.indexOf('Double_Quote'));
assert.equal(searchForProperty(','.codePointAt(0)), propertyMap.indexOf('MidNum'));
assert.equal(searchForProperty('.'.codePointAt(0)), propertyMap.indexOf('MidNumLet'));
assert.equal(searchForProperty('-'.codePointAt(0)), propertyMap.indexOf('Other'));
});
it('correctly finds character classes for specialized BMP characters', () => {
assert.equal(searchForProperty(0x05D0), propertyMap.indexOf('Hebrew_Letter'));
assert.equal(searchForProperty(0x3031), propertyMap.indexOf('Katakana'));
assert.equal(searchForProperty(0xFFFE), propertyMap.indexOf('Other'));
assert.equal(searchForProperty(0xFFFF), propertyMap.indexOf('Other'));
});
it('correctly finds character classes for non-BMP characters', () => {
assert.equal(searchForProperty(0x0001F1E6), propertyMap.indexOf('Regional_Indicator'));
assert.equal(searchForProperty(0x00013430), propertyMap.indexOf('Format'));
assert.equal(searchForProperty(0x00010000), propertyMap.indexOf('ALetter'));
});
});

View file

@ -0,0 +1,22 @@
MIT License
Copyright (c) 2019 National Research Council Canada
Copyright (c) 2024 Eddie Antonio Santos
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -0,0 +1,388 @@
#!/usr/bin/env node
// Original version found at: https://github.com/eddieantonio/unicode-default-word-boundary/blob/master/libexec/compile-word-break.js
import fs from 'fs';
import path from 'path';
import { createRequire } from 'module';
const require = createRequire(import.meta.url);
/*
* Generates the TypeScript file for data required for the word boundary
* function:
*
* - a sorted array to facilitate binary search of the Word_Break property.
* - a regular expression that matches characters that have Extended_Pictographic=Yes.
*
* For internal use only. Please keep away from children.
*
* The generated file is saved to ../../src/main/default/data.inc.ts
*/
const MAX_CODE_POINT = 0x10FFFF;
// Where to get the data:
// - https://www.unicode.org/reports/tr51/#emoji_data
// - https://www.unicode.org/reports/tr41/#Props0
//////////////////////////////////// Main ////////////////////////////////////
const projectDir = path.dirname(require.resolve("@keymanapp/models-wordbreakers/README.md"));
const generatedFilename = path.join(projectDir, 'src', 'main', 'default', 'data.inc.ts');
// The data files should be in this repository, with names matching the
// Unicode version.
const wordBoundaryFilename = path.join(projectDir, `../../../resources/standards-data/unicode-character-database/WordBreakProperty.txt`);
const emojiDataFilename = path.join(projectDir, `../../../resources/standards-data/unicode-character-database/emoji-data.txt`);
///////////////////////////// Word_Boundary file /////////////////////////////
interface DataRange {
start: number;
end: number;
property: string;
}
// Extract the ranges IN ASCENDING ORDER from the file.
// This will be the big binary search table.
let ranges = readCharacterPropertyFile(wordBoundaryFilename)
.sort((a, b) => {
return a.start - b.start;
});
// The list of ranges are initially sparse — having gaps between assigned
// ranges. Fill in those gaps:
ranges = makeDense(ranges);
ensureDense(ranges);
// The possible Word_Break property assignments.
let categories = new Set<string>();
for (let {property} of ranges) {
categories.add(property);
}
// Also add pseudo-categories of start-of-text and end-of-text:
categories.add('sot');
categories.add('eot');
///////////////////////// Extended_Pictographic=Yes //////////////////////////
let extendedPictographicCodePoints = readCharacterPropertyFile(emojiDataFilename)
.filter(({property}) => property === 'Extended_Pictographic');
// Try generating the regular expression both in a way that is
// backwards-compatbile and one that only works in ES6+.
// let extendedPictographicRegExp;
let compatibleRegexp = utf16AlternativesStrategy();
let es6Regexp = unicodeRangeStrategy();
// Choose the shortest regular expression.
// In my experience, the ES6 regexp is an order of magnitude smaller!
if (es6Regexp.length < compatibleRegexp.length) {
// extendedPictographicRegExp = es6Regexp;
console.warn(`Using ES6 regexp [${es6Regexp.length} chars]`);
} else {
// extendedPictographicRegExp = compatibleRegexp;
console.warn(`Using compatibility regexp [${compatibleRegexp.length} chars]`);
}
let catIndexSeed = 0;
const categoryMap = new Map<string, number>();
for(let cat of categories) {
categoryMap.set(cat, catIndexSeed++);
if(catIndexSeed == '`'.charCodeAt(0)) {
catIndexSeed++; // Skip the back-tick as an encoding symbol.
// Reduces complications, as it's the encoding string start/end char.
}
}
const bmpRanges: typeof ranges = [];
const nonBmpRanges: typeof ranges = [];
// { start: number, property: number}[]
for(let range of ranges) { // already sorted
if(range.start <= 0xFFFF) {
bmpRanges.push(range);
} else {
if(nonBmpRanges.length == 0) {
const finalBmpRange = bmpRanges[bmpRanges.length - 1];
bmpRanges.push({
start: 0xFFFF,
property: finalBmpRange.property,
end: undefined
});
if(range.start != 0x10000) {
nonBmpRanges.push({
start: 0x10000,
property: finalBmpRange.property,
end: undefined
});
}
}
nonBmpRanges.push(range);
}
}
//////////////////////// Creating the generated file /////////////////////////
// Save the output in the gen/ directory.
let stream = fs.createWriteStream(generatedFilename);
function escape(codedChar: string) {
if(codedChar == '`' || codedChar == '\\') {
return '\\' + codedChar;
} else {
return codedChar;
}
}
// // Former entry in the original version by Eddie that was never included in our repo:
// export const extendedPictographic = ${extendedPictographicRegExp};
// Generate the file!
stream.write(`// Automatically generated file. DO NOT MODIFY.
// The generator script is defined at /common/models/wordbreakers/src/data-compiler/index.ts.
/**
* Valid values for a word break property.
*
* Is optimized away at compile-time; use \`propertyMap\` to find the mapped
* value at runtime for a property name if needed.
*/
export const enum WordBreakProperty {
${ /* Create enum values for each word break property */
Array.from(categories)
.map(x => ` ${x} = ${categoryMap.get(x)}`)
.join(',\n')
}
};
/**
* Contains property names per associated index, as this is compiled away
* by TypeScript for \`const enum\` cases like \`WordBreakProperty\`.
*/
export const propertyMap = [
${ /* Enumerate the plain-text names for ease of lookup at runtime */
Array.from(categories)
.map(x => ` "${x}"`)
.join(',\n')
}
];
export const WORD_BREAK_PROPERTY_BMP: string = \`${
// To consider: emit `\uxxxx` codes instead of the raw char?
bmpRanges.map(({start, property}) => {
let codedStart = escape(String.fromCodePoint(start));
// Offset the encoded property value to lie within a friendlier range,
// with characters that render naturally within code editors.
const codedProp = escape(String.fromCharCode(categoryMap.get(property) + 0x20));
return `${codedStart}${codedProp}`;
}).join('')
}\`;
export const WORD_BREAK_PROPERTY_NON_BMP: string = \`${
// To consider: emit `\uxxxx` codes instead of the raw char?
nonBmpRanges.map(({start, property}) => {
const codedStart = escape(String.fromCodePoint(start));
// Offset the encoded property value to lie within a friendlier range,
// with characters that render naturally within code editors.
const codedProp = escape(String.fromCharCode(categoryMap.get(property) + 0x20));
return `${codedStart}${codedProp}`;
}).join('')
}\`;
`);
/**
* Reads a Unicode character property file.
*
* Character property files are composed of comment lines, empty lines, and
* property lines. Comments lines begin with '#' and should be ignored, as
* well as empty lines.
*
* Property lines have a code point or a code point range, followed by a
* semi-colon, followed by the property text. e.g.,
*
* 1F600 ; Emoji # 6.1 [1] (😀) grinning face
* 26C4..26C5 ; Emoji_Presentation # 5.2 [2] (⛄..⛅) snowman without snow..sun behind butt
*
* This will read the file at the given filename, and return an ordered array
* or property lines, with attributes:
*
* {start: number, end: number, property: string}
*
* If the property specifies a single code point (i.e., not a range of code
* points), then end === start.
*/
function readCharacterPropertyFile(filename: string) {
let textContents = fs.readFileSync(filename, { encoding: 'utf8'});
return textContents.split('\n')
.filter(line => !line.startsWith('#') && line.trim())
.map(line => {
let [_, startText, endText, property] = line.match(
// Parse lines that look like this:
// 0000 .. 0000 ; CategoryName
/^([0-9A-F]{4,6})(?:..([0-9A-F]{4,6}))?\s+;\s+([A-Za-z_]+)/
);
let start = parseCodepoint(startText);
let end = endText !== undefined ? parseCodepoint(endText) : start;
return { start, end, property };
});
}
/**
* Parses a code point, expressed as a 4 or 6 digit hexadecimal string.
* Does some bounds checking in order to determine if the string is in fact a
* valid code point.
*/
function parseCodepoint(hexString: string) {
let number = parseInt(hexString, 16);
if (Number.isNaN(number)) {
throw new SyntaxError(`Cannot parse codepoint: ${hexString}`);
}
if (number < 0 || number > MAX_CODE_POINT) {
throw new RangeError(`Codepoint out of range: ${number}`);
}
return number;
}
function toUnicodeEscape(codePoint: number) {
let isBMP = codePoint <= 0xFFFF;
let simpleConversion = codePoint.toString(16).toUpperCase();
let padding = (isBMP ? 4 : 6) - simpleConversion.length;
let digits = '0'.repeat(padding) + simpleConversion;
if (isBMP) {
return '\\u' + digits;
} else {
return `\\u{${digits}}`;
}
}
function utf16AlternativesStrategy() {
let codePoints = [];
for (let {start, end} of extendedPictographicCodePoints) {
for (let current = start; current <= end; current ++) {
codePoints.push(current);
}
}
let alternatives = codePoints.map(codePointToUTF16Escape);
return `/^(?:${alternatives.join('|')})/`;
}
function codePointToUTF16Escape(codePoint: number): string {
// Scalar values remain the same
if (codePoint <= 0xFFFF) {
return toUnicodeEscape(codePoint);
}
const LOWEST_TEN_BITS_MASK = 0x03FF;
let astralBits = codePoint - 0x10000;
let highSurrogate = 0xD800 + (astralBits >>> 10);
let lowSurrogate = 0xDC00 + (astralBits & LOWEST_TEN_BITS_MASK);
console.assert(highSurrogate <= 0xDBFF);
console.assert(lowSurrogate <= 0xDFFF);
console.assert(String.fromCharCode(highSurrogate) + String.fromCharCode(lowSurrogate) ===
String.fromCodePoint(codePoint));
return codePointToUTF16Escape(highSurrogate) + codePointToUTF16Escape(lowSurrogate);
}
function unicodeRangeStrategy() {
let regexp = '';
for (let {start, end} of extendedPictographicCodePoints) {
if (start === end) {
regexp += toUnicodeEscape(start);
} else {
regexp += toUnicodeEscape(start) + '-' + toUnicodeEscape(end);
}
}
return `/^[${regexp}]/u`;
}
function makeDense(ranges: DataRange[]) {
return joinSameAdjacentProperties(fillInGaps(ranges));
}
function ensureDense(ranges: DataRange[]) {
let lastEnd = -1;
let lastProperty = 'sot';
for (let range of ranges) {
let {start, end, property} = range
if (lastEnd + 1 !== start) {
throw new Error(`Non-adjacent range: ${JSON.stringify(range)}`);
}
if (lastProperty === property) {
throw new Error(`adjacent ranges have same property: ${JSON.stringify(range)}`);
}
lastEnd = end;
lastProperty = property;
}
}
function joinSameAdjacentProperties(ranges: DataRange[]) {
console.assert(ranges.length > 1);
let conjoinedRanges = [];
conjoinedRanges.push(ranges.shift());
for (let range of ranges) {
let lastRange = conjoinedRanges[conjoinedRanges.length - 1];
if (range.property === lastRange.property) {
lastRange.end = range.end;
} else {
conjoinedRanges.push(range);
}
}
return conjoinedRanges;
}
function fillInGaps(ranges: DataRange[]) {
console.assert(ranges.length > 1);
let denseRanges = [];
let nextUnaccountedCodepoint = 0x0000;
for (let range of ranges) {
if (range.start > nextUnaccountedCodepoint) {
// Need to create a range BEFORE the next start of ranges
denseRanges.push({
start: nextUnaccountedCodepoint,
end: range.start - 1,
// If it's unassigned in the file, it should be 'Other'.
property: 'Other',
});
}
denseRanges.push(range);
nextUnaccountedCodepoint = range.end + 1;
}
// Create the last range (till the end)
if (nextUnaccountedCodepoint < MAX_CODE_POINT) {
denseRanges.push({
start: nextUnaccountedCodepoint,
end: MAX_CODE_POINT,
property: 'Other',
})
}
return denseRanges;
}

View file

@ -0,0 +1,21 @@
{
"extends": "../../../tsconfig.kmw-worker-base.json",
"compilerOptions": {
"baseUrl": "./",
"outDir": "../../build/tools/data-compiler/obj",
"tsBuildInfoFile": "../../build/tools/data-compiler/obj/tsconfig.tsbuildinfo",
"rootDir": "./",
"module": "node16",
"moduleResolution": "node16"
},
"references": [
{ "path": "../../../types" }
],
"include": [
"./**/*"
],
"exclude": [
"node_modules"
]
}

View file

@ -3,18 +3,17 @@
"compilerOptions": {
"baseUrl": "./",
"outDir": "build/obj",
"tsBuildInfoFile": "build/obj/tsconfig.tsbuildinfo",
"rootDir": "./src"
"outDir": "./build/main/obj",
"tsBuildInfoFile": "./build/main/obj/tsconfig.tsbuildinfo",
"rootDir": "./src/main"
},
"references": [
{ "path": "../types" }
],
"include": [
"src/**/*"
"./src/main/**/*"
],
"exclude": [
"node_modules",
"test/**/*.ts"
"node_modules"
]
}
}

View file

@ -3,6 +3,7 @@ import esbuild from 'esbuild';
import { esmConfiguration, forES6, iifeConfiguration } from './configuration.mjs';
import { prepareTslibTreeshaking } from './tslibTreeshaking.mjs';
let CHARSET = 'ascii';
let FORMAT = 'iife';
let MINIFY = false;
@ -28,6 +29,8 @@ Parameters:
Options:
--help Shows this script's documentation
--charset=<charset> Sets the charset type for esbuild to emit. Defaults to 'ascii'
but may also be 'utf8'.
--format=<format> Sets the format type to use for the generated bundle. Should be
'iife' or 'esm'.
@ -49,15 +52,28 @@ if(process.argv.length > 2) {
case '--help':
doHelp();
break;
case '--format': // bc TS uses this exact flag. esbuild... uses sourcemap (in the JS config)
let input = process.argv[++i];
switch(input) {
case 'iife':
case 'esm':
FORMAT = input;
case '--charset':
let charsetOption = process.argv[++i];
switch(charsetOption) {
case 'ascii':
case 'utf8':
CHARSET = charsetOption;
break;
default:
console.error(`Invalid bundling format specified: ${input}. Must be 'iife' or 'esm'.`);
console.error(`Invalid bundling format specified: ${charsetOption}. Must be 'ascii' or 'utf8'.`);
doHelp(1);
break;
}
break;
case '--format': // bc TS uses this exact flag. esbuild... uses sourcemap (in the JS config)
let formatOption = process.argv[++i];
switch(formatOption) {
case 'iife':
case 'esm':
FORMAT = formatOption;
break;
default:
console.error(`Invalid bundling format specified: ${formatOption}. Must be 'iife' or 'esm'.`);
doHelp(1);
break;
}
@ -128,6 +144,7 @@ const baseConfig = FORMAT == 'iife' ? iifeConfiguration : esmConfiguration;
const config: esbuild.BuildOptions = {
...jsVersionTarget == 'es6' ? forES6(baseConfig) : baseConfig,
entryPoints: [sourceFile],
charset: CHARSET as 'ascii' | 'utf8',
outfile: destFile,
minify: MINIFY,
metafile: !!profilePath,

View file

@ -1,78 +0,0 @@
#!/usr/bin/env bash
## START STANDARD BUILD SCRIPT INCLUDE
# adjust relative paths as necessary
THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
. "$(dirname "$THIS_SCRIPT")/../../../resources/build/builder.inc.sh"
## END STANDARD BUILD SCRIPT INCLUDE
. "$KEYMAN_ROOT/resources/shellHelperFunctions.sh"
BUNDLE_CMD="node $KEYMAN_ROOT/common/web/es-bundling/build/common-bundle.mjs"
################################ Main script ################################
builder_describe "Builds the gesture-recognition model for Web-based on-screen keyboards" \
"@/common/web/es-bundling build" \
"@/common/web/utils build" \
"clean" \
"configure" \
"build" \
"test" \
":module" \
":tools tools for testing & developing test resources for this module" \
"--ci sets the --ci option for child scripts (i.e, the $(builder_term test) action)"
builder_describe_outputs \
configure /node_modules \
build:module /common/web/gesture-recognizer/build/lib/index.mjs \
build:tools /common/web/gesture-recognizer/build/tools/lib/index.mjs
builder_parse "$@"
# TODO: build if out-of-date if test is specified
# TODO: configure if npm has not been run, and build is specified
function do_configure() {
verify_npm_setup
# Configure Web browser-engine testing environments. As is, this should only
# make changes when we update the dependency, even on our CI build agents.
playwright install
}
builder_run_action configure do_configure
if builder_start_action clean; then
rm -rf build/
builder_finish_action success clean
fi
if builder_start_action build:module; then
# Build
tsc --build $builder_verbose
$BUNDLE_CMD "${KEYMAN_ROOT}/common/web/gesture-recognizer/build/obj/index.js" \
--out "${KEYMAN_ROOT}/common/web/gesture-recognizer/build/lib/index.mjs" \
--format esm
builder_finish_action success build:module
fi
if builder_start_action build:tools; then
src/tools/build.sh build
builder_finish_action success build:tools
fi
if builder_start_action test:module; then
if builder_has_option --ci; then
./test.sh --ci
else
./test.sh
fi
builder_finish_action success test:module
fi
if builder_has_action test:tools && ! builder_has_action test:module; then
echo "The $(builder_term test:tools) action is currently a no-op."
fi

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,69 +0,0 @@
// @ts-check
import { devices, playwrightLauncher } from '@web/test-runner-playwright';
import { defaultReporter, summaryReporter } from '@web/test-runner';
import { LauncherWrapper, sessionStabilityReporter } from '@keymanapp/common-test-resources/test-runner-stability-reporter.mjs';
import named from '@keymanapp/common-test-resources/test-runner-rename-browser.mjs'
import { esbuildPlugin } from '@web/dev-server-esbuild';
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' })),
playwrightLauncher({ product: 'webkit', concurrency: 1}),
named(new LauncherWrapper(playwrightLauncher({ product: 'webkit', concurrency: 1, createBrowserContext({browser}) {
return browser.newContext({...devices['iPhone X'] });
}})), 'iOS Phone (emulated)'),
named(new LauncherWrapper(playwrightLauncher({ product: 'chromium' , createBrowserContext({browser}) {
return browser.newContext({...devices['Pixel 4'] })
}})), 'Android Phone (emulated)'),
],
concurrency: 10,
nodeResolve: true,
files: [
'**/*.spec.html'
],
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/web/gesture-recognizer/src/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,30 +0,0 @@
/*
* VS Code Intellisense needs this helper in order to properly use subpath imports in the
* test specs found under the `headless` subfolder. Otherwise, it'll report errors while
* editing - even if the tests themselves actually work.
*/
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "../../build/test/",
// Not needed when testing via Node, and when `true` it seems to desync preset breakpoints
// worse than when `false`.
"importHelpers": false,
"allowJs": true,
"baseUrl": "../../",
"tsBuildInfoFile": "../../build/test/tsconfig.tsbuildinfo",
"rootDir": "./",
"typeRoots": ["./resources/@types", "../../../../../node_modules/@types"]
},
"include": [
"./auto/headless/**/*.ts",
"./resources/**/*.ts"
],
// Undo the base config's exclude.
"exclude": ["../../../../../node_modules/promise-status-async/lib/index.d.ts"],
"references": [
{"path": "../../"}
]
}

View file

@ -1,84 +0,0 @@
#!/usr/bin/env bash
## START STANDARD BUILD SCRIPT INCLUDE
# adjust relative paths as necessary
THIS_SCRIPT="$(greadlink -f "${BASH_SOURCE[0]}" 2>/dev/null || readlink -f "${BASH_SOURCE[0]}")"
. "$(dirname "$THIS_SCRIPT")/../../../resources/build/build-utils.sh"
## END STANDARD BUILD SCRIPT INCLUDE
. $KEYMAN_ROOT/resources/shellHelperFunctions.sh
# This script runs from its own folder
cd "$(dirname $THIS_SCRIPT)"
################################ Main script ################################
builder_describe "Runs all tests for the gesture-recognizer module" \
"@/common/web/gesture-recognizer" \
"test+" \
":headless Runs headless user tests" \
":browser Runs browser-based user tests" \
"--ci Uses CI-based test configurations & emits CI-friendly test reports"
builder_parse "$@"
# TODO: build if out-of-date if test is specified
# TODO: configure if npm has not been run, and build is specified
# START - Script parameter configuration
REPORT_STYLE="local" # Default setting.
if builder_has_option --ci; then
REPORT_STYLE="ci"
echo "Replacing user-friendly test reports & configurations with CI-friendly versions."
fi
# END - Script parameter configuration
test-headless ( ) {
# During debugging, "--slow 0" allows reporting the duration of ALL tests, not just the ones that run long.
# Can be useful... but probably shouldn't be the default.
MOCHA_FLAGS=
if [ $REPORT_STYLE == "ci" ]; then
MOCHA_FLAGS="$MOCHA_FLAGS --reporter mocha-teamcity-reporter"
fi
# The currently-bundled declaration file for this package generates errors when compiling against it
# with current tsc versions.
rm -f "${KEYMAN_ROOT}/node_modules/promise-status-async/lib/index.d.ts"
tsc -b ./src/test/tsconfig.json
c8 mocha --recursive $MOCHA_FLAGS ./build/test/auto/headless/
}
test-browser ( ) {
local WTR_DEBUG=
local WTR_CONFIG=
if [[ $# -eq 1 && $1 == "debug" ]]; then
WTR_DEBUG=" --manual"
elif [ $REPORT_STYLE != "local" ]; then
WTR_CONFIG=.CI
fi
web-test-runner --config src/test/auto/browser/web-test-runner${WTR_CONFIG}.config.mjs ${WTR_DEBUG}
}
if builder_start_action test:headless; then
test-headless
builder_finish_action success test:headless
fi
if builder_start_action test:browser; then
if builder_has_option --debug; then
echo "Running browser-based unit tests in debug-mode configuration..."
echo
echo "${COLOR_YELLOW}You must manually terminate this mode (CTRL-C) for the script to exit.${COLOR_RESET}"
sleep 2
test-browser debug
else
test-browser
fi
builder_finish_action success test:browser
fi

View file

@ -7,3 +7,4 @@ delphi/**/*.~*
delphi/**/__recovery/
delphi/**/__history/
delphi/general/keymanversion_build.inc
delphi/ext/cef4delphi/packages/CEF4Delphi.res

View file

@ -1,299 +0,0 @@
# DEBUG=1
# TODO: this should be a shared Defines.mak for common,developer,windows. So we
# need to move any project-specific stuff into a defines-windows.mak,
# defines-etc.mak
#
# Paths
#
!IFNDEF KEYMAN_ROOT
!ERROR KEYMAN_ROOT must be defined!
!ENDIF
# TODO: COMMON_ROOT should be common\windows
COMMON_ROOT=$(KEYMAN_ROOT)\common\windows\delphi
WINDOWS_ROOT=$(KEYMAN_ROOT)\windows
OUTLIB=$(WINDOWS_ROOT)\lib
COMMON_OUTLIB=$(KEYMAN_ROOT)\windows\lib
COMMON_BIN=$(KEYMAN_ROOT)\windows\bin
# INCLUDE=$(ROOT)\src\global\inc;$(INCLUDE)
!IFDEF DEBUG
GO_FAST=1
MAKEFLAG_DEBUG="DEBUG=$(DEBUG)"
DELPHI_MSBUILD_FLAG_DEBUG="/p:Config=Debug"
!ELSE
!IFDEF TEAMCITY_PR_NUMBER
GO_FAST=1
!ENDIF
DELPHI_MSBUILD_FLAG_DEBUG="/p:Config=Release"
!ENDIF
!IFDEF USERDEFINES
MAKEFLAG_USERDEFINES="USERDEFINES=$(USERDEFINES)"
!ENDIF
!IFDEF SC_TIMESTAMP
MAKEFLAG_SC_TIMESTAMP="SC_TIMESTAMP=$(SC_TIMESTAMP)"
!ENDIF
!IFDEF BUILDHELP
MAKEFLAG_BUILDHELP="BUILDHELP=$(BUILDHELP)"
!ENDIF
!IFDEF LINT
MAKEFLAG_LINT="LINT=$(LINT)"
!ENDIF
!IFDEF NOUI
MAKEFLAG_QUIET="NOUI=$(NOUI)"
!ELSE
!IFDEF QUIET
MAKEFLAG_QUIET="QUIET=$(QUIET)"
!ENDIF
!ENDIF
!IFDEF RELEASE_OEM
MAKEFLAG_RELEASE_OEM="RELEASE_OEM=$(RELEASE_OEM)"
!ENDIF
#
# USERDEFINES allows the developer to specify overrides for various settings. We need a variable
# because Makefiles cannot test for file existence
#
# TODO: can we eliminate this?
!ifdef USERDEFINES
!include $(WINDOWS_ROOT)\src\UserDefines.mak
!endif
#
# Delphi Compiler Configuration - Delphi 10.3.2
#
!IFNDEF DELPHI_VERSION
DELPHI_VERSION=20.0
!ENDIF
DCC32PATH=C:\Program Files (x86)\Embarcadero\Studio\$(DELPHI_VERSION)\bin
#
# Pass local configuration through to sub-instances of MAKE
#
MAKE="nmake" /C $(MAKEFLAG_QUICK_BUILD_KEYMAN) $(MAKEFLAG_USERDEFINES) $(MAKEFLAG_DEBUG) $(MAKEFLAG_BUILDHELP) $(MAKEFLAG_BUILDRTF) $(MAKEFLAG_SC_TIMESTAMP) $(MAKEFLAG_LINT) $(MAKEFLAG_QUIET) $(MAKEFLAG_RELEASE_OEM)
#
# Delphi build commands
#
!IFDEF DEBUG
TARGET_PATH=Debug
!ELSE
TARGET_PATH=Release
!ENDIF
!IFDEF LINT
DELPHIWARNINGS=-W+MESSAGE_DIRECTIVE -W+IMPLICIT_STRING_CAST -W+IMPLICIT_STRING_CAST_LOSS -W+EXPLICIT_STRING_CAST -W+EXPLICIT_STRING_CAST_LOSS -W+CVT_WCHAR_TO_ACHAR -W+CVT_NARROWING_STRING_LOST -W+CVT_ACHAR_TO_WCHAR -W+CVT_WIDENING_STRING_LOST -W+UNICODE_TO_LOCALE -W+LOCALE_TO_UNICODE -W+IMPLICIT_VARIANTS
!ELSE
DELPHIWARNINGS=-W-MESSAGE_DIRECTIVE -W-IMPLICIT_STRING_CAST -W-IMPLICIT_STRING_CAST_LOSS -W-EXPLICIT_STRING_CAST -W-EXPLICIT_STRING_CAST_LOSS -W-CVT_WCHAR_TO_ACHAR -W-CVT_NARROWING_STRING_LOST -W-CVT_ACHAR_TO_WCHAR -W-CVT_WIDENING_STRING_LOST -W-UNICODE_TO_LOCALE -W-LOCALE_TO_UNICODE -W-IMPLICIT_VARIANTS -W-IMPLICIT_INTEGER_CAST_LOSS -W-IMPLICIT_CONVERSION_LOSS -W-COMBINING_SIGNED_UNSIGNED64 -W-COMBINING_SIGNED_UNSIGNED64
!ENDIF
DELPHIDPRPARAMS=-Q -B -GD -H -VT -^$C+ -^$D+ -^$J+ -^$L+ -^$O+ -^$Q- -^$R- -^$W+ -^$Y+ -E. $(DELPHIWARNINGS) -I$(DELPHIINCLUDES) -U$(DELPHIINCLUDES) -R$(DELPHIINCLUDES) -NSVcl;Vcl.Imaging;Vcl.Touch;Vcl.Samples;Vcl.Shell;System;Xml;Web;Soap;Winapi;System.Win -NU.\obj\Win32\$(TARGET_PATH) -E.\bin\Win32\$(TARGET_PATH)
DELPHIDPRPARAMS64=-Q -B -GD -H -VT -^$C+ -^$D+ -^$J+ -^$L+ -^$O+ -^$Q- -^$R- -^$W+ -^$Y+ -E. $(DELPHIWARNINGS) -I$(DELPHIINCLUDES) -U$(DELPHIINCLUDES) -R$(DELPHIINCLUDES) -NSVcl;Vcl.Imaging;Vcl.Touch;Vcl.Samples;Vcl.Shell;System;Xml;Web;Soap;Winapi;System.Win -NU.\obj\Win64\$(TARGET_PATH) -E.\bin\Win64\$(TARGET_PATH)
DELPHIDPKPARAMS=-Q -B -GD -VT -^$C+ -^$D+ -^$J+ -^$L+ -^$O+ -^$Q- -^$R- -^$W+ -^$Y+ -E. $(DELPHIWARNINGS) -I$(DELPHIINCLUDES) -U$(DELPHIINCLUDES) -R$(DELPHIINCLUDES) -NSVcl;Vcl.Imaging;Vcl.Touch;Vcl.Samples;Vcl.Shell;System;Xml;Web;Soap;Winapi;System.Win -LE$(OUTLIB) -LN$(OUTLIB) -NSData -NUobj\Win32\$(TARGET_PATH)
COMMON_DELPHIDPKPARAMS=-Q -B -GD -VT -^$C+ -^$D+ -^$J+ -^$L+ -^$O+ -^$Q- -^$R- -^$W+ -^$Y+ -E. $(DELPHIWARNINGS) -I$(DELPHIINCLUDES) -U$(DELPHIINCLUDES) -R$(DELPHIINCLUDES) -NSVcl;Vcl.Imaging;Vcl.Touch;Vcl.Samples;Vcl.Shell;System;Xml;Web;Soap;Winapi;System.Win -LE$(COMMON_OUTLIB) -LN$(COMMON_OUTLIB) -NSData -NUobj\Win32\$(TARGET_PATH)
# we are using cmd /c because dcc32/dcc64 are failing on direct execution
# from nmake
DCC32=cmd /c "$(DCC32PATH)\dcc32.exe" $(DELPHIDPRPARAMS)
DCC32DPK=cmd /c "$(DCC32PATH)\dcc32.exe" $(DELPHIDPKPARAMS)
COMMON_DCC32DPK=cmd /c "$(DCC32PATH)\dcc32.exe" $(COMMON_DELPHIDPKPARAMS)
DCC64=cmd /c "$(DCC32PATH)\dcc64.exe" $(DELPHIDPRPARAMS64) -N0x64\ -Ex64\
#
# Delphi MSBuild related commands and macros
#
DELPHI_MSBUILD="$(COMMON_ROOT)\tools\msbuild-wrapper.bat" "$(DCC32PATH)" $(DELPHI_MSBUILD_FLAG_DEBUG)
!IFDEF NODELPHI
DCC32=echo skipping
DCC32DPK=echo skipping
DCC64=echo skipping
DELPHI_MSBUILD=echo skipping
!ENDIF
# Visual C++ x86, x64
WIN32_TARGET_PATH=bin\Win32\$(TARGET_PATH)
X64_TARGET_PATH=bin\x64\$(TARGET_PATH)
# Delphi x86, x64
# WIN32_TARGET_PATH=...
WIN64_TARGET_PATH=bin\Win64\$(TARGET_PATH)
#
# Shared devtools app for common Delphi source manipulation
#
DEVTOOLS=$(COMMON_ROOT)\tools\devtools\$(WIN32_TARGET_PATH)\devtools.exe
#
# Other program build commands
#
BRCC32=rc.exe
HHC="C:\Program Files (x86)\HTML Help Workshop\hhc.exe"
NMAKE=nmake.exe
CL=cl.exe
MSBUILD=msbuild.exe
# /maxcpucount see https://devblogs.microsoft.com/cppblog/precompiled-header-pch-issues-and-recommendations/
MT=mt.exe
VCBUILD=error
!IFDEF DEBUG
MSBUILD_BUILD=/t:Build /p:Configuration=Debug
MSBUILD_CLEAN=/t:Clean /p:Configuration=Debug
!ELSE
MSBUILD_BUILD=/t:Rebuild /p:Configuration=Release
MSBUILD_CLEAN=/t:Clean /p:Configuration=Release
!ENDIF
COPY=copy
ISXBUILD=C:\PROGRA~1\INSTALLSHIELD\Express\System\IsExpCmdBld
WZZIPPATH="C:\program files\7-zip\7z.exe"
!IFDEF GO_FAST
WZZIP=$(WZZIPPATH) a -mx1
!ELSE
WZZIP=$(WZZIPPATH) a -mx9
!ENDIF
WZUNZIP=$(WZZIPPATH) e
# we are using cmd /c because tds2dbg is failing on direct execution
# from nmake
TDS2DBG=cmd /c $(KEYMAN_ROOT)\common\windows\bin\tools\tds2dbg
SENTRYTOOL=$(COMMON_ROOT)\tools\sentrytool\$(WIN32_TARGET_PATH)\sentrytool.exe
SENTRYTOOL_DELPHIPREP=$(SENTRYTOOL) delphiprep -r $(KEYMAN_ROOT) -i $(DELPHIINCLUDES)
WIXPATH="c:\program files (x86)\WiX Toolset v3.11\bin"
WIXCANDLE=$(WIXPATH)\candle.exe -wx -nologo
!IFDEF LINT
WIXLIGHTLINT=
!ELSE
# we suppress ICE82 because it reports spurious errors with merge module keymanengine to do with duplicate sequence numbers. Safely ignored.
WIXLIGHTLINT= -sice:ICE82 -sice:ICE80
!ENDIF
!IFDEF GO_FAST
# for debug builds, we turn off compression because it is so hideously slow
# for test builds, we also turn off compression
WIXLIGHTCOMPRESSION=-dcl:none
!ELSE
WIXLIGHTCOMPRESSION=-dcl:high
!ENDIF
WIXLIGHT=$(WIXPATH)\light.exe -wx -nologo $(WIXLIGHTLINT) $(WIXLIGHTCOMPRESSION)
WIXLIT=$(WIXPATH)\lit.exe -wx -nologo
WIXHEAT=$(WIXPATH)\heat.exe
LINKPATH=link.exe
#
# Certificates and code signing
#
!ifdef SIGNCODE_BUILD
MAKE=$(MAKE) "SIGNCODE_BUILD=$(SIGNCODE_BUILD)"
!else
MAKE=$(MAKE)
!endif
#
# To get a .pfx from a .spc and .pvk, run pvk2pfx.exe
#
!IFNDEF SC_PFX_SHA1
SC_PFX_SHA1="$(COMMON_ROOT)\tools\certificates\keymantest-sha1.pfx"
!ENDIF
!IFNDEF SC_PFX_SHA256
SC_PFX_SHA256="$(COMMON_ROOT)\tools\certificates\keymantest-sha256.pfx"
!ENDIF
!IFNDEF SC_URL
SC_URL="https://keyman.com/"
!ENDIF
!IFNDEF SC_PWD
SC_PWD=""
!ENDIF
SIGNCODE=@"$(KEYMAN_ROOT)\common\windows\signtime.bat" signtool.exe $(SC_PFX_SHA1) $(SC_PFX_SHA256) $(SC_URL) $(SC_PWD)
#
# On some computers, the PLATFORM environment variable is set to x86. This can break msbuild
# with our projects. This may be resolvable in the future, but for now the easy fix is ...
#
PLATFORM=Win32
#
# mkver commands. mkver determines tag from the local build environment variables
# in the same way as /resources/build/build-utils.sh.
#
!ifdef GIT_BASH_FOR_KEYMAN
MKVER_SH=$(GIT_BASH_FOR_KEYMAN) $(KEYMAN_ROOT)\common\windows\mkver.sh
!else
MKVER_SH=start /wait $(KEYMAN_ROOT)\common\windows\mkver.sh
!endif
MKVER_M=$(MKVER_SH) manifest.in manifest.xml
MKVER_U=$(MKVER_SH)
#
# Symstore
#
# KEYMAN_SYMSTOREPATH defaults to sibling folder "symbols". If it is not present,
# then we won't attempt to write symbols to the store.
!IFNDEF KEYMAN_SYMSTOREPATH
KEYMAN_SYMSTOREPATH=$(KEYMAN_ROOT)\..\symbols
!ENDIF
# Nearly matches algorithm from resources/build/build-utils.sh
# For now, we'll use it only for SYMSTORE, where it is for reference
# only. Thus using the variable name __VERSION_WITH_TAG. Issues:
# 1. always appends tier, even for stable
# 2. test builds will append a branch name for master/beta/stable-x.y
# 3. this is only available for `make symbols` (VERSION_WIN, VERSION_TIER
# are defined in Targets.mak only here)
# Fixing this properly would be possible but take a fair bit more
# work than I want to do just now. The intent is to make it possible to find
# symbols in the symstore index which we can purge later on.
__VERSION_WITH_TAG=$(VERSION_WIN)-$(VERSION_TIER)
!IFNDEF TEAMCITY_VERSION
__VERSION_WITH_TAG=$(__VERSION_WITH_TAG)-local
!ELSE
!IFDEF TEAMCITY_PR_NUMBER
__VERSION_WITH_TAG=$(__VERSION_WITH_TAG)-test-$(TEAMCITY_PR_NUMBER)
!ENDIF
!ENDIF
# This command depends on VERSION_WIN and VERSION_TIER being defined, through
# `make symbols` (i.e. don't call `make wrap-symbols`)
SYMSTORE="C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\symstore.exe" add \
/s "$(KEYMAN_SYMSTOREPATH)" \
/v "$(VERSION_WIN)" \
/c "Version: $(__VERSION_WITH_TAG)" \
/compress /f
CLEAN=-del /S /Q

View file

@ -1,59 +0,0 @@
#
# Header.mak - used for makefiles which are in parent folders
#
HEADER_MAK=1
!IFNDEF TARGETS
!ERROR You must define the targets before including the Header.mak file!
!ENDIF
!IFNDEF KEYMAN_ROOT
!ERROR KEYMAN_ROOT must be defined!
!ENDIF
# This path will need to be updated if the root path changes
!include $(KEYMAN_ROOT)\common\windows\delphi\Defines.mak
#
# The targets build, signcode, symbols are standard
# targets for all projects
#
build: $(BUILDPREREQ)
$(MAKE) "TARGET=build" $(TARGETS)
!IFNDEF NOTARGET_SIGNCODE
signcode:
$(MAKE) "TARGET=signcode" $(TARGETS)
symbols:
$(MAKE) "TARGET=symbols" $(TARGETS)
!ELSE
signcode:
rem no signcode required
symbols:
rem no symbols required
!ENDIF
build-release:
!IFDEF RELEASE_TARGETS
$(MAKE) "TARGET=build-release" $(RELEASE_TARGETS)
!ELSE
@rem
!ENDIF
clean:
$(MAKE) "TARGET=clean" $(TARGETS) $(CLEANS)
install:
$(MAKE) "TARGET=install" $(TARGETS)
test-manifest:
!IFDEF MANIFESTS
$(MAKE) "TARGET=test-manifest" $(MANIFESTS)
!ELSE
$(MAKE) "TARGET=test-manifest" $(TARGETS)
!ENDIF

View file

@ -1,14 +0,0 @@
!CMDSWITCHES +S
def-clean:
$(CLEAN) *.err *.stat *.dproj.local *.Build.CppClean.Log *.suo *.jdbg *.dbg *.dcu *.~* *.dsk *.exe *.rsm *.ncb *.opt *.pch *.plg *.aps *.001 *.sbr *.dep *.drc *.bak *.pdb *.lib *.cod *.ilk *.tds vc80.idb *.map *.bsc version.res manifest.xml manifest.res >nul 2>nul
$(CLEAN) ExcMagic.Debug *.wixpdb *.identcache *.embed.manifest *.embed.manifest.res *.intermediate.manifest error.log >nul 2>nul
if exist bin rd /s/q bin
if exist obj rd /s/q obj
!CMDSWITCHES -S
# This virtual rule forces targets which are folders, e.g. `kmshell` to
# always execute
.virtual:
rem always execute

View file

@ -13,31 +13,6 @@
<PropertyGroup Condition="'$(Config)'=='Base' or '$(Base)'!=''">
<Base>true</Base>
</PropertyGroup>
<PropertyGroup Condition="('$(Platform)'=='Android' and '$(Base)'=='true') or '$(Base_Android)'!=''">
<Base_Android>true</Base_Android>
<CfgParent>Base</CfgParent>
<Base>true</Base>
</PropertyGroup>
<PropertyGroup Condition="('$(Platform)'=='Android64' and '$(Base)'=='true') or '$(Base_Android64)'!=''">
<Base_Android64>true</Base_Android64>
<CfgParent>Base</CfgParent>
<Base>true</Base>
</PropertyGroup>
<PropertyGroup Condition="('$(Platform)'=='iOSDevice32' and '$(Base)'=='true') or '$(Base_iOSDevice32)'!=''">
<Base_iOSDevice32>true</Base_iOSDevice32>
<CfgParent>Base</CfgParent>
<Base>true</Base>
</PropertyGroup>
<PropertyGroup Condition="('$(Platform)'=='iOSDevice64' and '$(Base)'=='true') or '$(Base_iOSDevice64)'!=''">
<Base_iOSDevice64>true</Base_iOSDevice64>
<CfgParent>Base</CfgParent>
<Base>true</Base>
</PropertyGroup>
<PropertyGroup Condition="('$(Platform)'=='iOSSimulator' and '$(Base)'=='true') or '$(Base_iOSSimulator)'!=''">
<Base_iOSSimulator>true</Base_iOSSimulator>
<CfgParent>Base</CfgParent>
<Base>true</Base>
</PropertyGroup>
<PropertyGroup Condition="('$(Platform)'=='Win32' and '$(Base)'=='true') or '$(Base_Win32)'!=''">
<Base_Win32>true</Base_Win32>
<CfgParent>Base</CfgParent>
@ -59,6 +34,12 @@
<CfgParent>Base</CfgParent>
<Base>true</Base>
</PropertyGroup>
<PropertyGroup Condition="('$(Platform)'=='Win32' and '$(Cfg_2)'=='true') or '$(Cfg_2_Win32)'!=''">
<Cfg_2_Win32>true</Cfg_2_Win32>
<CfgParent>Cfg_2</CfgParent>
<Cfg_2>true</Cfg_2>
<Base>true</Base>
</PropertyGroup>
<PropertyGroup Condition="'$(Base)'!=''">
<DCC_DcuOutput>.\obj\$(Platform)\$(Config)</DCC_DcuOutput>
<DCC_ExeOutput>.\$(Platform)\$(Config)</DCC_ExeOutput>
@ -77,23 +58,6 @@
<VerInfo_Locale>3081</VerInfo_Locale>
<VerInfo_Keys>CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=</VerInfo_Keys>
</PropertyGroup>
<PropertyGroup Condition="'$(Base_Android)'!=''">
<DCC_CBuilderOutput>None</DCC_CBuilderOutput>
<EnabledSysJars>android-support-v4.dex.jar;cloud-messaging.dex.jar;com-google-android-gms.play-services-ads-base.17.2.0.dex.jar;com-google-android-gms.play-services-ads-identifier.16.0.0.dex.jar;com-google-android-gms.play-services-ads-lite.17.2.0.dex.jar;com-google-android-gms.play-services-ads.17.2.0.dex.jar;com-google-android-gms.play-services-analytics-impl.16.0.8.dex.jar;com-google-android-gms.play-services-analytics.16.0.8.dex.jar;com-google-android-gms.play-services-base.16.0.1.dex.jar;com-google-android-gms.play-services-basement.16.2.0.dex.jar;com-google-android-gms.play-services-gass.17.2.0.dex.jar;com-google-android-gms.play-services-identity.16.0.0.dex.jar;com-google-android-gms.play-services-maps.16.1.0.dex.jar;com-google-android-gms.play-services-measurement-base.16.4.0.dex.jar;com-google-android-gms.play-services-measurement-sdk-api.16.4.0.dex.jar;com-google-android-gms.play-services-stats.16.0.1.dex.jar;com-google-android-gms.play-services-tagmanager-v4-impl.16.0.8.dex.jar;com-google-android-gms.play-services-tasks.16.0.1.dex.jar;com-google-android-gms.play-services-wallet.16.0.1.dex.jar;com-google-firebase.firebase-analytics.16.4.0.dex.jar;com-google-firebase.firebase-common.16.1.0.dex.jar;com-google-firebase.firebase-iid-interop.16.0.1.dex.jar;com-google-firebase.firebase-iid.17.1.1.dex.jar;com-google-firebase.firebase-measurement-connector.17.0.1.dex.jar;com-google-firebase.firebase-messaging.17.5.0.dex.jar;fmx.dex.jar;google-play-billing.dex.jar;google-play-licensing.dex.jar</EnabledSysJars>
</PropertyGroup>
<PropertyGroup Condition="'$(Base_Android64)'!=''">
<DCC_CBuilderOutput>None</DCC_CBuilderOutput>
<EnabledSysJars>android-support-v4.dex.jar;cloud-messaging.dex.jar;com-google-android-gms.play-services-ads-base.17.2.0.dex.jar;com-google-android-gms.play-services-ads-identifier.16.0.0.dex.jar;com-google-android-gms.play-services-ads-lite.17.2.0.dex.jar;com-google-android-gms.play-services-ads.17.2.0.dex.jar;com-google-android-gms.play-services-analytics-impl.16.0.8.dex.jar;com-google-android-gms.play-services-analytics.16.0.8.dex.jar;com-google-android-gms.play-services-base.16.0.1.dex.jar;com-google-android-gms.play-services-basement.16.2.0.dex.jar;com-google-android-gms.play-services-gass.17.2.0.dex.jar;com-google-android-gms.play-services-identity.16.0.0.dex.jar;com-google-android-gms.play-services-maps.16.1.0.dex.jar;com-google-android-gms.play-services-measurement-base.16.4.0.dex.jar;com-google-android-gms.play-services-measurement-sdk-api.16.4.0.dex.jar;com-google-android-gms.play-services-stats.16.0.1.dex.jar;com-google-android-gms.play-services-tagmanager-v4-impl.16.0.8.dex.jar;com-google-android-gms.play-services-tasks.16.0.1.dex.jar;com-google-android-gms.play-services-wallet.16.0.1.dex.jar;com-google-firebase.firebase-analytics.16.4.0.dex.jar;com-google-firebase.firebase-common.16.1.0.dex.jar;com-google-firebase.firebase-iid-interop.16.0.1.dex.jar;com-google-firebase.firebase-iid.17.1.1.dex.jar;com-google-firebase.firebase-measurement-connector.17.0.1.dex.jar;com-google-firebase.firebase-messaging.17.5.0.dex.jar;fmx.dex.jar;google-play-billing.dex.jar;google-play-licensing.dex.jar</EnabledSysJars>
</PropertyGroup>
<PropertyGroup Condition="'$(Base_iOSDevice32)'!=''">
<DCC_CBuilderOutput>None</DCC_CBuilderOutput>
</PropertyGroup>
<PropertyGroup Condition="'$(Base_iOSDevice64)'!=''">
<DCC_CBuilderOutput>None</DCC_CBuilderOutput>
</PropertyGroup>
<PropertyGroup Condition="'$(Base_iOSSimulator)'!=''">
<DCC_CBuilderOutput>None</DCC_CBuilderOutput>
</PropertyGroup>
<PropertyGroup Condition="'$(Base_Win32)'!=''">
<DCC_Namespace>Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace)</DCC_Namespace>
<BT_BuildType>Debug</BT_BuildType>
@ -113,6 +77,7 @@
<DCC_RemoteDebug>false</DCC_RemoteDebug>
<VerInfo_IncludeVerInfo>true</VerInfo_IncludeVerInfo>
<VerInfo_Locale>1033</VerInfo_Locale>
<DCC_HppOutput>..\..\lib</DCC_HppOutput>
</PropertyGroup>
<PropertyGroup Condition="'$(Cfg_2)'!=''">
<DCC_LocalDebugSymbols>false</DCC_LocalDebugSymbols>
@ -120,6 +85,11 @@
<DCC_SymbolReferenceInfo>0</DCC_SymbolReferenceInfo>
<DCC_DebugInformation>0</DCC_DebugInformation>
</PropertyGroup>
<PropertyGroup Condition="'$(Cfg_2_Win32)'!=''">
<DCC_HppOutput>..\..\lib</DCC_HppOutput>
<VerInfo_IncludeVerInfo>true</VerInfo_IncludeVerInfo>
<VerInfo_Locale>1033</VerInfo_Locale>
</PropertyGroup>
<ItemGroup>
<DelphiCompile Include="$(MainSource)">
<MainSource>MainSource</MainSource>
@ -164,8 +134,6 @@
<Source Name="MainSource">common_components.dpk</Source>
</Source>
<Excluded_Packages>
<Excluded_Packages Name="c:\projects\keyman\app\windows\lib\DCPdelphi2009.bpl">File c:\projects\keyman\app\windows\lib\DCPdelphi2009.bpl not found</Excluded_Packages>
<Excluded_Packages Name="c:\projects\keyman\app\windows\lib\delphiprojectmanager.bpl">File c:\projects\keyman\app\windows\lib\delphiprojectmanager.bpl not found</Excluded_Packages>
<Excluded_Packages Name="$(BDSBIN)\dcloffice2k260.bpl">Microsoft Office 2000 Sample Automation Server Wrapper Components</Excluded_Packages>
<Excluded_Packages Name="$(BDSBIN)\dclofficexp260.bpl">Microsoft Office XP Sample Automation Server Wrapper Components</Excluded_Packages>
</Excluded_Packages>
@ -944,13 +912,6 @@
<ProjectRoot Platform="Android64" Name="$(PROJECTNAME)"/>
</Deployment>
<Platforms>
<Platform value="Android">False</Platform>
<Platform value="Android64">False</Platform>
<Platform value="iOSDevice32">False</Platform>
<Platform value="iOSDevice64">False</Platform>
<Platform value="iOSSimulator">False</Platform>
<Platform value="OSX32">False</Platform>
<Platform value="OSX64">False</Platform>
<Platform value="Win32">True</Platform>
<Platform value="Win64">False</Platform>
</Platforms>

View file

@ -21,6 +21,8 @@ builder_describe_outputs \
function do_certificates() {
rm -f KeymanTestCA-sha1.* KeymanTest-sha1.* KeymanTest-sha256.* KeymanTestCA-sha256.*
source "$KEYMAN_ROOT/resources/build/win/visualstudio_environment.inc.sh"
makecert -r -pe -n "CN=Keyman Test CA SHA1" -ss CA -sr CurrentUser -a sha1 -cy authority -sky signature -sv KeymanTestCA-sha1.pvk KeymanTestCA-sha1.cer
certutil -user -addstore Root KeymanTestCA-sha1.cer
makecert -pe -n "CN=Keyman Test Certificate SHA1" -a sha1 -cy end -sky signature -ic KeymanTestCA-sha1.cer -iv KeymanTestCA-sha1.pvk -sv KeymanTest-sha1.pvk KeymanTest-sha1.cer

View file

@ -85,7 +85,7 @@ ldml_processor::ldml_processor(path const & kb_path, const std::vector<uint8_t>
} else {
str = keyEntry->get_to_string();
}
keys.add((km_core_virtual_key)kmapEntry->vkey, (uint16_t)kmapEntry->mod, str);
keys.add((km_core_virtual_key)kmapEntry->vkey, kmapEntry->mod, str);
}
} // else: no keys! but still valid. Just, no keys.

View file

@ -17,7 +17,7 @@ vkeys::vkeys() : vkey_to_string() {
}
void
vkeys::add(km_core_virtual_key vk, uint16_t modifier_state, std::u16string output) {
vkeys::add(km_core_virtual_key vk, km_core_ldml_modifier_state modifier_state, std::u16string output) {
// construct key
const vkey_id id(vk, modifier_state);
// assign the string

View file

@ -19,10 +19,17 @@ namespace km {
namespace core {
namespace ldml {
/**
* LDML keyboards have 32-bit modifier flags in order to support
* LDML_KEYS_MOD_OTHER (0x10000), unlike the Core APIs which have only 16 bit
* modifier flags.
*/
typedef uint32_t km_core_ldml_modifier_state;
/**
* identifier for keybag lookup
*/
typedef std::pair<km_core_virtual_key, uint16_t> vkey_id;
typedef std::pair<km_core_virtual_key, km_core_ldml_modifier_state> vkey_id;
/**
* LDML Class to manage all things key related: vkey remapping and vkey to string
@ -35,9 +42,9 @@ public:
vkeys();
/**
* add a vkey to the bag
* add a vkey to the bag.
*/
void add(km_core_virtual_key vk, uint16_t modifier_state, std::u16string output);
void add(km_core_virtual_key vk, km_core_ldml_modifier_state ldml_modifier_state, std::u16string output);
/**
* Lookup a vkey, returns an empty string if not found

View file

@ -2,6 +2,42 @@
This is the current home for Keyman Developer.
## Build Prerequisites
* See [Windows Build Environment Configuration](../../docs/build/windows.md).
## Building Keyman Developer
1. Start 'Git Bash' (part of Git for Windows).
2. Run `developer/src/build.sh`.
3. Artifacts from a successful build will be placed in **developer/bin** folder.
*Note*: running `build.sh` will currently reset the packages and path settings
in your Delphi environment. If you use Delphi for other projects, you should
consider building Keyman under a login user dedicated to it, or in a VM.
Type `build.sh` to see build targets and actions. Common build actions are:
* `build.sh build`
: builds Keyman Developer
* `build.sh clean`
: remove temporary files and build artifacts
* `build.sh publish`
: makes a release of all Keyman Developer projects
* `build.sh install`
: install some or all components to Program Files (requires elevated command prompt).
### Building without Delphi
It is possible to build all components that do _not_ require Delphi. Currently a
few components are Delphi-based (TIKE, setup, a few others), but you may be able
to get away without building them. In this situation, we recommend copying the
relevant Delphi-built components into developer/bin folders from a compatible
installed version of Keyman Developer for testing and debugging purposes.
# Folders
## common
@ -106,4 +142,6 @@ testing and packing keyboards for distribution.
## tools
Various build-time tools for Keyman Developer.
Various build-time tools for Keyman Developer.

View file

@ -35,9 +35,9 @@ function copy_cldr_imports() {
# TODO-LDML: developer/src/inst/download.in.mak needs these also...
CLDR_PATH=$(dirname "$CLDR_INFO_PATH")
CLDR_VER=$(basename "$CLDR_PATH")
mkdir -p "$THIS_SCRIPT_PATH/build/src/import/$CLDR_VER"
mkdir -p "$THIS_SCRIPT_PATH/build/src/types/import/$CLDR_VER"
# TODO-LDML: When these are copied, the DOCTYPE will break due to the wrong path. We don't use the DTD so it should be OK.
cp "$CLDR_INFO_PATH" "$CLDR_PATH/import/"*.xml "$THIS_SCRIPT_PATH/build/src/import/$CLDR_VER/"
cp "$CLDR_INFO_PATH" "$CLDR_PATH/import/"*.xml "$THIS_SCRIPT_PATH/build/src/types/import/$CLDR_VER/"
done
}

View file

@ -23,7 +23,7 @@ export class LDMLKeyboardXMLSourceFileReader {
}
static get defaultImportsURL(): [string,string] {
return ['../../import/', import.meta.url];
return ['../import/', import.meta.url];
}
readImportFile(version: string, subpath: string): Uint8Array {

View file

@ -3,6 +3,23 @@ import { LDMLKeyboard, CompilerCallbacks } from "@keymanapp/developer-utils";
import { KeysCompiler } from "./keys.js";
import { LdmlCompilerMessages } from "./ldml-compiler-messages.js";
// This is a partial polyfill for findLast, so not polluting Array.prototype
// https://medium.com/@stheodorejohn/findlast-method-polyfill-in-javascript-bridging-browser-gaps-c3baf6aabae1
// TODO: remove and replace with Array.prototype.findLast when it is
// well-supported
function findLast(arr: any, callback: any) {
if (!arr) {
return undefined;
}
const len = arr.length >>> 0;
for (let i = len - 1; i >= 0; i--) {
if (callback(arr[i], i, arr)) {
return arr[i];
}
}
return undefined;
}
export class LdmlKeyboardVisualKeyboardCompiler {
public constructor(private callbacks: CompilerCallbacks) {
}
@ -57,7 +74,8 @@ export class LdmlKeyboardVisualKeyboardCompiler {
const keyId = key;
x++;
let keydef = source.keyboard3.keys?.key?.find(x => x.id == key);
//@ts-ignore
let keydef = findLast(source.keyboard3.keys?.key, x => x.id == key);
if (!keydef) {
this.callbacks.reportMessage(

View file

@ -9,6 +9,8 @@ import { KeymanFileTypes } from '@keymanapp/common-types';
describe('LexicalModelCompiler', function () {
let callbacks = new TestCompilerCallbacks();
this.timeout(5000);
// Try to compile ALL of the correct models.
const MODELS = [
'example.qaa.sencoten',

View file

@ -2310,11 +2310,11 @@ KMX_DWORD GetXStringImpl(PKMX_WCHAR tstr, PFILE_KEYBOARD fk, PKMX_WCHAR str, KMX
{
if (u16icmp(q, fk->dpStoreArray[i].szName) == 0) break;
}
if (i == fk->cxStoreArray) return KmnCompilerMessages::ERROR_StoreDoesNotExist;
if (!kmcmp::IsValidCallStore(&fk->dpStoreArray[i])) return KmnCompilerMessages::ERROR_InvalidCall;
kmcmp::CheckStoreUsage(fk, i, FALSE, FALSE, TRUE);
if (i == fk->cxStoreArray) return KmnCompilerMessages::ERROR_StoreDoesNotExist;
tstr[mx++] = UC_SENTINEL;
tstr[mx++] = CODE_CALL;
tstr[mx++] = (KMX_WCHAR)i + 1;

View file

@ -1225,6 +1225,94 @@ TEST_F(CompilerTest, GetXStringImpl_type_o_test) {
EXPECT_EQ(0, u16cmp(tstr_outs_space_after_valid, tstr));
}
// tests strings starting with 'c'
TEST_F(CompilerTest, GetXStringImpl_type_c_test) {
KMX_WCHAR tstr[128];
fileKeyboard.version = VERSION_60;
KMX_WCHAR str[LINESIZE];
KMX_WCHAR output[GLOBAL_BUFSIZE];
PKMX_WCHAR newp = nullptr;
PFILE_STORE file_store = new FILE_STORE[100];
fileKeyboard.cxStoreArray = 3u;
fileKeyboard.dpStoreArray = file_store;
file_store[1].fIsCall = TRUE;
file_store[1].dwSystemID = TSS_NONE;
u16cpy(file_store[0].szName, u"a");
u16cpy(file_store[1].szName, u"b");
u16cpy(file_store[2].szName, u"c");
// call, KmnCompilerMessages::ERROR_501FeatureOnly_Call
fileKeyboard.version = VERSION_50;
u16cpy(str, u"call");
EXPECT_EQ(KmnCompilerMessages::ERROR_501FeatureOnly_Call, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
// call, KmnCompilerMessages::ERROR_CallInVirtualKeySection *** TODO ***
// call, no close delimiter => NULL
fileKeyboard.version = VERSION_501;
u16cpy(str, u"call(");
EXPECT_EQ(KmnCompilerMessages::ERROR_InvalidCall, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
// call, empty delimiters => empty string
fileKeyboard.version = VERSION_501;
u16cpy(str, u"call()");
EXPECT_EQ(KmnCompilerMessages::ERROR_InvalidCall, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
// call, space in delimiters (see I11814, I11937, #11910, #11894, #11938)
fileKeyboard.version = VERSION_501;
u16cpy(str, u"call( )");
EXPECT_EQ(KmnCompilerMessages::ERROR_InvalidCall, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
// call, KmnCompilerMessages::ERROR_StoreDoesNotExist
fileKeyboard.version = VERSION_501;
u16cpy(str, u"call(d)");
EXPECT_EQ(KmnCompilerMessages::ERROR_StoreDoesNotExist, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
// call, KmnCompilerMessages::ERROR_StoreDoesNotExist, space before store
fileKeyboard.version = VERSION_501;
u16cpy(str, u"call( d)");
EXPECT_EQ(KmnCompilerMessages::ERROR_StoreDoesNotExist, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
// call, KmnCompilerMessages::ERROR_StoreDoesNotExist, space after store
fileKeyboard.version = VERSION_501;
u16cpy(str, u"call(d )");
EXPECT_EQ(KmnCompilerMessages::ERROR_StoreDoesNotExist, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
// call, KmnCompilerMessages::ERROR_InvalidCall
fileKeyboard.version = VERSION_501;
file_store[1].dpString = (PKMX_WCHAR)u"*"; // cause IsValidCallStore() to fail
u16cpy(str, u"call(b)");
EXPECT_EQ(KmnCompilerMessages::ERROR_InvalidCall, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
// call, valid
fileKeyboard.version = VERSION_501;
file_store[1].dpString = (PKMX_WCHAR)u"a.dll:A";
file_store[1].dwSystemID = TSS_NONE;
u16cpy(str, u"call(b)");
EXPECT_EQ(STATUS_Success, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
const KMX_WCHAR tstr_call_valid[] = { UC_SENTINEL, CODE_CALL, 2, 0 };
EXPECT_EQ(0, u16cmp(tstr_call_valid, tstr));
EXPECT_EQ(TSS_CALLDEFINITION, file_store[1].dwSystemID);
// call, space before store, valid
fileKeyboard.version = VERSION_501;
file_store[1].dpString = (PKMX_WCHAR)u"a.dll:A";
file_store[1].dwSystemID = TSS_NONE;
u16cpy(str, u"call( b)");
EXPECT_EQ(STATUS_Success, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(0, u16cmp(tstr_call_valid, tstr));
EXPECT_EQ(TSS_CALLDEFINITION, file_store[1].dwSystemID);
// call, space after store, valid (see I11937, #11938)
fileKeyboard.version = VERSION_501;
file_store[1].dpString = (PKMX_WCHAR)u"a.dll:A";
file_store[1].dwSystemID = TSS_NONE;
u16cpy(str, u"call(b )");
EXPECT_EQ(STATUS_Success, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(0, u16cmp(tstr_call_valid, tstr));
EXPECT_EQ(TSS_CALLDEFINITION, file_store[1].dwSystemID);
}
// KMX_DWORD process_baselayout(PFILE_KEYBOARD fk, PKMX_WCHAR q, PKMX_WCHAR tstr, int *mx)
// KMX_DWORD process_platform(PFILE_KEYBOARD fk, PKMX_WCHAR q, PKMX_WCHAR tstr, int *mx)
// KMX_DWORD process_if_synonym(KMX_DWORD dwSystemID, PFILE_KEYBOARD fk, PKMX_WCHAR q, PKMX_WCHAR tstr, int *mx)

View file

@ -19,7 +19,7 @@ source "$KEYMAN_ROOT/resources/build/win/environment.inc.sh"
WIN32_TARGET="$WIN32_TARGET_PATH/tike.exe"
builder_describe_outputs \
configure:project /resources/build/win/delphi_environment_generated.inc.sh \
configure:project /developer/src/tike/xml/layoutbuilder/keymanweb-osk.ttf \
build:project /developer/src/tike/$WIN32_TARGET
#-------------------------------------------------------------------------------------------------------------------
@ -31,6 +31,7 @@ function do_configure() {
mkdir -p "$DEVELOPER_PROGRAM"
cp "$KEYMAN_ROOT/common/schemas/kps/kps.xsd" "$DEVELOPER_PROGRAM"
cp "$KEYMAN_ROOT/common/resources/fonts/keymanweb-osk.ttf" "$DEVELOPER_ROOT/src/tike/xml/layoutbuilder/keymanweb-osk.ttf"
run_in_vs_env rc icons.rc
}
function do_monaco_copy() {
@ -52,8 +53,6 @@ function do_monaco_copy() {
pushd "$DEVELOPER_ROOT/src/tike/xml/app/lib/sentry"
replaceVersionStrings_Mkver init.js.in init.js
popd
run_in_vs_env rc icons.rc
}
KEYMANCORE_DLL=keymancore-2.dll

View file

@ -63,17 +63,33 @@ See [node.md](node.md) for more information.
#### Emscripten
You'll also have to install `emscripten` (version 3.1.44 is known to work):
You'll also have to install `emscripten`:
```shell
git clone https://github.com/emscripten-core/emsdk.git
cd emsdk
./emsdk install 3.1.44
./emsdk activate 3.1.44
export EMSCRIPTEN_BASE=$(pwd)/upstream/emscripten
./emsdk install 3.1.58
./emsdk activate 3.1.58
export EMSCRIPTEN_BASE="$(pwd)/upstream/emscripten"
echo "export EMSCRIPTEN_BASE=\"$EMSCRIPTEN_BASE\"" >> .bashrc
```
**NOTE:** Don't put EMSDK on the path, i.e. don't source `emsdk_env.sh`.
> ![WARNING]
> Don't put EMSDK on the path, i.e. don't source `emsdk_env.sh`.
>
> Emscripten very unhelpfully overwrites `JAVA_HOME`, and adds its own
> versions of Python, Node and Java to the `PATH`. For best results, restart
> your shell after installing Emscripten so that you don't end up with the
> wrong versions.
**Optional environment variables**:
To let the Keyman build scripts control the version of Emscripten installed on
your computer:
```shell
export KEYMAN_USE_EMSDK=1
```
## Keyman Core

36
docs/build/macos.md vendored
View file

@ -91,16 +91,38 @@ See [node.md](node.md) for more information.
### emscripten
To install emscripten:
The recommended way to install emscripten is with the official method, as that
then allows the build scripts to select the appropriate version automatically.
```shell
brew install emscripten
To install emscripten, `cd` to an appropriate path, and then:
```bash
git clone https://github.com/emscripten-core/emsdk
cd emsdk
emsdk install 3.1.58
emsdk activate 3.1.58
export EMSCRIPTEN_BASE="$(pwd)/upstream/emscripten"
echo "export EMSCRIPTEN_BASE=\"$EMSCRIPTEN_BASE\"" >> .bashrc
```
Note: if you install emscripten with brew on macOS, only emscripten binaries are
added to the path via symlinks. This makes it reasonably safe to have emscripten
on the path, unlike on other platforms where emscripten also ends up adding its
versions of node, python, and other binaries to the path.
You will want to add `EMSCRIPTEN_BASE` to your .bashrc.
> ![WARNING]
> Don't put EMSDK on the path, i.e. don't source `emsdk_env.sh`.
>
> Emscripten very unhelpfully overwrites `JAVA_HOME`, and adds its own
> versions of Python, Node and Java to the `PATH`. For best results, restart
> your shell after installing Emscripten so that you don't end up with the
> wrong versions.
**Optional environment variables**:
To let the Keyman build scripts control the version of Emscripten installed on
your computer:
```shell
export KEYMAN_USE_EMSDK=1
```
## Keyman for iOS Dependencies

314
docs/build/windows.md vendored
View file

@ -6,13 +6,13 @@ On Windows, you can build the following projects:
* [Keyman for Android](#keyman-for-android)
* [Keyman for Windows](#keyman-for-windows)
* [Keyman Developer](#keyman-developer) (together with Keyman for Windows)
* [Keyman Developer](#keyman-developer)
* [KeymanWeb](#keymanweb)
The following libraries can also be built:
* Keyman Core (Windows, wasm targets) (aka core)
* Common/Web
* Keyman Core (Windows, wasm targets)
* Common libraries
The following projects **cannot** be built on Windows:
@ -63,10 +63,6 @@ Dependencies:
* [Base](#base-dependencies)
* [Windows Platform](#windows-platform-dependencies)
**Note**: Keyman for Windows is currently built together with Keyman Developer.
We are working on splitting these projects. For now, you will need the Keyman
Developer dependencies as well.
Building:
* [Building Keyman for Windows](../../windows/src/README.md)
@ -75,7 +71,7 @@ Building:
Dependencies:
* [Base](#base-dependencies)
* [Web](#web-dependencies)
* [Windows Platform](#windows-platform-dependencies)
* [Windows Platform](#windows-platform-dependencies) (optional, for Windows-only components)
Building:
* [Building Keyman Developer](../../windows/src/README.md)
@ -94,42 +90,19 @@ Building:
**Dependencies**:
* [Base](#base-dependencies)
* [Web](#web-dependencies)
**Additional requirements**:
* Android SDK
* Android Studio
* Ant
* Gradle
* Maven
* Optional: OpenJDK 11 (https://learn.microsoft.com/en-us/java/openjdk/download)
```ps1
# Elevated PowerShell
choco install android-sdk android-studio ant gradle maven
# optionally install sdk images
sdkmanager "system-images;android-33;google_apis;armeabi-v7a"
sdkmanager --update
sdkmanager --licenses
```
* Run Android Studio once after installation to install additional components
such as emulator images and SDK updates.
**Required environment variables**:
* [`JAVA_HOME`](#java_home)
**Optional environment variables**:
* [`JAVA_HOME_11`](#java_home)
* [Android](#android-dependencies)
Building:
* [Building Keyman for Android](../../android/README.md)
## Prerequisites
---
## Dependencies and Prerequisites
Many dependencies are only required for specific projects.
We prefer [Chocolatey](https://chocolatey.org/install) at present for
installation of dependencies. Chocolatey should be run in an elevated
installation of most dependencies. Chocolatey should be run in an elevated
PowerShell.
### Base Dependencies
@ -150,28 +123,36 @@ PowerShell.
# for *much* faster download, hide progress bar (PowerShell/PowerShell#2138)
$ProgressPreference = 'SilentlyContinue'
choco install git jq python ninja pandoc meson
refreshenv
```
**Environment variables**:
* [`KEYMAN_ROOT`](#keyman_root)
* `PATH`: add your Python scripts folder to your path: it will normally be `%appdata%\Python\Python310\Scripts`.
If you pull the entire `keyman.git` repo to `c:\keyman`, then the paths by
default will work without changes. Otherwise, you will need to set an
environment variable `KEYMAN_ROOT` to the root path of the Keyman repo. For
example:
```bat
SET KEYMAN_ROOT=c:\Projects\keyman\keyman
SET PATH=%path%;%appdata%\Python\Python310\Scripts
SETX KEYMAN_ROOT "c:\Projects\keyman\keyman"
```
To check whether environment variables are set, run `SET <variable>` in command
prompt.
> [!NOTE]
> The `SETX` command will set persistent environment variables but they do not
> impact the current shell environment. Start a new shell to see the variables.
You can use Windows Settings to add these environment variables permanently:
1. In Windows Search, type "environment" and select "Edit System Environment
Variables"
2. Click `Environment Variables...`
3. You can add or edit variables in either User or System settings, as you
prefer.
> [!TIP]
>
> To check whether environment variables are set, run `SET <variable>` in command
> prompt.
>
> You can alternatively use Windows Settings to add these environment variables
> permanently:
>
> 1. In Windows Search, type "environment" and select "Edit System Environment
> Variables"
> 2. Click `Environment Variables...`
> 3. You can add or edit variables in either User or System settings, as you
> prefer.
### Web Dependencies
@ -181,32 +162,46 @@ You can use Windows Settings to add these environment variables permanently:
* KeymanWeb
**Requirements**:
* emscripten 3.1.46 or later
* node.js 18+
* [openjdk 11](https://learn.microsoft.com/en-us/java/openjdk/download#openjdk-11)+
* Emscripten
* node.js
```ps1
# Elevated PowerShell
#### Emscripten
# for *much* faster download, hide progress bar (PowerShell/PowerShell#2138)
$ProgressPreference = 'SilentlyContinue'
In bash, run the following commands:
choco install emscripten --version 3.1.46
```bash
cd /c/Projects/keyman
git clone https://github.com/emscripten-core/emsdk
cd emsdk
emsdk install 3.1.58
emsdk activate 3.1.58
```
Note: emscripten very unhelpfully overwrites JAVA_HOME, and adds its own
versions of Python, Node and Java to the PATH. For best results, go ahead
and remove those paths from your PATH variable before continuing.
> ![WARNING]
> Emscripten very unhelpfully overwrites `JAVA_HOME`, and adds its own
> versions of Python, Node and Java to the `PATH`. For best results, restart
> your shell after installing Emscripten so that you don't end up with the
> wrong versions.
There is no need to add emscripten to the path in order to build Keyman.
However, you should set the EMSCRIPTEN_BASE variable to the path where `emcc`
can be found, but always in the upstream\emscripten subdirectory where you
installed emsdk (most likely %LocalAppData%\emsdk\upstream\emscripten)
However, you should set the `EMSCRIPTEN_BASE` variable to the path where `emcc`
can be found, in the `upstream\emscripten` subdirectory of where you installed
emsdk.
**Environment variables**:
* `EMSCRIPTEN_BASE`: `<your-emsdk-path>\upstream\emscripten`
After installing emscripten, you'll need to install node.js and openjdk.
```bat
SETX EMSCRIPTEN_BASE "<your-emsdk-path>\upstream\emscripten"
```
**Optional environment variables**:
To let the Keyman build scripts control the version of Emscripten
installed on your computer:
```bat
SETX KEYMAN_USE_EMSDK 1
```
#### node.js
@ -214,30 +209,22 @@ Our recommended way to install node.js is to use
[nvm-windows](https://github.com/coreybutler/nvm-windows). This makes it
easy to switch between versions of node.js.
Alternatively, use Powershell + Chocolatey to install node.js:
```ps1
# Elevated PowerShell
# for *much* faster download, hide progress bar (PowerShell/PowerShell#2138)
$ProgressPreference = 'SilentlyContinue'
choco install nodejs
```bat
nvm install 20.16.0
nvm use 20.16.0
```
See [node.md](node.md) for more information.
**Optional environment variables**:
#### openjdk
To let the Keyman build scripts control the version of node.js installed
and active on your computer:
Use Powershell + Chocolatey to install OpenJDK:
```ps1
# Elevated PowerShell
# for *much* faster download, hide progress bar (PowerShell/PowerShell#2138)
$ProgressPreference = 'SilentlyContinue'
choco install openjdk
```
```bat
SETX KEYMAN_USE_NVM 1
````
See [node.md](node.md) for more information, including automatic selection
of appropriate node versions during builds.
### Windows Platform Dependencies
@ -260,8 +247,7 @@ choco install openjdk
Start Delphi IDE once after installation as it will create various environment
files and take you through required registration.
* Note: It is possible to build all components that do _not_ require Delphi by
adding the environment variable `NODELPHI=1` before starting the build.
* Note: It is possible to build all components that do _not_ require Delphi.
Currently many components are Delphi-based, but if you are working just in
Keyman Core, the compiler, or Keyman Engine's C++ components, you may be
able to get away without building them. In this situation, we recommend
@ -273,10 +259,12 @@ choco install openjdk
```ps1
choco install visualstudio2019community visualstudio2019-workload-nativedesktop visualstudio2019buildtools
```
* Verify required build tools are installed
* Run `Visual Studio Installer`
* Check the `Individual components` tab
* Verify `MSVC v142 - VS 2019 c++ x64/x86 build tools (Latest)` is installed. If not, install it.
* Verify `MSVC v142 - VS 2019 c++ x64/x86 build tools (Latest)` is installed.
If not, install it.
Recommended: configure Visual Studio to use two-space tab stops:
1. Open the options dialog: Tools > Options.
@ -284,55 +272,15 @@ choco install openjdk
3. Change 'Tab size' to 2 and 'Indent size' to 2.
4. Select 'Insert spaces'.
* Windows SDK (C++ Desktop Development)
https://developer.microsoft.com/en-us/windows/downloads/windows-sdk/
**Required environment variables**:
* `PATH`
* Add the C:\Projects\keyman\keyman\windows\lib folder in the Keyman
* Add the `C:\Projects\keyman\keyman\windows\lib` folder in the Keyman
repository to your `PATH` environment variable. This is required for
Keyman's design-time packages to load in Delphi.
* [`KEYMAN_CEF4DELPHI_ROOT`](#keyman_cef4delphi_root)
**Optional environment variables**:
* [`GIT_BASH_FOR_KEYMAN`](#git_bash_for_keyman)
* [`USERDEFINES`](#userdefines)
```bat
SET KEYMAN_CEF4DELPHI_ROOT=c:\Projects\keyman\CEF4Delphi_Binary
SET GIT_BASH_FOR_KEYMAN="C:\Program Files\Git\bin\bash.exe" --init-file "c:\Program Files\Git\etc\profile" -l
```
**Additional requirements for release builds**:
* [Certificates](#certificates)
* [7-Zip](http://www.7-zip.org/), used for archiving build files
* [HTML Help Workshop](http://web.archive.org/web/20160201063255/http://download.microsoft.com/download/0/A/9/0A939EF6-E31C-430F-A3DF-DFAE7960D564/htmlhelp.exe) note: Microsoft no longer offer this download...
* [WiX 3.11.1](https://github.com/wixtoolset/wix3/releases/tag/wix3111rtm)
* [CEF4Delphi_Binary](https://github.com/keymanapp/CEF4Delphi_Binary) repository
```ps1
# Elevated PowerShell
choco install 7zip html-help-workshop
choco install wixtoolset --version=3.11.1
git clone https://github.com/keymanapp/CEF4Delphi_Binary C:\Projects\keyman\CEF4Delphi_Binary
```
## Certificates
In order to make a release build, you need to sign all the executables. See
[windows/src/README.md#Certificates](../../windows/src/README.md#Certificates)
for details on how to create test code signing certificates or specify your own
certificates for the build.
## Notes on Environment Variables
### KEYMAN_ROOT
If you pull the entire `keyman.git` repo to `c:\keyman`, then the paths by
default will work without changes. Otherwise, you will need to set an
environment variable `KEYMAN_ROOT` to the root path of the Keyman repo. For
example:
```bat
SET KEYMAN_ROOT=c:\projects\keyman\keyman
```
### KEYMAN_CEF4DELPHI_ROOT
@ -349,41 +297,85 @@ release build, the common/windows/cef-checkout.sh script will checkout the corre
branch of the repository automatically and extract any compressed files found in
it.
### GIT_BASH_FOR_KEYMAN
This environment variable is optional: the build will run bash in a separate
window in order to build KeymanWeb if it isn't present, but you'll lose logging
and have the annoyance of a window popping up halfway through the build. To
resolve both of those issues, set the environment variable to:
The [`KEYMAN_CEF4DELPHI_ROOT`](#keyman_cef4delphi_root) variable is
used to specify the path to the CEF4Delphi binaries.
```bat
SET GIT_BASH_FOR_KEYMAN="C:\Program Files\Git\bin\bash.exe" --init-file "c:\Program Files\Git\etc\profile" -l
SETX KEYMAN_CEF4DELPHI_ROOT "c:\Projects\keyman\CEF4Delphi_Binary"
```
You should verify the install location of Git on your computer as it may vary.
**Additional requirements for release builds**:
* [Certificates](#certificates)
* [7-Zip](http://www.7-zip.org/), used for archiving build files
* [HTML Help Workshop](http://web.archive.org/web/20160201063255/http://download.microsoft.com/download/0/A/9/0A939EF6-E31C-430F-A3DF-DFAE7960D564/htmlhelp.exe) note: Microsoft no longer offer this download...
* [WiX 3.11.1](https://github.com/wixtoolset/wix3/releases/tag/wix3111rtm)
* [CEF4Delphi_Binary](https://github.com/keymanapp/CEF4Delphi_Binary) repository
### USERDEFINES
You can specify defines that will not be added to the git repository and will be
used in the build in the UserDefines.mak file in the root folder. This is used
mostly for code signing certificates. If not specified, a test certificate will
be used to sign executables when you build a release.
To include UserDefines.mak in the build, use the command line parameter
`-DUSERDEFINES`. You can also set an environment variable `USERDEFINES=1` to get
the same result.
### JAVA_HOME
This environment variable tells Gradle what version of Java to use for building Keyman for Android.
**Multiple versions of Java:** If you need to build Keyman for Android 16.0 or older versions, you can set `JAVA_HOME_11` to the OpenJDK 11 path and `JAVA_HOME` to the OpenJDK 8 path. This will build both versions correctly from command line. But note that you do need to update your `JAVA_HOME` env var to the associated version before opening Android Studio and loading any Android projects. `JAVA_HOME_11` is mostly used by CI.
```bat
SET JAVA_HOME="path to OpenJDK 8"
SET JAVA_HOME_11="path to OpenJDK 11"
```ps1
# Elevated PowerShell
choco install 7zip html-help-workshop
choco install wixtoolset --version=3.11.1
git clone https://github.com/keymanapp/CEF4Delphi_Binary C:\Projects\keyman\CEF4Delphi_Binary
```
### Android dependencies
**Projects**:
* Keyman for Android
**Requirements**:
* Android SDK
* Android Studio
* Ant
* Gradle
* Maven
* JDK 11 (Temurin11)
#### JDK 11
Use Powershell + Chocolatey to install JDK 11:
```ps1
# Elevated PowerShell
# for *much* faster download, hide progress bar (PowerShell/PowerShell#2138)
$ProgressPreference = 'SilentlyContinue'
choco install temurin11
```
**Multiple versions of Java:** If you need to build Keyman for Android 16.0 or
older versions, you can set `JAVA_HOME_11` to the JDK 11 path and
`JAVA_HOME` to the JDK 8 path. This will build both versions correctly
from command line. But note that you do need to update your `JAVA_HOME` env
var to the associated version before opening Android Studio and loading any
Android projects. `JAVA_HOME_11` is mostly used by CI.
#### Android Studio and friends
```ps1
# Elevated PowerShell
choco install androidstudio ant gradle maven android-sdk
```
Start a new shell to get the new paths and then update Android SDKs:
```ps1
# optionally install sdk images
sdkmanager --update
# sdkmanager "system-images;android-33;google_apis;armeabi-v7a"
sdkmanager --licenses
```
* Run Android Studio once after installation to install additional components
such as emulator images and SDK updates.
## Certificates
In order to make a release build, you need to sign all the executables. See
[windows/src/README.md#Certificates](../../windows/src/README.md#Certificates)
for details on how to create test code signing certificates or specify your own
certificates for the build.
## Optional Tools
* sentry-cli (optional)

View file

@ -456,7 +456,7 @@ For each key:
|---|------|---------|------------------------------------------|
|16+| 32 | vkey | int: vkey ID |
|20+| 32 | mod | int: modifier key flags |
|24+| 32 | key | int: index into `key` sibling subtable |
|24+| 32 | key | int: index into `keys` sibling subtable |
- `vkey`: If this is 0-255, it is the resolved standard/predefined vkey (K_A,
etc.). It is resolved because the `vkeyMap` from LDML has already been

View file

@ -49,7 +49,6 @@ https://help.keyman.com/developer/engine/android/latest-version/
| KEYMAN Variable | Value |
|-----------------------------------|--------------|
| KEYMAN_DEFAULT_VERSION_UBUNTU_CONTAINER | noble |
| KEYMAN_MAX_VERSION_EMSCRIPTEN | 3.1.58 |
| KEYMAN_MIN_TARGET_VERSION_ANDROID | 5 |
| KEYMAN_MIN_TARGET_VERSION_CHROME | 95.0 |
| KEYMAN_MIN_TARGET_VERSION_IOS | 12.2 |
@ -58,7 +57,7 @@ https://help.keyman.com/developer/engine/android/latest-version/
| KEYMAN_MIN_TARGET_VERSION_WINDOWS | 10 |
| KEYMAN_MIN_VERSION_ANDROID_SDK | 21 |
| KEYMAN_MIN_VERSION_CPP | 17 |
| KEYMAN_MIN_VERSION_EMSCRIPTEN | 3.1.44 |
| KEYMAN_MIN_VERSION_EMSCRIPTEN | 3.1.58 |
| KEYMAN_MIN_VERSION_MESON | 1.0.0 |
| KEYMAN_MIN_VERSION_NODE_MAJOR | 20 |
| KEYMAN_MIN_VERSION_NPM | 10.5.1 |

View file

@ -831,12 +831,22 @@ public class Manager: NSObject, UIGestureRecognizerDelegate {
shared.copyUserDefaults(to: nonShared, withKeys: keysToCopy, shouldOverwrite: true)
do {
try shared.copyFiles(to: nonShared)
FontManager.shared.registerCustomFonts()
} catch {
let message = ("Failed to copy from shared container: \(error)")
os_log("%{public}s", log:KeymanEngineLogger.settings, type: .error, message)
SentryManager.capture(error, message:message)
}
// This operation has a surprisingly high cost and isn't critical
// for getting the keyboard loaded and available to the OS.
//
// We certainly want it done _soon_... but we don't want it to cause
// the primary, synchronous initialization call from the OS to run so
// long that the keyboard fails to start due to its enforced time
// constraints.
DispatchQueue.main.async {
FontManager.shared.registerCustomFonts()
}
}
}

View file

@ -26,6 +26,17 @@ public class FontManager {
fonts[url] = RegisteredFont(name: name, isRegistered: false)
return name
}
private func cachedFont(at url: URL) -> RegisteredFont? {
if let font = fonts[url] {
return font
}
guard let name = readFontName(at: url) else {
return nil
}
fonts[url] = RegisteredFont(name: name, isRegistered: false)
return fonts[url]
}
/// Registers all new fonts found in the font path. Call this after you have preloaded all your font files
/// with `preloadFontFile(atPath:shouldOverwrite:)`
@ -33,8 +44,50 @@ public class FontManager {
guard let keyboardDirs = Storage.active.keyboardDirs else {
return
}
var fontSet: Set<URL> = []
for dir in keyboardDirs {
registerFonts(in: dir)
fontSet = fontSet.union(listFonts(in: dir))
}
registerListedFonts(fontSet)
}
/**
* Iterates across all listed fonts, registering those not yet registered.
* Checks for, and filters out, any fonts already registered on the system.
* Also initializes the registration cache per URL if needed.
*/
private func registerListedFonts(_ initialFontSet: Set<URL>) {
// If we are unable to read the font file's properties sufficiently,
// skip it. We also don't need to register anything already registered or
// that cannot be registered due to loading/parsing errors.
//
// Calls to `cachedFont` after the `.filter` below may be assumed to have
// non-nil return values.
var fontSet = initialFontSet.filter { !(cachedFont(at: $0)?.isRegistered ?? true) }
// The prior line filters out any entries where cachedFont(at: $0) would be nil.
// Batch-lookups all fonts lacking cache-confirmation of prior registration.
var fontNamesToRegister = missingFonts(from: Set(fontSet.map { cachedFont(at: $0)!.name }))
for fontUrl in fontSet {
let fontName = cachedFont(at: fontUrl)!.name
guard fontNamesToRegister.contains(fontName) else {
let message = "Did not register font at \(fontUrl) because font name \(fontName) is already registered"
os_log("%{public}s", log:KeymanEngineLogger.resources, type: .info, message)
continue
}
let didRegister = _registerFont(at: fontUrl)
fonts[fontUrl] = RegisteredFont(name: fontName, isRegistered: didRegister)
// We no longer need to register a font with this name, so drop it from
// the set to register.
if didRegister {
fontNamesToRegister.remove(fontName)
}
}
}
@ -43,6 +96,8 @@ public class FontManager {
guard let keyboardDirs = Storage.active.keyboardDirs else {
return
}
// This doesn't use the expensive looped lookup operation seen in missingFonts,
// so there's no need to batch similar operations here.
for dir in keyboardDirs {
unregisterFonts(in: dir)
}
@ -63,44 +118,29 @@ public class FontManager {
}
return name as String
}
private func _registerFont(at url: URL) -> Bool {
var errorRef: Unmanaged<CFError>?
let fontName = fontName(at: url)!
let didRegister = CTFontManagerRegisterFontsForURL(url as CFURL, .none, &errorRef)
let error = errorRef?.takeRetainedValue() // Releases errorRef
if !didRegister {
let message = "Failed to register font \(fontName) at \(url) reason: \(String(describing: error))"
os_log("%{public}s", log:KeymanEngineLogger.resources, type: .error, message)
} else {
let message = "Registered font \(fontName) at \(url)"
os_log("%{public}s", log:KeymanEngineLogger.resources, type: .info, message)
}
return didRegister
}
/// - Parameters:
/// - url: URL of the font to register
/// - Returns: Font is registered.
public func registerFont(at url: URL) -> Bool {
let fontName: String
if let font = fonts[url] {
if font.isRegistered {
return true
}
fontName = font.name
} else {
guard let name = readFontName(at: url) else {
return false
}
fontName = name
}
let didRegister: Bool
if !fontExists(fontName) {
var errorRef: Unmanaged<CFError>?
didRegister = CTFontManagerRegisterFontsForURL(url as CFURL, .none, &errorRef)
let error = errorRef?.takeRetainedValue() // Releases errorRef
if !didRegister {
let message = "Failed to register font \(fontName) at \(url) reason: \(String(describing: error))"
os_log("%{public}s", log:KeymanEngineLogger.resources, type: .error, message)
} else {
let message = "Registered font \(fontName) at \(url)"
os_log("%{public}s", log:KeymanEngineLogger.resources, type: .info, message)
}
} else {
didRegister = false
let message = "Did not register font at \(url) because font name \(fontName) is already registered"
os_log("%{public}s", log:KeymanEngineLogger.resources, type: .info, message)
}
let font = RegisteredFont(name: fontName, isRegistered: didRegister)
fonts[url] = font
return didRegister
registerListedFonts([url])
return fonts[url]?.isRegistered ?? false
}
/// - Parameters:
@ -133,32 +173,53 @@ public class FontManager {
return font.isRegistered
}
public func registerFonts(in directory: URL) {
private func listFonts(in directory: URL) -> [URL] {
guard let urls = try? FileManager.default.contentsOfDirectory(at: directory, includingPropertiesForKeys: nil) else {
let message = "Could not list contents of directory \(directory)"
os_log("%{public}s", log:KeymanEngineLogger.resources, type: .error, message)
return
}
for url in urls where url.lastPathComponent.hasFontExtension {
_ = registerFont(at: url)
return []
}
return urls.filter { $0.lastPathComponent.hasFontExtension }
}
public func registerFonts(in directory: URL) {
let fontsToRegister = listFonts(in: directory)
registerListedFonts(Set(fontsToRegister))
}
public func unregisterFonts(in directory: URL, fromSystemOnly: Bool = true) {
guard let urls = try? FileManager.default.contentsOfDirectory(at: directory, includingPropertiesForKeys: nil) else {
let message = "Could not list contents of directory \(directory)"
os_log("%{public}s", log:KeymanEngineLogger.resources, type: .error, message)
return
}
for url in urls where url.lastPathComponent.hasFontExtension {
let fontsToUnregister = listFonts(in: directory)
for url in fontsToUnregister {
_ = unregisterFont(at: url, fromSystemOnly: fromSystemOnly)
}
}
private func fontExists(_ fontName: String) -> Bool {
return UIFont.familyNames.contains { familyName in
UIFont.fontNames(forFamilyName: familyName).contains(fontName)
/**
* Queries the system for existing registrations for the specified fonts with a single batch run.
* Only fonts that could not be found will be returned within the result set.
*/
private func missingFonts(from fontNames: Set<String>) -> Set<String> {
var fontsToFind = fontNames
UIFont.familyNames.forEach { familyName in
if fontsToFind.count == 0 {
return
}
let familyFonts = UIFont.fontNames(forFamilyName: familyName)
for font in familyFonts {
if fontsToFind.contains(font) {
fontsToFind.remove(font)
}
if fontsToFind.count == 0 {
break
}
}
}
return fontsToFind
}
}

View file

@ -164,43 +164,43 @@ VKey_to_Iso = {
}
def bytecheck(value, check):
def _bytecheck(value, check):
if bytes([value & check[0]]) == check:
return True
else:
return False
def get_nkey(file, fileContent, offset):
def _get_nkey(file, fileContent, offset):
nkey = NKey()
data = struct.unpack_from("<B2H", fileContent, offset)
nkey.flags = data[0]
nkey.hasBitmap = bytecheck(data[0], kvkkBitmap)
nkey.hasUnicode = bytecheck(data[0], kvkkUnicode)
nkey.hasBitmap = _bytecheck(data[0], kvkkBitmap)
nkey.hasUnicode = _bytecheck(data[0], kvkkUnicode)
nkey.shiftflags = data[1]
if data[1] == 0:
nkey.Normal = True
nkey.Shift = bytecheck(data[1], KVKS_SHIFT)
nkey.Ctrl = bytecheck(data[1], KVKS_CTRL)
nkey.Alt = bytecheck(data[1], KVKS_ALT)
nkey.LCtrl = bytecheck(data[1], KVKS_LCTRL)
nkey.RCtrl = bytecheck(data[1], KVKS_RCTRL)
nkey.LAlt = bytecheck(data[1], KVKS_LALT)
nkey.RAlt = bytecheck(data[1], KVKS_RALT)
nkey.Shift = _bytecheck(data[1], KVKS_SHIFT)
nkey.Ctrl = _bytecheck(data[1], KVKS_CTRL)
nkey.Alt = _bytecheck(data[1], KVKS_ALT)
nkey.LCtrl = _bytecheck(data[1], KVKS_LCTRL)
nkey.RCtrl = _bytecheck(data[1], KVKS_RCTRL)
nkey.LAlt = _bytecheck(data[1], KVKS_LALT)
nkey.RAlt = _bytecheck(data[1], KVKS_RALT)
nkey.VKey = data[2]
nkey.text, newoffset = get_nstring(file, fileContent, offset + struct.calcsize("<B2H"))
nkey.bitmap, newoffset = get_nbitmap(file, fileContent, newoffset)
nkey.text, newoffset = _get_nstring(file, fileContent, offset + struct.calcsize("<B2H"))
nkey.bitmap, newoffset = _get_nbitmap(file, fileContent, newoffset)
return nkey, newoffset
def get_nfont(file, fileContent, offset):
def _get_nfont(file, fileContent, offset):
nfont = NFont()
nfont.name, curoffset = get_nstring(file, fileContent, offset)
nfont.name, curoffset = _get_nstring(file, fileContent, offset)
data = struct.unpack_from("<L4B", fileContent, curoffset)
nfont.resv = data[4]
nfont.blue = data[1]
@ -210,7 +210,7 @@ def get_nfont(file, fileContent, offset):
return nfont, curoffset + struct.calcsize("<L4B")
def get_nstring(file, fileContent, offset):
def _get_nstring(file, fileContent, offset):
stringlength = struct.unpack_from("<H", fileContent, offset)
file.seek(offset + 2)
if stringlength[0] > 256:
@ -224,7 +224,7 @@ def get_nstring(file, fileContent, offset):
return stringdata.decode('utf-16'), offset + 2 + (2 * stringlength[0])
def get_nbitmap(file, fileContent, offset):
def _get_nbitmap(file, fileContent, offset):
bitmap = None
bitmaplength = struct.unpack_from("<I", fileContent, offset)
file.seek(offset + struct.calcsize("<I"))
@ -283,31 +283,31 @@ def print_kvk(kvkData, allkeys=False):
print(" text:", key.text)
def plus_join(modifier, name):
def _plus_join(modifier, name):
if modifier:
modifier = modifier + "+"
modifier = modifier + name
return modifier
def get_modifer(key):
def _get_modifer(key):
modifier = ""
if key.Normal:
return "None"
if key.Shift:
modifier = plus_join(modifier, "shift")
modifier = _plus_join(modifier, "shift")
if key.Ctrl:
modifier = plus_join(modifier, "ctrl")
modifier = _plus_join(modifier, "ctrl")
if key.Alt:
modifier = plus_join(modifier, "alt")
modifier = _plus_join(modifier, "alt")
if key.LCtrl:
modifier = plus_join(modifier, "ctrlL")
modifier = _plus_join(modifier, "ctrlL")
if key.RCtrl:
modifier = plus_join(modifier, "ctrlR")
modifier = _plus_join(modifier, "ctrlR")
if key.LAlt:
modifier = plus_join(modifier, "altL")
modifier = _plus_join(modifier, "altL")
if key.RAlt:
modifier = plus_join(modifier, "altR")
modifier = _plus_join(modifier, "altR")
return modifier
@ -315,7 +315,7 @@ def convert_ldml(kvkData):
keymaps = {}
for key in kvkData.Keys:
modifier = get_modifer(key)
modifier = _get_modifer(key)
if modifier in keymaps:
keymaps[modifier] = keymaps[modifier] + (key,)
else:
@ -381,20 +381,20 @@ def parse_kvk_file(kvkfile):
kvkstart = struct.unpack_from("<4s4cc", fileContent, 0)
kvkData.version = (kvkstart[1], kvkstart[2], kvkstart[3], kvkstart[4])
kvkData.flags = kvkstart[5]
kvkData.key102 = bytecheck(kvkData.flags[0], kvkk102key)
kvkData.DisplayUnderlying = bytecheck(kvkData.flags[0], kvkkDisplayUnderlying)
kvkData.UseUnderlying = bytecheck(kvkData.flags[0], kvkkUseUnderlying)
kvkData.AltGr = bytecheck(kvkData.flags[0], kvkkAltGr)
kvkData.key102 = _bytecheck(kvkData.flags[0], kvkk102key)
kvkData.DisplayUnderlying = _bytecheck(kvkData.flags[0], kvkkDisplayUnderlying)
kvkData.UseUnderlying = _bytecheck(kvkData.flags[0], kvkkUseUnderlying)
kvkData.AltGr = _bytecheck(kvkData.flags[0], kvkkAltGr)
kvkData.AssociatedKeyboard, newoffset = get_nstring(file, fileContent, struct.calcsize("<4s4cc"))
kvkData.AnsiFont, newoffset = get_nfont(file, fileContent, newoffset)
kvkData.UnicodeFont, newoffset = get_nfont(file, fileContent, newoffset)
kvkData.AssociatedKeyboard, newoffset = _get_nstring(file, fileContent, struct.calcsize("<4s4cc"))
kvkData.AnsiFont, newoffset = _get_nfont(file, fileContent, newoffset)
kvkData.UnicodeFont, newoffset = _get_nfont(file, fileContent, newoffset)
numkeys = struct.unpack_from("I", fileContent, newoffset)
kvkData.KeyCount = numkeys[0]
newoffset = newoffset + struct.calcsize("I")
for num in range(numkeys[0]):
nkey, newoffset = get_nkey(file, fileContent, newoffset)
nkey, newoffset = _get_nkey(file, fileContent, newoffset)
nkey.number = num
kvkData.Keys.append(nkey)
except Exception as e:

View file

@ -36,7 +36,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>Keyman $(PRODUCT_VERSION) for macOS</string>
<string>$(PRODUCT_VERSION)</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleURLTypes</key>

View file

@ -16,6 +16,7 @@ NS_ASSUME_NONNULL_BEGIN
@property (readonly) NSString *clientApplicationId;
-(instancetype)initWithClient:(id) client applicationId:(NSString *)appId;
-(NSString *)description;
-(void)checkCompliance:(id) client;
-(void) checkComplianceAfterInsert:(id) client delete:(NSString *)textToDelete insert:(NSString *)textToInsert;
-(BOOL)isComplianceUncertain;

View file

@ -73,7 +73,7 @@ NSString *const kKMLegacyApps = @"KMLegacyApps";
-(NSString *)description
{
return [NSString stringWithFormat:@"complianceUncertain: %d, hasCompliantSelectionApi: %d, canReadText: %d, canReplaceText: %d, mustBackspaceUsingEvents: %d, clientAppId: %@, client: %@", self.complianceUncertain, self.hasCompliantSelectionApi, [self canReadText], [self canReplaceText], [self mustBackspaceUsingEvents], _clientApplicationId, _client];
return [NSString stringWithFormat:@"complianceUncertain: %d, hasCompliantSelectionApi: %d, canReadText: %d, canReplaceText: %d, mustBackspaceUsingEvents: %d, clientApplicationId: %@, client: %@", self.complianceUncertain, self.hasCompliantSelectionApi, [self canReadText], [self canReplaceText], [self mustBackspaceUsingEvents], _clientApplicationId, _client];
}
/** test to see if the API selectedRange functions properly for the text input client */

View file

@ -15,23 +15,27 @@
#import "TextApiCompliance.h"
KMInputMethodEventHandler *testEventHandler = nil;
id testClient = nil;
@interface InputMethodTests : XCTestCase
@end
@interface KMInputMethodEventHandler (Testing)
@property (nonatomic, retain) TextApiCompliance* apiCompliance;
@property (nonatomic, retain) NSString* clientApplicationId;
@property BOOL contextChanged;
- (instancetype)initWithClient:(NSString *)clientAppId client:(id) sender;
- (NSRange) calculateInsertRangeForDeletedText:(NSString*)textToDelete selectionRange:(NSRange) selection;
- (void)checkTextApiCompliance:(id)client;
@end
@implementation InputMethodTests
- (void)setUp {
id client = [[AppleCompliantTestClient alloc] init];
testClient = [[AppleCompliantTestClient alloc] init];
NSString *clientAppId = @"com.compliant.app";
testEventHandler = [[KMInputMethodEventHandler alloc]initWithClient:clientAppId client:client];
testEventHandler = [[KMInputMethodEventHandler alloc]initWithClient:clientAppId client:testClient];
}
- (void)tearDown {
@ -85,4 +89,43 @@ KMInputMethodEventHandler *testEventHandler = nil;
XCTAssertTrue(correctResult, @"insert or replacement range expected to be {1,2}");
}
/**
* test compliance check of a KMInputMethodEventHandler with a nil client application ID
* not sure if this can ever happen, but the lifecycle of the input method is not 100% clear,
* and we would like this scenario, if it can occur, to not result in a crash
*/
- (void)testCheckCompliance_withUnknownApplicationId_createsComplianceObject {
id client = [[AppleCompliantTestClient alloc] init];
KMInputMethodEventHandler *eventHandler = [[KMInputMethodEventHandler alloc]initWithClient:nil client:client];
[eventHandler checkTextApiCompliance:client];
XCTAssertNotNil(eventHandler.apiCompliance, @"apiCompliance object was not created");
}
- (void)testCheckCompliance_withNilComplianceObject_createsComplianceObject {
[testEventHandler checkTextApiCompliance:testClient];
XCTAssertNotNil(testEventHandler.apiCompliance, @"apiCompliance object was not created");
}
- (void)testCheckCompliance_withChangedClientApplicationId_createsNewComplianceObject {
// first call causes textApiCompliance object to be created
[testEventHandler checkTextApiCompliance:testClient];
TextApiCompliance *originalComplianceObject = testEventHandler.apiCompliance;
testEventHandler.clientApplicationId = @"com.different.app";
// second call causes new textApiCompliance object to be created due to stale application ID
[testEventHandler checkTextApiCompliance:testClient];
XCTAssertNotEqualObjects(originalComplianceObject, testEventHandler.apiCompliance, @"New TextApiCompliance object not created for new client application ID");
}
- (void)testCheckCompliance_withContextChanged_createsNewComplianceObject {
// first call causes textApiCompliance object to be created
[testEventHandler checkTextApiCompliance:testClient];
TextApiCompliance *originalComplianceObject = testEventHandler.apiCompliance;
testEventHandler.contextChanged = YES;
// second call causes new textApiCompliance object to be created due to setting contextChanged flag
[testEventHandler checkTextApiCompliance:testClient];
XCTAssertNotEqualObjects(originalComplianceObject, testEventHandler.apiCompliance, @"New TextApiCompliance object not created after contextChanged flag set");
}
@end

View file

@ -17,7 +17,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>Keyman Engine $(PRODUCT_VERSION) for macOS</string>
<string>$(PRODUCT_VERSION)</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>

View file

@ -7,57 +7,58 @@ fv,fv_uummarmiutun,Uummarmiutun,Arctic,fv_uummarmiutun_kmw-9.0.js,9.1.1,ikt-Latn
fv,fv_eastern_canadian_inuktitut,ᐃᓄᒃᑎᑐᑦ (Eastern Canadian Inuktitut),Arctic,fv_eastern_canadian_inuktitut_kmw-9.0.js,9.2.1,ike-Cans,Eastern Canadian Inuktitut (Unified Canadian Aboriginal Syllabics)
fv,fv_migmaq,Mi'gmawi'simg / Mi'kmawi'simk,Atlantic,fv_migmaq_kmw-9.0.js,9.1.2,mic-Latn,Mi'kmaq (Latin)
fv,fv_skicinuwatuwewakon,Skicinuwatuwewakon,Atlantic,fv_skicinuwatuwewakon_kmw-9.0.js,9.1.1,pqm-Latn,Malecite-Passamaquoddy (Latin)
fv,fv_uwikala,'Uwik̓ala,BC Coast,fv_uwikala_kmw-9.0.js,9.3,hei,Heiltsuk
fv,fv_uwikala,'Uwik̓ala,BC Coast,fv_uwikala_kmw-9.0.js,9.4,hei,Heiltsuk
fv,fv_dexwlesucid,Dəxʷləšucid,BC Coast,fv_dexwlesucid_kmw-9.0.js,9.2.1,lut-Latn,Lushootseed (Latin)
fv,fv_diitiidatx,Diidiitidq,BC Coast,fv_diitiidatx_kmw-9.0.js,9.2.1,nuk-Latn,Nuu-chah-nulth (Latin)
fv,fv_gitsenimx,Gitsenimx̱,BC Coast,fv_gitsenimx_kmw-9.0.js,10.1.1,git,Gitxsan (Latin)
fv,fv_gitsenimx,Gitsenimx̱,BC Coast,fv_gitsenimx_kmw-9.0.js,10.1.2,git,Gitxsan (Latin)
fv,fv_hailzaqvla,Haiɫzaqvla,BC Coast,fv_hailzaqvla_kmw-9.0.js,9.5.2,hei,Heiltsuk (Latin)
fv,fv_haisla,Haisla,BC Coast,fv_haisla.js,2.1,has-Latn,Haisla (Latin)
fv,fv_haisla,Haisla,BC Coast,fv_haisla.js,2.1.2,has-Latn,Haisla (Latin)
fv,fv_halqemeylem,Halq'eméylem,BC Coast,fv_halqemeylem_kmw-9.0.js,9.2,hur-Latn,Halkomelem (Latin)
fv,fv_henqeminem,Hǝn̓q̓ǝmin̓ǝm,BC Coast,fv_henqeminem_kmw-9.0.js,10.1,hur-Latn,Halkomelem (Latin)
fv,fv_klahoose,Homalco-Klahoose-Sliammon,BC Coast,fv_klahoose_kmw-9.0.js,10.1,coo,Comox
fv,fv_hulquminum,Hul’q’umi’num’,BC Coast,fv_hulquminum_kmw-9.0.js,9.1,hur-Latn,Halkomelem (Latin)
fv,fv_hulquminum_combine,Hul̓q̓umin̓um̓,BC Coast,fv_hulquminum_combine_kmw-9.0.js,2.0.1,hur-Latn,Halkomelem (Latin)
fv,fv_kwakwala_liqwala,Kʷak̓ʷala,BC Coast,fv_kwakwala_liqwala_kmw-9.0.js,9.2.5,kwk-Latn,Kwakiutl (Latin)
fv,fv_kwakwala,Kwak̕wala,BC Coast,fv_kwakwala_kmw-9.0.js,9.1.2,kwk-Latn,Kwakiutl (Latin)
fv,fv_nexwslayemucen,Nəxʷsƛ̓ay̓əmúcən,BC Coast,fv_nexwslayemucen_kmw-9.0.js,9.2.1,clm-Latn,Clallam (Latin)
fv,fv_nisgaa,Nisg̱a'a,BC Coast,fv_nisgaa_kmw-9.0.js,9.1.2,ncg-Latn,Nisga'a (Latin)
fv,fv_nuucaanul,Nuučaan̓uł,BC Coast,fv_nuucaanul_kmw-9.0.js,9.1.4,nuk-Latn,Nuu-chah-nulth (Latin)
fv,fv_nuxalk,Nuxalk,BC Coast,fv_nuxalk_kmw-9.0.js,10.0,blc-Latn,Bella Coola (Latin)
fv,fv_kwakwala_liqwala,Kʷak̓ʷala,BC Coast,fv_kwakwala_liqwala_kmw-9.0.js,9.3,kwk-Latn,Kwakiutl (Latin)
fv,fv_kwakwala,Kwak̕wala,BC Coast,fv_kwakwala_kmw-9.0.js,9.2,kwk-Latn,Kwakiutl (Latin)
fv,fv_lekwungen,Lək̓ʷəŋən,BC Coast,fv_lekwungen-9.0.js,1.0,str,Lekwungen
fv,fv_nexwslayemucen,Nəxʷsƛ̓ay̓əmúcən,BC Coast,fv_nexwslayemucen_kmw-9.0.js,10.0,clm-Latn,Clallam (Latin)
fv,fv_nisgaa,Nisg̱a'a,BC Coast,fv_nisgaa_kmw-9.0.js,9.2,ncg-Latn,Nisga'a (Latin)
fv,fv_nuucaanul,Nuučaan̓uł,BC Coast,fv_nuucaanul_kmw-9.0.js,9.2,nuk-Latn,Nuu-chah-nulth (Latin)
fv,fv_nuxalk,Nuxalk,BC Coast,fv_nuxalk_kmw-9.0.js,10.0.1,blc-Latn,Bella Coola (Latin)
fv,fv_sencoten,SENĆOŦEN,BC Coast,fv_sencoten_kmw-9.0.js,9.2.2,str,Straits Salish
fv,fv_sguuxs,Sgüüx̱s,BC Coast,fv_sguuxs.js,1.0.1,tsi,Tsimshian
fv,fv_sguuxs,Sgüüx̱s,BC Coast,fv_sguuxs.js,1.1,tsi,Tsimshian
fv,fv_shashishalhem,Shashishalhem,BC Coast,fv_shashishalhem_kmw-9.0.js,9.2,sec-Latn,Sechelt (Latin)
fv,fv_skwxwumesh_snichim,Sḵwx̱wúmesh sníchim,BC Coast,fv_skwxwumesh_snichim_kmw-9.0.js,9.3,squ-Latn,Squamish (Latin)
fv,fv_skwxwumesh_snichim,Sḵwx̱wúmesh sníchim,BC Coast,fv_skwxwumesh_snichim_kmw-9.0.js,10.0,squ-Latn,Squamish (Latin)
fv,fv_smalgyax,Sm'algya̱x,BC Coast,fv_smalgyax_kmw-9.0.js,9.2,tsi-Latn,Tsimshian (Latin)
fv,fv_xaislakala,X̄a'ʼislak̓ala,BC Coast,fv_xaislakala_kmw-9.0.js,9.1.1,has-Latn,Haisla (Latin)
fv,fv_hlgaagilda_xaayda_kil,X̱aayda-X̱aad Kil,BC Coast,fv_hlgaagilda_xaayda_kil_kmw-9.0.js,9.3,hax,Southern Haida
fv,fv_dakelh,Dakelh,BC Interior,fv_dakelh_kmw-9.0.js,9.2,caf-Latn,Southern Carrier (Latin)
fv,fv_xaislakala,X̄a'ʼislak̓ala,BC Coast,fv_xaislakala_kmw-9.0.js,10.0,has-Latn,Haisla (Latin)
fv,fv_hlgaagilda_xaayda_kil,X̱aayda-X̱aad Kil,BC Coast,fv_hlgaagilda_xaayda_kil_kmw-9.0.js,9.4,hax,Southern Haida
fv,fv_dakelh,Dakelh,BC Interior,fv_dakelh_kmw-9.0.js,9.2.1,caf-Latn,Southern Carrier (Latin)
fv,fv_ktunaxa,Ktunaxa,BC Interior,fv_ktunaxa_kmw-9.0.js,10.0,kut-Latn,Kutenai (Latin)
fv,fv_kwadacha_tsekene,Kwadacha Tsek’ene,BC Interior,fv_kwadacha_tsekene_kmw-9.0.js,1.0,sek-Latn,Sekani
fv,fv_kwadacha_tsekene,Kwadacha Tsek’ene,BC Interior,fv_kwadacha_tsekene_kmw-9.0.js,1.1,sek-Latn,Sekani
fv,fv_natwits,Nedut’en-Witsuwit'en,BC Interior,fv_natwits_kmw-9.0.js,9.1.3,caf-Latn,Southern Carrier (Latin)
fv,fv_nlekepmxcin,Nłeʔkepmxcin,BC Interior,fv_nlekepmxcin_kmw-9.0.js,9.4,thp-Latn,Thompson (Latin)
fv,fv_nlha7kapmxtsin,Nlha7kapmxtsin,BC Interior,fv_nlha7kapmxtsin_kmw-9.0.js,10.0,thp-Latn,Thompson (Latin)
fv,fv_nlakapamuxcheen,Nlakapamuxcheen,BC Interior,fv_nlakapamuxcheen_kmw-9.0.js,1.0.2,thp,Thompson
fv,fv_nsilxcen,Nsilxcən,BC Interior,fv_nsilxcen_kmw-9.0.js,9.3,oka,Okanagan
fv,fv_nlekepmxcin,Nłeʔkepmxcin,BC Interior,fv_nlekepmxcin_kmw-9.0.js,9.5.1,thp-Latn,Thompson (Latin)
fv,fv_nlha7kapmxtsin,Nlha7kapmxtsin,BC Interior,fv_nlha7kapmxtsin_kmw-9.0.js,10.1.1,thp-Latn,Thompson (Latin)
fv,fv_nlakapamuxcheen,Nlakapamuxcheen,BC Interior,fv_nlakapamuxcheen_kmw-9.0.js,1.1,thp,Thompson
fv,fv_nsilxcen,Nsilxcən,BC Interior,fv_nsilxcen_kmw-9.0.js,10.0,oka,Okanagan
fv,fv_secwepemctsin,Secwepemctsín,BC Interior,fv_secwepemctsin_kmw-9.0.js,9.2,shs-Latn,Shuswap (Latin)
fv,fv_stlatlimxec,Sƛ̓aƛ̓imxəc,BC Interior,fv_stlatlimxec_kmw-9.0.js,9.3,lil-Latn,Lillooet (Latin)
fv,fv_statimcets,St̓át̓imcets,BC Interior,fv_statimcets_kmw-9.0.js,9.1.4,lil-Latn,Lillooet (Latin)
fv,fv_taltan,Tāłtān,BC Interior,fv_taltan_kmw-9.0.js,9.1.5,tht-Latn,Tahltan (Latin)
fv,fv_tsekehne,Tsek'ehne,BC Interior,fv_tsekehne_kmw-9.0.js,9.1.2,sek-Latn,Sekani (Latin)
fv,fv_tsilhqotin,Tŝilhqot'in,BC Interior,fv_tsilhqotin_kmw-9.0.js,9.1.3,clc-Latn,Chilcotin (Latin)
fv,fv_southern_carrier,ᑐᑊᘁᗕᑋᗸ (Southern Carrier),BC Interior,fv_southern_carrier_kmw-9.0.js,10.0.1,caf-Cans,Southern Carrier (Unified Canadian Aboriginal Syllabics)
fv,fv_statimcets,St̓át̓imcets,BC Interior,fv_statimcets_kmw-9.0.js,9.2,lil-Latn,Lillooet (Latin)
fv,fv_taltan,Tāłtān,BC Interior,fv_taltan_kmw-9.0.js,9.2,tht-Latn,Tahltan (Latin)
fv,fv_tsekehne,Tsek'ehne,BC Interior,fv_tsekehne_kmw-9.0.js,9.2,sek-Latn,Sekani (Latin)
fv,fv_tsilhqotin,Tŝilhqot'in,BC Interior,fv_tsilhqotin_kmw-9.0.js,9.2,clc-Latn,Chilcotin (Latin)
fv,fv_southern_carrier,ᑐᑊᘁᗕᑋᗸ (Southern Carrier),BC Interior,fv_southern_carrier_kmw-9.0.js,10.1,caf-Cans,Southern Carrier (Unified Canadian Aboriginal Syllabics)
fv,fv_anicinapemi8in,Anicinapemi8in/Anishinàbemiwin,Eastern Subarctic,fv_anicinapemi8in_kmw-9.0.js,9.1.1,alq-Latn,Algonquin (Latin)
fv,fv_atikamekw,Atikamekw,Eastern Subarctic,fv_atikamekw_kmw-9.0.js,9.1.1,atj-Latn,Atikamekw (Latin)
fv,fv_ilnu_innu_aimun,Ilnu-Innu Aimun,Eastern Subarctic,fv_ilnu_innu_aimun_kmw-9.0.js,9.1.1,moe-Latn,Montagnais (Latin)
fv,fv_swampy_cree,ᐃᓂᓂᒧᐎᐣ (Swampy Cree),Eastern Subarctic,fv_swampy_cree_kmw-9.0.js,9.2.1,csw-Cans,Swampy Cree (Unified Canadian Aboriginal Syllabics)
fv,fv_moose_cree,ᐃᓕᓖᒧᐎᓐ (Moose Cree),Eastern Subarctic,fv_moose_cree_kmw-9.0.js,9.2.1,crm-Cans,Moose Cree (Unified Canadian Aboriginal Syllabics)
fv,fv_northern_east_cree,ᐄᔨᔫ-ᐄᓅ ᐊᔨᒨᓐ (Northern East Cree),Eastern Subarctic,fv_northern_east_cree_kmw-9.0.js,9.2.1,crl-Cans,Northern East Cree (Unified Canadian Aboriginal Syllabics)
fv,fv_severn_ojibwa,ᐊᓂᔑᓂᓂᒧᐎᐣ (Severn Ojibwa),Eastern Subarctic,fv_severn_ojibwa_kmw-9.0.js,10.0.1,ojs-Cans,Severn Ojibwa (Unified Canadian Aboriginal Syllabics)
fv,fv_severn_ojibwa_rdot,ᐊᓂᔑᓂᓂᒧᐏᐣ (Severn Ojibwa right w-dot),Eastern Subarctic,fv_severn_ojibwa_kmw-9.0.js,1.0.1,ojs,Oji-Cree
fv,fv_ojibwa,ᐊᓂᔑᓇᐯᒧᐎᓐ (a-finals),Eastern Subarctic,fv_ojibwa_kmw-9.0.js,10.0.1,ojb-Cans,Northwestern Ojibwa (Unified Canadian Aboriginal Syllabics)
fv,fv_ojibwa_rdot,ᐁᓂᔑᓇᐯᒧᐏᓐ (a-finals right w-dot),Eastern Subarctic,fv_ojibwa_rdot_kmw-9.0.js,1.0.1,oj,Ojibwa
fv,fv_ojibwa_ifinal,ᐊᓂᔑᓇᐯᒧᐎᣙ (i-finals),Eastern Subarctic,fv_ojibwa_ifinal_kmw-9.0.js,1.0,oj,Ojibwa
fv,fv_ojibwa_ifinal_rdot,ᐊᓂᔑᓇᐯᒧᐏᣙ (i-finals right w-dot),Eastern Subarctic,fv_ojibwa_ifinal_rdot_kmw-9.0.js,1.0,oj,Ojibwa
fv,fv_severn_ojibwa,ᐊᓂᔑᓂᓂᒧᐎᐣ (Severn Ojibwa),Eastern Subarctic,fv_severn_ojibwa_kmw-9.0.js,10.1,ojs-Cans,Severn Ojibwa (Unified Canadian Aboriginal Syllabics)
fv,fv_severn_ojibwa_rdot,ᐊᓂᔑᓂᓂᒧᐏᐣ (Severn Ojibwa right w-dot),Eastern Subarctic,fv_severn_ojibwa_kmw-9.0.js,1.1,ojs,Oji-Cree
fv,fv_ojibwa,ᐊᓂᔑᓇᐯᒧᐎᓐ (a-finals),Eastern Subarctic,fv_ojibwa_kmw-9.0.js,10.1,ojb-Cans,Northwestern Ojibwa (Unified Canadian Aboriginal Syllabics)
fv,fv_ojibwa_rdot,ᐁᓂᔑᓇᐯᒧᐏᓐ (a-finals right w-dot),Eastern Subarctic,fv_ojibwa_rdot_kmw-9.0.js,1.0.2,oj,Ojibwa
fv,fv_ojibwa_ifinal,ᐊᓂᔑᓇᐯᒧᐎᣙ (i-finals),Eastern Subarctic,fv_ojibwa_ifinal_kmw-9.0.js,1.0.1,oj,Ojibwa
fv,fv_ojibwa_ifinal_rdot,ᐊᓂᔑᓇᐯᒧᐏᣙ (i-finals right w-dot),Eastern Subarctic,fv_ojibwa_ifinal_rdot_kmw-9.0.js,1.0.1,oj,Ojibwa
fv,fv_naskapi,ᓇᔅᑲᐱ (Naskapi),Eastern Subarctic,fv_naskapi_kmw-9.0.js,9.3.1,nsk-Cans,Naskapi (Unified Canadian Aboriginal Syllabics)
sil,sil_euro_latin,English,European,european2-1.6.js,3.0.2,en,English
basic,basic_kbdcan,Français,European,canadian_french-1.0.js,1.1.1,fr-CA,French (Canada)
@ -82,23 +83,23 @@ fv,fv_isga_iabi,Isga Iʔabi,Prairies,fv_isga_iabi_kmw-9.0.js,9.1.1,sto-Latn,Ston
fv,fv_lakota,Lak̇ot̄a,Prairies,fv_lakota-9.0.js,9.1.1,lkt-Latn,Lakota (Latin)
fv,fv_nakoda,Nakoda,Prairies,fv_nakoda_kmw-9.0.js,9.1.1,asb-Latn,Assiniboine (Latin)
fv,fv_tsuutina,Tsúùt'ínà,Prairies,fv_tsuutina_kmw-9.0.js,9.1.1,srs-Latn,Sarsi (Latin)
fv,fv_plains_cree,ᓀᐦᐃᔭᐍᐏᐣ (Plains Cree),Prairies,fv_plains_cree_kmw-9.0.js,11.0.2,crk-Cans,ᓀᐦᐃᔭᐍᐏᐣ (Cree syllabics)
fv,fv_plains_cree,ᓀᐦᐃᔭᐍᐏᐣ (Plains Cree),Prairies,fv_plains_cree_kmw-9.0.js,11.1,crk-Cans,ᓀᐦᐃᔭᐍᐏᐣ (Cree syllabics)
fv,fv_dine_bizaad,Diné Bizaad,South West,fv_dine_bizaad_kmw-9.0.js,9.1.1,nv-Latn,Navajo (Latin)
fv,fv_dane_zaa_zaage,Dane-Z̲aa Z̲áágéʔ,Western Subarctic,fv_dane_zaa_zaage_kmw-9.0.js,9.4,bea,Beaver
fv,fv_dene_dzage,Dene Dzage,Western Subarctic,fv_dene_dzage_kmw-9.0.js,11.0.1,kkz-Latn,Kaska (Latin)
fv,fv_dene_zhatie,Dene Zhatié,Western Subarctic,fv_dene_zhatie_kmw-9.0.js,10.2,den,Dene Zhatıé
fv,fv_dene_zhatie,Dene Zhatié,Western Subarctic,fv_dene_zhatie_kmw-9.0.js,10.2.1,den,Dene Zhatıé
fv,fv_denesuline_epsilon,Dënesųłıné,Western Subarctic,fv_denesuline_epsilon_kmw-9.0.js,10.0.1,chp,Chipewyan
fv,fv_denesuline,Dɛnɛsųłiné,Western Subarctic,fv_denesuline_kmw-9.0.js,10.0.1,chp,Chipewyan (Latin)
fv,fv_gwichin,Gwich'in,Western Subarctic,fv_gwichin_kmw-9.0.js,9.2.1,gwi-Latn,Gwichʼin (Latin)
fv,fv_han,Hän,Western Subarctic,fv_han_kmw-9.0.js,9.2,haa-Latn,Han (Latin)
fv,fv_kashogotine_yati,K'áshogot'ı̨nę́ Yatı̨́,Western Subarctic,fv_kashogotine_yati_kmw-9.0.js,10.0,scs,North Slavey
fv,fv_tlingit,Łingít,Western Subarctic,fv_tlingit_kmw-9.0.js,10.0.1,tli,Tlingit (Latin)
fv,fv_kashogotine_yati,K'áshogot'ı̨nę́ Yatı̨́,Western Subarctic,fv_kashogotine_yati_kmw-9.0.js,10.1,scs,North Slavey
fv,fv_tlingit,Łingít,Western Subarctic,fv_tlingit_kmw-9.0.js,10.1,tli,Tlingit (Latin)
fv,fv_neeaandeg,Nee'aanděg',Western Subarctic,fv_neeaandeg_kmw-9.0.js,9.1.1,tcb-Latn,Tanacross (Latin)
fv,fv_neeaaneegn,Nee'aaneegn',Western Subarctic,fv_neeaaneegn_kmw-9.0.js,9.1,tau-Latn,Upper Tanana (Latin)
fv,fv_northern_tutchone,Northern Tutchone,Western Subarctic,fv_northern_tutchone_kmw-9.0.js,9.2,ttm-Latn,Northern Tutchone (Latin)
fv,fv_sahugotine_yati,Sahtúgot'ı̨nę́ Yatı̨́,Western Subarctic,fv_sahugotine_yati_kmw-9.0.js,9.1.1,scs-Latn,North Slavey (Latin)
fv,fv_shihgotine_yati,Shıhgot'ı̨nę́ Yatı̨́,Western Subarctic,fv_shihgotine_yati_kmw-9.0.js,9.1.1,scs-Latn,North Slavey (Latin)
fv,fv_southern_tutchone,Southern Tutchone,Western Subarctic,fv_southern_tutchone_kmw-9.0.js,9.2.2,tce-Latn,Southern Tutchone (Latin)
fv,fv_southern_tutchone,Southern Tutchone,Western Subarctic,fv_southern_tutchone_kmw-9.0.js,9.3,tce-Latn,Southern Tutchone (Latin)
fv,fv_tagizi_dene,Tāgizi Dene,Western Subarctic,fv_tagizi_dene_kmw-9.0.js,9.3,tgx-Latn,Tagish (Latin)
fv,fv_tlicho_yatii,Tłı̨chǫ Yatıı̀,Western Subarctic,fv_tlicho_yatii_kmw-9.0.js,9.1.1,dgr-Latn,Dogrib (Latin)
fv,fv_dene_mb,ᑌᓀ ᔭᕠᐁ (Dene MB),Western Subarctic,fv_dene_mb_kmw-9.0.js,9.2.1,chp-Cans,Chipewyan (Unified Canadian Aboriginal Syllabics)

1 Shortname ID Name Region 9.0 Web Keyboard Version Language ID Language Name
7 fv fv_eastern_canadian_inuktitut ᐃᓄᒃᑎᑐᑦ (Eastern Canadian Inuktitut) Arctic fv_eastern_canadian_inuktitut_kmw-9.0.js 9.2.1 ike-Cans Eastern Canadian Inuktitut (Unified Canadian Aboriginal Syllabics)
8 fv fv_migmaq Mi'gmawi'simg / Mi'kmawi'simk Atlantic fv_migmaq_kmw-9.0.js 9.1.2 mic-Latn Mi'kmaq (Latin)
9 fv fv_skicinuwatuwewakon Skicinuwatuwewakon Atlantic fv_skicinuwatuwewakon_kmw-9.0.js 9.1.1 pqm-Latn Malecite-Passamaquoddy (Latin)
10 fv fv_uwikala 'Uwik̓ala BC Coast fv_uwikala_kmw-9.0.js 9.3 9.4 hei Heiltsuk
11 fv fv_dexwlesucid Dəxʷləšucid BC Coast fv_dexwlesucid_kmw-9.0.js 9.2.1 lut-Latn Lushootseed (Latin)
12 fv fv_diitiidatx Diidiitidq BC Coast fv_diitiidatx_kmw-9.0.js 9.2.1 nuk-Latn Nuu-chah-nulth (Latin)
13 fv fv_gitsenimx Gitsenimx̱ BC Coast fv_gitsenimx_kmw-9.0.js 10.1.1 10.1.2 git Gitxsan (Latin)
14 fv fv_hailzaqvla Haiɫzaqvla BC Coast fv_hailzaqvla_kmw-9.0.js 9.5.2 hei Heiltsuk (Latin)
15 fv fv_haisla Haisla BC Coast fv_haisla.js 2.1 2.1.2 has-Latn Haisla (Latin)
16 fv fv_halqemeylem Halq'eméylem BC Coast fv_halqemeylem_kmw-9.0.js 9.2 hur-Latn Halkomelem (Latin)
17 fv fv_henqeminem Hǝn̓q̓ǝmin̓ǝm BC Coast fv_henqeminem_kmw-9.0.js 10.1 hur-Latn Halkomelem (Latin)
18 fv fv_klahoose Homalco-Klahoose-Sliammon BC Coast fv_klahoose_kmw-9.0.js 10.1 coo Comox
19 fv fv_hulquminum Hul’q’umi’num’ BC Coast fv_hulquminum_kmw-9.0.js 9.1 hur-Latn Halkomelem (Latin)
20 fv fv_hulquminum_combine Hul̓q̓umin̓um̓ BC Coast fv_hulquminum_combine_kmw-9.0.js 2.0.1 hur-Latn Halkomelem (Latin)
21 fv fv_kwakwala_liqwala Kʷak̓ʷala BC Coast fv_kwakwala_liqwala_kmw-9.0.js 9.2.5 9.3 kwk-Latn Kwakiutl (Latin)
22 fv fv_kwakwala Kwak̕wala BC Coast fv_kwakwala_kmw-9.0.js 9.1.2 9.2 kwk-Latn Kwakiutl (Latin)
23 fv fv_nexwslayemucen fv_lekwungen Nəxʷsƛ̓ay̓əmúcən Lək̓ʷəŋən BC Coast fv_nexwslayemucen_kmw-9.0.js fv_lekwungen-9.0.js 9.2.1 1.0 clm-Latn str Clallam (Latin) Lekwungen
24 fv fv_nisgaa fv_nexwslayemucen Nisg̱a'a Nəxʷsƛ̓ay̓əmúcən BC Coast fv_nisgaa_kmw-9.0.js fv_nexwslayemucen_kmw-9.0.js 9.1.2 10.0 ncg-Latn clm-Latn Nisga'a (Latin) Clallam (Latin)
25 fv fv_nuucaanul fv_nisgaa Nuučaan̓uł Nisg̱a'a BC Coast fv_nuucaanul_kmw-9.0.js fv_nisgaa_kmw-9.0.js 9.1.4 9.2 nuk-Latn ncg-Latn Nuu-chah-nulth (Latin) Nisga'a (Latin)
26 fv fv_nuxalk fv_nuucaanul Nuxalk Nuučaan̓uł BC Coast fv_nuxalk_kmw-9.0.js fv_nuucaanul_kmw-9.0.js 10.0 9.2 blc-Latn nuk-Latn Bella Coola (Latin) Nuu-chah-nulth (Latin)
27 fv fv_nuxalk Nuxalk BC Coast fv_nuxalk_kmw-9.0.js 10.0.1 blc-Latn Bella Coola (Latin)
28 fv fv_sencoten SENĆOŦEN BC Coast fv_sencoten_kmw-9.0.js 9.2.2 str Straits Salish
29 fv fv_sguuxs Sgüüx̱s BC Coast fv_sguuxs.js 1.0.1 1.1 tsi Tsimshian
30 fv fv_shashishalhem Shashishalhem BC Coast fv_shashishalhem_kmw-9.0.js 9.2 sec-Latn Sechelt (Latin)
31 fv fv_skwxwumesh_snichim Sḵwx̱wúmesh sníchim BC Coast fv_skwxwumesh_snichim_kmw-9.0.js 9.3 10.0 squ-Latn Squamish (Latin)
32 fv fv_smalgyax Sm'algya̱x BC Coast fv_smalgyax_kmw-9.0.js 9.2 tsi-Latn Tsimshian (Latin)
33 fv fv_xaislakala X̄a'ʼislak̓ala BC Coast fv_xaislakala_kmw-9.0.js 9.1.1 10.0 has-Latn Haisla (Latin)
34 fv fv_hlgaagilda_xaayda_kil X̱aayda-X̱aad Kil BC Coast fv_hlgaagilda_xaayda_kil_kmw-9.0.js 9.3 9.4 hax Southern Haida
35 fv fv_dakelh Dakelh BC Interior fv_dakelh_kmw-9.0.js 9.2 9.2.1 caf-Latn Southern Carrier (Latin)
36 fv fv_ktunaxa Ktunaxa BC Interior fv_ktunaxa_kmw-9.0.js 10.0 kut-Latn Kutenai (Latin)
37 fv fv_kwadacha_tsekene Kwadacha Tsek’ene BC Interior fv_kwadacha_tsekene_kmw-9.0.js 1.0 1.1 sek-Latn Sekani
38 fv fv_natwits Nedut’en-Witsuwit'en BC Interior fv_natwits_kmw-9.0.js 9.1.3 caf-Latn Southern Carrier (Latin)
39 fv fv_nlekepmxcin Nłeʔkepmxcin BC Interior fv_nlekepmxcin_kmw-9.0.js 9.4 9.5.1 thp-Latn Thompson (Latin)
40 fv fv_nlha7kapmxtsin Nlha7kapmxtsin BC Interior fv_nlha7kapmxtsin_kmw-9.0.js 10.0 10.1.1 thp-Latn Thompson (Latin)
41 fv fv_nlakapamuxcheen Nlakapamuxcheen BC Interior fv_nlakapamuxcheen_kmw-9.0.js 1.0.2 1.1 thp Thompson
42 fv fv_nsilxcen Nsilxcən BC Interior fv_nsilxcen_kmw-9.0.js 9.3 10.0 oka Okanagan
43 fv fv_secwepemctsin Secwepemctsín BC Interior fv_secwepemctsin_kmw-9.0.js 9.2 shs-Latn Shuswap (Latin)
44 fv fv_stlatlimxec Sƛ̓aƛ̓imxəc BC Interior fv_stlatlimxec_kmw-9.0.js 9.3 lil-Latn Lillooet (Latin)
45 fv fv_statimcets St̓át̓imcets BC Interior fv_statimcets_kmw-9.0.js 9.1.4 9.2 lil-Latn Lillooet (Latin)
46 fv fv_taltan Tāłtān BC Interior fv_taltan_kmw-9.0.js 9.1.5 9.2 tht-Latn Tahltan (Latin)
47 fv fv_tsekehne Tsek'ehne BC Interior fv_tsekehne_kmw-9.0.js 9.1.2 9.2 sek-Latn Sekani (Latin)
48 fv fv_tsilhqotin Tŝilhqot'in BC Interior fv_tsilhqotin_kmw-9.0.js 9.1.3 9.2 clc-Latn Chilcotin (Latin)
49 fv fv_southern_carrier ᑐᑊᘁᗕᑋᗸ (Southern Carrier) BC Interior fv_southern_carrier_kmw-9.0.js 10.0.1 10.1 caf-Cans Southern Carrier (Unified Canadian Aboriginal Syllabics)
50 fv fv_anicinapemi8in Anicinapemi8in/Anishinàbemiwin Eastern Subarctic fv_anicinapemi8in_kmw-9.0.js 9.1.1 alq-Latn Algonquin (Latin)
51 fv fv_atikamekw Atikamekw Eastern Subarctic fv_atikamekw_kmw-9.0.js 9.1.1 atj-Latn Atikamekw (Latin)
52 fv fv_ilnu_innu_aimun Ilnu-Innu Aimun Eastern Subarctic fv_ilnu_innu_aimun_kmw-9.0.js 9.1.1 moe-Latn Montagnais (Latin)
53 fv fv_swampy_cree ᐃᓂᓂᒧᐎᐣ (Swampy Cree) Eastern Subarctic fv_swampy_cree_kmw-9.0.js 9.2.1 csw-Cans Swampy Cree (Unified Canadian Aboriginal Syllabics)
54 fv fv_moose_cree ᐃᓕᓖᒧᐎᓐ (Moose Cree) Eastern Subarctic fv_moose_cree_kmw-9.0.js 9.2.1 crm-Cans Moose Cree (Unified Canadian Aboriginal Syllabics)
55 fv fv_northern_east_cree ᐄᔨᔫ-ᐄᓅ ᐊᔨᒨᓐ (Northern East Cree) Eastern Subarctic fv_northern_east_cree_kmw-9.0.js 9.2.1 crl-Cans Northern East Cree (Unified Canadian Aboriginal Syllabics)
56 fv fv_severn_ojibwa ᐊᓂᔑᓂᓂᒧᐎᐣ (Severn Ojibwa) Eastern Subarctic fv_severn_ojibwa_kmw-9.0.js 10.0.1 10.1 ojs-Cans Severn Ojibwa (Unified Canadian Aboriginal Syllabics)
57 fv fv_severn_ojibwa_rdot ᐊᓂᔑᓂᓂᒧᐏᐣ (Severn Ojibwa right w-dot) Eastern Subarctic fv_severn_ojibwa_kmw-9.0.js 1.0.1 1.1 ojs Oji-Cree
58 fv fv_ojibwa ᐊᓂᔑᓇᐯᒧᐎᓐ (a-finals) Eastern Subarctic fv_ojibwa_kmw-9.0.js 10.0.1 10.1 ojb-Cans Northwestern Ojibwa (Unified Canadian Aboriginal Syllabics)
59 fv fv_ojibwa_rdot ᐁᓂᔑᓇᐯᒧᐏᓐ (a-finals right w-dot) Eastern Subarctic fv_ojibwa_rdot_kmw-9.0.js 1.0.1 1.0.2 oj Ojibwa
60 fv fv_ojibwa_ifinal ᐊᓂᔑᓇᐯᒧᐎᣙ (i-finals) Eastern Subarctic fv_ojibwa_ifinal_kmw-9.0.js 1.0 1.0.1 oj Ojibwa
61 fv fv_ojibwa_ifinal_rdot ᐊᓂᔑᓇᐯᒧᐏᣙ (i-finals right w-dot) Eastern Subarctic fv_ojibwa_ifinal_rdot_kmw-9.0.js 1.0 1.0.1 oj Ojibwa
62 fv fv_naskapi ᓇᔅᑲᐱ (Naskapi) Eastern Subarctic fv_naskapi_kmw-9.0.js 9.3.1 nsk-Cans Naskapi (Unified Canadian Aboriginal Syllabics)
63 sil sil_euro_latin English European european2-1.6.js 3.0.2 en English
64 basic basic_kbdcan Français European canadian_french-1.0.js 1.1.1 fr-CA French (Canada)
83 fv fv_lakota Lak̇ot̄a Prairies fv_lakota-9.0.js 9.1.1 lkt-Latn Lakota (Latin)
84 fv fv_nakoda Nakoda Prairies fv_nakoda_kmw-9.0.js 9.1.1 asb-Latn Assiniboine (Latin)
85 fv fv_tsuutina Tsúùt'ínà Prairies fv_tsuutina_kmw-9.0.js 9.1.1 srs-Latn Sarsi (Latin)
86 fv fv_plains_cree ᓀᐦᐃᔭᐍᐏᐣ (Plains Cree) Prairies fv_plains_cree_kmw-9.0.js 11.0.2 11.1 crk-Cans ᓀᐦᐃᔭᐍᐏᐣ (Cree syllabics)
87 fv fv_dine_bizaad Diné Bizaad South West fv_dine_bizaad_kmw-9.0.js 9.1.1 nv-Latn Navajo (Latin)
88 fv fv_dane_zaa_zaage Dane-Z̲aa Z̲áágéʔ Western Subarctic fv_dane_zaa_zaage_kmw-9.0.js 9.4 bea Beaver
89 fv fv_dene_dzage Dene Dzage Western Subarctic fv_dene_dzage_kmw-9.0.js 11.0.1 kkz-Latn Kaska (Latin)
90 fv fv_dene_zhatie Dene Zhatié Western Subarctic fv_dene_zhatie_kmw-9.0.js 10.2 10.2.1 den Dene Zhatıé
91 fv fv_denesuline_epsilon Dënesųłıné Western Subarctic fv_denesuline_epsilon_kmw-9.0.js 10.0.1 chp Chipewyan
92 fv fv_denesuline Dɛnɛsųłiné Western Subarctic fv_denesuline_kmw-9.0.js 10.0.1 chp Chipewyan (Latin)
93 fv fv_gwichin Gwich'in Western Subarctic fv_gwichin_kmw-9.0.js 9.2.1 gwi-Latn Gwichʼin (Latin)
94 fv fv_han Hän Western Subarctic fv_han_kmw-9.0.js 9.2 haa-Latn Han (Latin)
95 fv fv_kashogotine_yati K'áshogot'ı̨nę́ Yatı̨́ Western Subarctic fv_kashogotine_yati_kmw-9.0.js 10.0 10.1 scs North Slavey
96 fv fv_tlingit Łingít Western Subarctic fv_tlingit_kmw-9.0.js 10.0.1 10.1 tli Tlingit (Latin)
97 fv fv_neeaandeg Nee'aanděg' Western Subarctic fv_neeaandeg_kmw-9.0.js 9.1.1 tcb-Latn Tanacross (Latin)
98 fv fv_neeaaneegn Nee'aaneegn' Western Subarctic fv_neeaaneegn_kmw-9.0.js 9.1 tau-Latn Upper Tanana (Latin)
99 fv fv_northern_tutchone Northern Tutchone Western Subarctic fv_northern_tutchone_kmw-9.0.js 9.2 ttm-Latn Northern Tutchone (Latin)
100 fv fv_sahugotine_yati Sahtúgot'ı̨nę́ Yatı̨́ Western Subarctic fv_sahugotine_yati_kmw-9.0.js 9.1.1 scs-Latn North Slavey (Latin)
101 fv fv_shihgotine_yati Shıhgot'ı̨nę́ Yatı̨́ Western Subarctic fv_shihgotine_yati_kmw-9.0.js 9.1.1 scs-Latn North Slavey (Latin)
102 fv fv_southern_tutchone Southern Tutchone Western Subarctic fv_southern_tutchone_kmw-9.0.js 9.2.2 9.3 tce-Latn Southern Tutchone (Latin)
103 fv fv_tagizi_dene Tāgizi Dene Western Subarctic fv_tagizi_dene_kmw-9.0.js 9.3 tgx-Latn Tagish (Latin)
104 fv fv_tlicho_yatii Tłı̨chǫ Yatıı̀ Western Subarctic fv_tlicho_yatii_kmw-9.0.js 9.1.1 dgr-Latn Dogrib (Latin)
105 fv fv_dene_mb ᑌᓀ ᔭᕠᐁ (Dene MB) Western Subarctic fv_dene_mb_kmw-9.0.js 9.2.1 chp-Cans Chipewyan (Unified Canadian Aboriginal Syllabics)

60
package-lock.json generated
View file

@ -27,6 +27,7 @@
"common/web/*",
"common/tools/hextobin",
"web",
"web/src/engine/osk/gesture-processor",
"web/src/engine/predictive-text/*"
],
"dependencies": {
@ -59,7 +60,7 @@
"eslint-plugin-promise": "^6.1.1",
"mocha": "^10.0.0",
"mocha-teamcity-reporter": "^4.0.0",
"playwright": "^1.43.1",
"playwright": "^1.46.1",
"sinon": "^17.0.1",
"source-map-support": "^0.5.21",
"tslib": "^2.5.2",
@ -159,19 +160,6 @@
},
"devDependencies": {}
},
"common/web/gesture-recognizer": {
"name": "@keymanapp/gesture-recognizer",
"dependencies": {
"eventemitter3": "^5.0.0"
},
"devDependencies": {
"@keymanapp/resources-gosh": "*",
"mocha": "^10.0.0",
"mocha-teamcity-reporter": "^4.0.0",
"promise-status-async": "^1.2.10",
"typescript": "^5.4.5"
}
},
"common/web/keyman-version": {
"name": "@keymanapp/keyman-version",
"license": "MIT",
@ -404,7 +392,7 @@
"eventemitter3": "^5.0.0",
"restructure": "^3.0.1",
"sax": ">=0.6.0",
"semver": "^7.5.2",
"semver": "^7.5.4",
"xmlbuilder": "~11.0.0"
},
"devDependencies": {
@ -1153,7 +1141,7 @@
"@keymanapp/keyman-version": "*",
"@keymanapp/kmc-kmn": "*",
"@keymanapp/ldml-keyboard-constants": "*",
"semver": "^7.5.2"
"semver": "^7.5.4"
},
"devDependencies": {
"@keymanapp/developer-test-helpers": "*",
@ -1942,7 +1930,7 @@
"open": "^8.4.0",
"restructure": "^3.0.1",
"sax": ">=0.6.0",
"semver": "^7.5.2",
"semver": "^7.5.4",
"ws": "^8.17.1",
"xmlbuilder": "~11.0.0"
},
@ -2795,7 +2783,7 @@
"link": true
},
"node_modules/@keymanapp/gesture-recognizer": {
"resolved": "common/web/gesture-recognizer",
"resolved": "web/src/engine/osk/gesture-processor",
"link": true
},
"node_modules/@keymanapp/hextobin": {
@ -12077,33 +12065,35 @@
}
},
"node_modules/playwright": {
"version": "1.43.1",
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.43.1.tgz",
"integrity": "sha512-V7SoH0ai2kNt1Md9E3Gwas5B9m8KR2GVvwZnAI6Pg0m3sh7UvgiYhRrhsziCmqMJNouPckiOhk8T+9bSAK0VIA==",
"version": "1.46.1",
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.46.1.tgz",
"integrity": "sha512-oPcr1yqoXLCkgKtD5eNUPLiN40rYEM39odNpIb6VE6S7/15gJmA1NzVv6zJYusV0e7tzvkU/utBFNa/Kpxmwng==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"playwright-core": "1.43.1"
"playwright-core": "1.46.1"
},
"bin": {
"playwright": "cli.js"
},
"engines": {
"node": ">=16"
"node": ">=18"
},
"optionalDependencies": {
"fsevents": "2.3.2"
}
},
"node_modules/playwright-core": {
"version": "1.43.1",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.43.1.tgz",
"integrity": "sha512-EI36Mto2Vrx6VF7rm708qSnesVQKbxEWvPrfA1IPY6HgczBplDx7ENtx+K2n4kJ41sLLkuGfmb0ZLSSXlDhqPg==",
"version": "1.46.1",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.46.1.tgz",
"integrity": "sha512-h9LqIQaAv+CYvWzsZ+h3RsrqCStkBHlgo6/TJlFst3cOTlLghBQlJwPOZKQJTKNaD3QIB7aAVQ+gfWbN3NXB7A==",
"dev": true,
"license": "Apache-2.0",
"bin": {
"playwright-core": "cli.js"
},
"engines": {
"node": ">=16"
"node": ">=18"
}
},
"node_modules/portfinder": {
@ -14630,7 +14620,7 @@
"devDependencies": {
"@types/semver": "^7.1.0",
"@types/yargs": "^17.0.26",
"semver": "^7.5.2"
"semver": "^7.5.4"
}
},
"resources/build/version/node_modules/ansi-regex": {
@ -14764,6 +14754,20 @@
"mocha": "^10.0.0"
}
},
"web/src/engine/osk/gesture-processor": {
"name": "@keymanapp/gesture-recognizer",
"dependencies": {
"@keymanapp/web-utils": "*",
"eventemitter3": "^5.0.0"
},
"devDependencies": {
"@keymanapp/resources-gosh": "*",
"mocha": "^10.0.0",
"mocha-teamcity-reporter": "^4.0.0",
"promise-status-async": "^1.2.10",
"typescript": "^5.4.5"
}
},
"web/src/engine/predictive-text/types": {
"name": "@keymanapp/lm-message-types",
"license": "MIT",

View file

@ -23,7 +23,7 @@
"eslint-plugin-promise": "^6.1.1",
"mocha": "^10.0.0",
"mocha-teamcity-reporter": "^4.0.0",
"playwright": "^1.43.1",
"playwright": "^1.46.1",
"sinon": "^17.0.1",
"source-map-support": "^0.5.21",
"tslib": "^2.5.2",
@ -52,6 +52,7 @@
"common/web/*",
"common/tools/hextobin",
"web",
"web/src/engine/osk/gesture-processor",
"web/src/engine/predictive-text/*"
],
"dependencies": {

View file

@ -23,8 +23,10 @@ source "$KEYMAN_ROOT/resources/build/win/environment_generated.inc.sh"
WINDOWS_ROOT="$KEYMAN_ROOT/windows"
WINDOWS_PROGRAM_APP="$WINDOWS_ROOT/bin/desktop"
WINDOWS_PROGRAM_ENGINE="$WINDOWS_ROOT/bin/engine"
WINDOWS_PROGRAM_SUPPORT="$WINDOWS_ROOT/bin/support"
WINDOWS_DEBUGPATH_APP="$WINDOWS_ROOT/debug/desktop"
WINDOWS_DEBUGPATH_ENGINE="$WINDOWS_ROOT/debug/engine"
WINDOWS_DEBUGPATH_SUPPORT="$WINDOWS_ROOT/debug/support"
COMMON_ROOT="$KEYMAN_ROOT/common/windows/delphi"
OUTLIB="$WINDOWS_ROOT/lib"
@ -141,6 +143,8 @@ create-windows-output-folders() {
mkdir -p "$OUTLIB"
mkdir -p "$WINDOWS_PROGRAM_APP"
mkdir -p "$WINDOWS_PROGRAM_ENGINE"
mkdir -p "$WINDOWS_PROGRAM_SUPPORT"
mkdir -p "$WINDOWS_DEBUGPATH_APP"
mkdir -p "$WINDOWS_DEBUGPATH_ENGINE"
mkdir -p "$WINDOWS_DEBUGPATH_SUPPORT"
}

View file

@ -69,5 +69,7 @@ _select_emscripten_version_with_emsdk() {
git pull
./emsdk install "$KEYMAN_MIN_VERSION_EMSCRIPTEN"
./emsdk activate "$KEYMAN_MIN_VERSION_EMSCRIPTEN"
cd upstream/emscripten
npm install
popd > /dev/null
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,58 @@
#!/usr/bin/env bash
## START STANDARD BUILD SCRIPT INCLUDE
# adjust relative paths as necessary
THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
. "$(dirname "$THIS_SCRIPT")/../../../resources/build/builder.inc.sh"
## END STANDARD BUILD SCRIPT INCLUDE
. "$KEYMAN_ROOT/resources/build/minimum-versions.inc.sh"
################################ Main script ################################
builder_describe \
"Downloads Unicode data files, version $KEYMAN_VERSION_UNICODE (see minimum-versions.inc.sh), to be committed to repo." \
download+
builder_describe_outputs \
download /resources/standards-data/unicode-character-database/UnicodeData.txt
builder_parse "$@"
# Used by Developer
BLOCKS_SRC_HREF="https://www.unicode.org/Public/$KEYMAN_VERSION_UNICODE/ucd/Blocks.txt"
BLOCKS_SRC_LOCAL="./Blocks.txt"
UNICODE_DATA_SRC_HREF="https://www.unicode.org/Public/$KEYMAN_VERSION_UNICODE/ucd/UnicodeData.txt"
UNICODE_DATA_SRC_LOCAL="./UnicodeData.txt"
# Used by common/models/wordbreakers for the default Unicode wordbreaker.
WORDBREAK_PROP_SRC_HREF="https://www.unicode.org/Public/$KEYMAN_VERSION_UNICODE/ucd/auxiliary/WordBreakProperty.txt"
WORDBREAK_PROP_SRC_LOCAL="./WordBreakProperty.txt"
EMOJI_DATA_SRC_HREF="https://www.unicode.org/Public/$KEYMAN_VERSION_UNICODE/ucd/emoji/emoji-data.txt"
EMOJI_DATA_SRC_LOCAL="./emoji-data.txt"
function downloadPropertyFile() {
local SRC="$1"
local DEST="$2"
local RETRY=5 # Curl retries this number of times before giving up
local RETRY_DELAY=5 # Make curl sleep this amount of time before each retry when a transfer has failed
echo "Downloading ${SRC} - ${RETRY} attempts"
# local URL_DOWNLOAD_FILE=`curl --retry "$RETRY" --retry-delay "$RETRY_DELAY" --silent "${SRC}" | "$JQ" -r .txt`
curl --fail --retry "$RETRY" --retry-delay "$RETRY_DELAY" --silent "$SRC" --output "$DEST" || {
builder_die "Downloading $SRC failed with error $?"
}
}
do_download() {
downloadPropertyFile "${BLOCKS_SRC_HREF}" "${BLOCKS_SRC_LOCAL}"
downloadPropertyFile "${UNICODE_DATA_SRC_HREF}" "${UNICODE_DATA_SRC_LOCAL}"
downloadPropertyFile "${WORDBREAK_PROP_SRC_HREF}" "${WORDBREAK_PROP_SRC_LOCAL}"
downloadPropertyFile "${EMOJI_DATA_SRC_HREF}" "${EMOJI_DATA_SRC_LOCAL}"
}
builder_run_action download do_download

File diff suppressed because it is too large Load diff

View file

@ -7,8 +7,6 @@
{ "path": "./common/models/wordbreakers/tsconfig.json" },
{ "path": "./common/tools/hextobin/" },
{ "path": "./common/web/gesture-recognizer/tsconfig.json" },
{ "path": "./common/web/gesture-recognizer/src/tools/unit-test-resources/tsconfig.json" },
{ "path": "./common/web/keyman-version" },
{ "path": "./common/web/recorder/tsconfig.json" },
{ "path": "./common/web/sentry-manager/src/tsconfig.json" },
@ -43,6 +41,8 @@
{ "path": "./web/src/engine/predictive-text/types/" },
{ "path": "./web/src/engine/predictive-text/worker-main/tsconfig.all.json" },
{ "path": "./web/src/engine/predictive-text/worker-thread" },
{ "path": "./web/src/engine/osk/gesture-processor/tsconfig.json" },
{ "path": "./web/src/engine/osk/gesture-processor/src/tools/unit-test-resources/tsconfig.json" },
// { "path": "./web/tools/recorder/tsconfig.json" },
// { "path": "./web/tools/sourcemap-root/tsconfig.json" },
]

View file

@ -94,7 +94,7 @@ graph TD;
LMWorker-->Wordbreakers;
LMLayer["@keymanapp/lexical-model-layer<br>(/web/src/engine/predictive-text/worker-main)"];
LMLayer-->LMWorker;
Gestures["@keymanapp/gesture-recognizer<br>(/common/web/gesture-recognizer)"];
Gestures["@keymanapp/gesture-recognizer<br>(/web/src/engine/osk/gesture-recognizer)"];
Gestures-->WebUtils;
subgraph PredText["PredText: WebWorker + its interface"]

View file

@ -109,6 +109,11 @@ precompile() {
build_action() {
builder_echo "Building auto tests..."
# The currently-bundled declaration file for gesture-processor generates
# errors when compiling against it with current tsc versions.
rm -f "${KEYMAN_ROOT}/node_modules/promise-status-async/lib/index.d.ts"
tsc --project "${KEYMAN_ROOT}/web/src/test/auto/tsconfig.json"
for dir in \

View file

@ -86,7 +86,7 @@ if builder_start_action test; then
# No --reporter option exists yet for the headless modules.
"$KEYMAN_ROOT/web/src/engine/keyboard/build.sh" test $OPTIONS
"$KEYMAN_ROOT/common/web/gesture-recognizer/test.sh" $OPTIONS
"$KEYMAN_ROOT/web/src/engine/osk/gesture-processor/build.sh" test $OPTIONS
./build.sh test $OPTIONS

View file

@ -10,6 +10,8 @@ BUNDLE_CMD="node $KEYMAN_ROOT/common/web/es-bundling/build/common-bundle.mjs"
# ### Parameters
#
# * 1: `product` the product's source path under src/
# * 2: `src_dir` the source directory. Optional. Default: ${KEYMAN_ROOT}/web/src
# * 3: `build_dir` the build directory. Optional. Default: ${KEYMAN_ROOT}/web/build
#
# ### Example
#
@ -22,11 +24,13 @@ function compile() {
fi
local COMPILE_TARGET="$1"
local SRC_DIR=${2:-"${KEYMAN_ROOT}/web/src"}
local BUILD_DIR=${3:-"${KEYMAN_ROOT}/web/build"}
tsc -b "${KEYMAN_ROOT}/web/src/$COMPILE_TARGET"
tsc -b "${SRC_DIR}/$COMPILE_TARGET"
# So... tsc does declaration-bundling on its own pretty well, at least for local development.
tsc --emitDeclarationOnly --outFile "${KEYMAN_ROOT}/web/build/$COMPILE_TARGET/lib/index.d.ts" -p "${KEYMAN_ROOT}/web/src/$COMPILE_TARGET"
tsc --emitDeclarationOnly --outFile "${BUILD_DIR}/$COMPILE_TARGET/lib/index.d.ts" -p "${SRC_DIR}/$COMPILE_TARGET"
}
function _copy_dir_if_exists() {

View file

@ -89,6 +89,7 @@
}
},
"imports": {
"#gesture-tools": "./src/engine/osk/gesture-processor/build/tools/obj/index.js",
"#recorder": "./build/tools/testing/recorder/obj/index.js"
},
"repository": {

View file

@ -44,22 +44,20 @@ do_clean() {
}
compile_and_copy() {
local COMPILE_FLAGS=
if builder_has_option --ci; then
COMPILE_FLAGS=--ci
fi
compile $SUBPROJECT_NAME $COMPILE_FLAGS
compile $SUBPROJECT_NAME
BUILD_ROOT="${KEYMAN_ROOT}/web/build/app/browser"
SRC_ROOT="${KEYMAN_ROOT}/web/src/app/browser/src"
$BUNDLE_CMD "${SRC_ROOT}/debug-main.js" \
--out "${BUILD_ROOT}/debug/keymanweb.js" \
--charset "utf8" \
--sourceRoot "@keymanapp/keyman/web/build/app/browser/debug" \
--target "es6"
$BUNDLE_CMD "${SRC_ROOT}/release-main.js" \
--out "${BUILD_ROOT}/release/keymanweb.js" \
--charset "utf8" \
--profile "${BUILD_ROOT}/filesize-profile.log" \
--sourceRoot "@keymanapp/keyman/web/build/app/browser/release" \
--minify \
@ -67,6 +65,7 @@ compile_and_copy() {
$BUNDLE_CMD "${BUILD_ROOT}/obj/test-index.js" \
--out "${BUILD_ROOT}/lib/index.mjs" \
--charset "utf8" \
--sourceRoot "@keymanapp/keyman/web/build/app/browser/lib" \
--format esm

View file

@ -45,11 +45,13 @@ compile_and_copy() {
$BUNDLE_CMD "${SRC_ROOT}/debug-main.js" \
--out "${BUILD_ROOT}/debug/keymanweb-webview.js" \
--charset "utf8" \
--sourceRoot "@keymanapp/keyman/web/build/app/webview/debug" \
--target "es6"
$BUNDLE_CMD "${SRC_ROOT}/release-main.js" \
--out "${BUILD_ROOT}/release/keymanweb-webview.js" \
--charset "utf8" \
--profile "${BUILD_ROOT}/filesize-profile.log" \
--sourceRoot "@keymanapp/keyman/web/build/app/webview/release" \
--minify \

View file

@ -1,7 +1,5 @@
import OutputTarget from './outputTarget.js';
// Due to some interesting requirements on compile ordering in TS,
// this needs to be in the same file as OutputTarget now.
export class Mock extends OutputTarget {
text: string;

View file

@ -66,12 +66,6 @@ function do_build() {
--format esm \
--platform node
# # Tests
# builder_echo "Bundle tests"
# ${BUNDLE_CMD} "${BUILD_DIR}/tests/dom/cases/domKeyboardLoader.spec.js" \
# --out "${BUILD_DIR}/tests/dom/domKeyboardLoader.spec.mjs" \
# --format esm
# Declaration bundling.
builder_echo "Declaration bundling"
tsc --emitDeclarationOnly --outFile "${BUILD_DIR}/lib/index.d.ts"

View file

@ -14,10 +14,10 @@ SUBPROJECT_NAME=engine/osk
builder_describe "Builds the Keyman Engine for Web's On-Screen Keyboard package (OSK)." \
"@/web/src/engine/keyboard build" \
"@/common/web/gesture-recognizer build" \
"@/web/src/engine/interfaces build" \
"@/web/src/engine/dom-utils build" \
"@/web/src/engine/events build" \
"@/web/src/engine/osk/gesture-processor" \
"clean" \
"configure" \
"build" \

View file

@ -6,6 +6,6 @@ KeymanWeb, seen within this repo at /web/.
## Minimum Compilation Requirements
See the repo-level [document for build configuration](../../../docs/build/index.md) for details on how to
See the repo-level [document for build configuration](../../../../../docs/build/index.md) for details on how to
configure your build environment.
- Follow the Keyman Engine for Web (KeymanWeb) requirements where appropriate.

View file

@ -0,0 +1,65 @@
#!/usr/bin/env bash
## START STANDARD BUILD SCRIPT INCLUDE
# adjust relative paths as necessary
THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
. "$(dirname "$THIS_SCRIPT")/../../../../../resources/build/builder.inc.sh"
## END STANDARD BUILD SCRIPT INCLUDE
SUBPROJECT_NAME=engine/osk/gesture-processor
. "${KEYMAN_ROOT}/web/common.inc.sh"
. "${KEYMAN_ROOT}/resources/shellHelperFunctions.sh"
BUNDLE_CMD="node $KEYMAN_ROOT/common/web/es-bundling/build/common-bundle.mjs"
BUILD_DIR="/web/src/engine/osk/gesture-processor/build"
################################ Main script ################################
builder_describe "Builds the gesture-recognition model for Web-based on-screen keyboards" \
"@/common/web/es-bundling build" \
"@/common/web/utils build" \
"clean" \
"configure" \
"build" \
"test" \
":module" \
":tools tools for testing & developing test resources for this module" \
"--ci sets the --ci option for child scripts (i.e, the $(builder_term test) action)"
builder_describe_outputs \
configure /node_modules \
build:module "${BUILD_DIR}/lib/index.mjs" \
build:tools "${BUILD_DIR}/tools/lib/index.mjs"
builder_parse "$@"
# TODO: build if out-of-date if test is specified
# TODO: configure if npm has not been run, and build is specified
function do_configure() {
verify_npm_setup
}
function do_build_module() {
# Build
tsc --build $builder_verbose
$BUNDLE_CMD "${KEYMAN_ROOT}/${BUILD_DIR}/obj/index.js" \
--out "${KEYMAN_ROOT}/${BUILD_DIR}/lib/index.mjs" \
--format esm
}
function do_test_tools() {
if ! builder_has_action test:module; then
echo "The $(builder_term test:tools) action is currently a no-op."
fi
}
builder_run_action configure do_configure
builder_run_action clean rm -rf build/ intermediate/
builder_run_action build:module do_build_module
builder_run_action build:tools src/tools/build.sh build
builder_run_action test:module test-headless-typescript "${SUBPROJECT_NAME}"
builder_run_action test:tools do_test_tools

View file

Before

Width:  |  Height:  |  Size: 92 KiB

After

Width:  |  Height:  |  Size: 92 KiB

View file

@ -8,14 +8,12 @@
"promise-status-async": "^1.2.10",
"typescript": "^5.4.5"
},
"imports": {
"#tools": "./build/tools/obj/index.js"
},
"scripts": {
"build": "gosh ./build.sh",
"test": "gosh ./test.sh"
"test": "gosh ./build.sh test"
},
"dependencies": {
"@keymanapp/web-utils": "*",
"eventemitter3": "^5.0.0"
},
"main": "./build/obj/index.js",

Some files were not shown because too many files have changed in this diff Show more