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

chore: merge master into user-dict
This commit is contained in:
Marc Durdin 2025-02-14 05:30:01 +07:00 • committed by GitHub
commit dbf02176fb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
343 changed files with 6651 additions and 1326 deletions

View file

@ -116,7 +116,7 @@ jobs:
strategy:
fail-fast: true
matrix:
dist: [focal, jammy, noble, oracular]
dist: [jammy, noble, oracular]
steps:
- name: Checkout

View file

@ -1,5 +1,84 @@
# Keyman Version History
## 19.0.2 alpha 2025-02-12
* chore(linux): remove support of Ubuntu 20.04 Focal (#13203)
## 19.0.1 alpha 2025-02-11
* refactor(windows): rename `TKeymanMutex.MutexOwned` to `TakeOwnership` and add `ReleaseOwnership` (#13168)
* chore: increment to alpha 19.0 (#13187)
## 18.0.188 alpha 2025-02-10
* fix(windows): check the params status flag equals ucrsUpdateReady before attempting to download the keyman setup file (#13154)
* feat(windows): background updates go from downloading to waiting for a restart except if the `apply now` flag is set (#13159)
* feat(developer): verify package version number format in kmc-package (#13118)
* fix(developer): support non-US base keyboard layouts in debuggers (#13131)
* feat(developer): improve compiler messages and user interface (#13156)
* feat(developer): verify that packages do not contain themselves in kmc-package (#13157)
* fix(developer): link welcome.htm in package for new projects; use v17 project format for new models (#13161)
* fix(windows): use Automatically check for updates and download for the english xml (#13162)
* change(linux): Implement ordered output without patched ibus (#11535)
## 18.0.187 alpha 2025-02-07
* docs(web): relocate gesture docs to web/docs/internal (#13139)
* chore(mac): remove empty options tab from configuration (#13160)
* chore(linux): Update debian changelog (#13143)
## 18.0.186 alpha 2025-02-06
* feat(windows): check updates and automatic update merged (#13115)
## 18.0.185 alpha 2025-02-05
* fix(developer): make kmc log options consistent across all commands (#13075)
* feat(developer): add user interface for `kmc copy` to TIKE (#13076)
* chore(developer): add verbose logs for project copier (#13080)
* chore(mac, ios): build and run with xcode 16 (#13077)
## 18.0.184 alpha 2025-02-04
* fix(common): handle undefined input in `@keymanapp/langtags` lookups (#13117)
* chore: update docs to reference node 20.0 (#13124)
* feat(developer): include command line in kmc sentry reports (#13113)
* fix(common/web): replace invalid StrsItem identity checks with isEqual() (#13122)
## 18.0.183 alpha 2025-02-03
* change(ios): set host-app bundle identifier in embedded Web debug-report (#13105)
* fix(ios): prevent message-handler collision (#13037)
* change(web): add context tracker bksp handling, alignment-offset calculations (#12911)
* fix(web): correct edit-path ordering at source (#12925)
* fix(web): correctly track context transitions caused by applying suggestions (#12927)
* chore(linux): preserve log files when running tests under Docker (#13107)
## 18.0.182 alpha 2025-01-31
* feat(windows): report on processor type in tsysinfo (#13084)
* fix(common): push branch when creating PR in version history writer (#13082)
* refactor: move langtags to @keymanapp/langtags npm module (#13046)
* chore(developer): use langtags.json for kmc-generate language names (#13047)
* feat(developer): support relocation of external files in kmc-copy (#13061)
* feat(windows): kmshell switch handling for the installing state (#12956)
* feat(windows): add boot switch (#12995)
* docs(web): remove `.md` in help links (#13051)
* chore: add android platform to titles (#13033)
* chore: add the windows platform to titles (#13026)
* fix(mac): properly manage Input Method lifecycle (#13006)
* chore(developer): handle forward slashes in paths in package editor (#13081)
* chore(developer,core,resources): LDML v46 update and remove workaround for import base (#13099)
## 18.0.181 alpha 2025-01-30
* feat(windows): add localized button captions and form labels (#13025)
* fix(web): disabled correction state should prevent predictive corrections (#13066)
* fix(web): disabled autoaccept should not autohighlight suggestions (#13068)
* chore(common): add 17.0.334 to version history (#13065)
* fix(windows): add api GetContext and TIPProcessKeyEx with IsTextSelected bool (#13023)
* fix(web): keyboard query error states should always provide informative Error objects, reports (#13048)
## 18.0.180 alpha 2025-01-29
* chore(common): Update crowdin strings for Portuguese (#13056)
@ -1242,6 +1321,18 @@
* chore(common): move to 18.0 alpha (#10713)
* chore: move to 18.0 alpha
## 17.0.335 alpha 2025-02-06
* fix(android): improve resource-update tool handling of host Activity's closure (#13057)
* fix(ios): prevent message-handler collision (#13058)
* chore(linux): improve compatibility with Gentoo Linux (#12889)
* chore(linux): Update debian changelog (#13062)
* fix(web): keyboard query error states should always provide informative Error objects, reports (#13079)
* chore: push the branch when creating PR in version history writer (#13085)
* fix(web): disabled correction state should prevent predictive corrections (#13074)
* change(ios): set host-app bundle identifier in embedded Web debug-report (#13112)
* chore(mac, ios): build and run with xcode 16 (#13121)
## 17.0.334 stable 2025-01-27
* chore(linux): update copyright year (#12919)

View file

@ -1 +1 @@
18.0.181
19.0.3

View file

@ -1,13 +1,13 @@
---
title: Guide: build an in-app keyboard
title: Guide: build an in-app keyboard for Android with Keyman Engine
---
Keyman Engine for Android allows you to use any Keyman touch keyboard in your Android app, or even to create your own
system keyboard app for purchase in the Play Store.<br/>
This guide will walk you through the steps for creating your first Android app with Keyman Engine for Android.
If you are not familiar with Android development, you will find the
[Android Developer online training](https://developer.android.com/training/index.html) an invaluable
If you are not familiar with Android development, you will find the
[Android Developer online training](https://developer.android.com/training/index.html) an invaluable
resource, and working through some of their tutorials first will help you with the rest of this guide.
### 1. Install Free Tools
@ -131,5 +131,5 @@ And there you have it: your first Keyman Engine for Android app!
## See Also
* [Guide: Build a system keyboard app](../system-keyboard/)
* [Keyman Developer Documentation](/developer/17.0/)
* [Keyman Engine for Android Documentation](/developer/engine/android/18.0/)
* [Keyman Engine for Android Documentation](/developer/engine/android/19.0/)
* [Android Developer Home](https://developer.android.com/index.html)

View file

@ -1,5 +1,5 @@
---
title: Guide: build a system keyboard app
title: Guide: build a system keyboard app for Android with Keyman Engine
---
In [part 1](../in-app/) of this series, we looked at the steps involved

View file

@ -4,7 +4,7 @@ title: Keyman Engine for Android
## Overview
Keyman Engine for Android 18.0 is a Java library for Android 5.0 and later versions which enables a fully customisable keyboard layout, both within an app and system-wide.
Keyman Engine for Android 19.0 is a Java library for Android 5.0 and later versions which enables a fully customisable keyboard layout, both within an app and system-wide.
Keyboard layouts for Keyman Engine can be created with [Keyman Developer](/developer/17.0), and a [library of existing keyboard layouts](http://keyman.com/developer/keymanweb/keyboards)
is also available.</p>

View file

@ -1,11 +1,6 @@
---
title: What's New in Keyman Engine 18.0 for Android
title: What's New in Keyman Engine 19.0 for Android
---
## Keyman Engine for Android Breaking Changes: ##
* Change package name from `com.tavultesoft.kmea` to `com.keyman.engine` #7881
* Update to Java 11 #8543
## See Also
* [Keyman Engine for Android Documentation](index)

View file

@ -1,5 +1,5 @@
---
title: About Keyman
title: About Keyman for Android
---
* [Welcome to Keyman](welcome)

View file

@ -1,9 +1,6 @@
---
title: What's New in Keyman 18.0 for Android
title: What's New in Keyman 19.0 for Android
---
Here are some of the new features we have added to Keyman 18.0 for Android:
Here are some of the new features we have added to Keyman 19.0 for Android:
* New menu to adjust longpress delay time (#12170, #12185)
* Support localizations for right-to-left languages (#12215)
* Handle additional actions for ENTER key (#12125, #12315)

View file

@ -1,5 +1,5 @@
---
title: Basic Help
title: Basic Help - Keyman for Android
---
* [Using the Banner on the Keyboard](using-the-banner)

View file

@ -1,5 +1,5 @@
---
title: Gesture Hints and Tips
title: Gesture Hints and Tips - Keyman for Android
---
## Using Gestures on the Keyboard Keys

View file

@ -1,5 +1,5 @@
---
title: Keyman for Android 18.0 Help
title: Keyman for Android 19.0 Help
---
## [About Keyman](about/)

View file

@ -1,5 +1,5 @@
---
title: How To - Enabling Keyman as a System-Wide Keyboard
title: How To - Enabling Keyman for Android as a System-Wide Keyboard
---
## Enabling System Wide Keyboards

View file

@ -1,5 +1,5 @@
---
title: Getting Started
title: Getting Started with Keyman for Android
---
* [How To - Download and Install a Keyman Keyboard](installing-keyboards)

View file

@ -1,5 +1,5 @@
---
title: How To - Granting Storage Permission
title: How To - Granting Storage Permission on Keyman for Android
---
## Granting Storage Permission

View file

@ -10,6 +10,7 @@ THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
builder_describe "Keyman common web modules" \
:keyman-version \
:langtags \
:types \
clean \
configure \

1
common/web/langtags/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
src/imports/

View file

@ -0,0 +1,38 @@
# Keyman langtags
This package provides a wrapper for the
[langtags.json](https://ldml.api.sil.org/langtags.json) dataset from
https://github.com/silnrsi/langtags.
This package is published in sync with Keyman, so changes in langtags may not
propagate immediately.
## Usage
* `getLangtagByTag(tag)`: Find a language tag from a given tag; matches on tag,
tags, full properties, with a case-insensitive search. Returns unmodified
object from langtags.json on match.
* `metadata.conformance()`: Returns script and region conformance data from
langtags.json
* `metadata.globalvar()`: Returns global variant data from langtags.json
* `metadata.phonvar()`: Returns phonetic variant data from langtags.json
* `metadata.version()`: Returns version data from langtags.json
* `langtags`: Raw access to the langtags.json array of objects
## Background
Key documents for further reading (from langtags repository):
* [Overview of language tags and how this project works with them](https://github.com/silnrsi/langtags/blob/master/doc/tagging.md)
* [How to use langtags.json](https://github.com/silnrsi/langtags/blob/master/doc/langtags.md)
## License
* Copyright ©️ 2025 SIL Global.
* MIT License ([License text](https://github.com/keymanapp/keyman/tree/master/LICENSE.md))

45
common/web/langtags/build.sh Executable file
View file

@ -0,0 +1,45 @@
#!/usr/bin/env bash
## START STANDARD BUILD SCRIPT INCLUDE
# adjust relative paths as necessary
THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
. "${THIS_SCRIPT%/*}/../../../resources/build/builder.inc.sh"
## END STANDARD BUILD SCRIPT INCLUDE
. "$KEYMAN_ROOT/resources/shellHelperFunctions.sh"
. "$KEYMAN_ROOT/resources/build/build-utils-ci.inc.sh"
builder_describe "Build Keyman langtags.js common module" \
"clean" \
"configure" \
"build" \
"test" \
"publish publish to npm" \
"--npm-publish+ For publish, do a npm publish, not npm pack (only for CI)" \
"--dry-run,-n don't actually publish, just dry run"
builder_describe_outputs \
configure /common/web/langtags/src/imports/langtags.js \
build /common/web/langtags/build/src/main.js
builder_parse "$@"
#-------------------------------------------------------------------------------------------------------------------
function compile_langtags() {
mkdir -p src/imports
echo 'export default ' > src/imports/langtags.js
cat "$KEYMAN_ROOT/resources/standards-data/langtags/langtags.json" >> src/imports/langtags.js
}
function do_configure() {
verify_npm_setup
compile_langtags
}
#-------------------------------------------------------------------------------------------------------------------
builder_run_action clean rm -rf ./build ./src/imports ./node_modules
builder_run_action configure do_configure
builder_run_action build tsc --build
builder_run_action test echo 'no tests for langtags'
builder_run_action publish builder_publish_npm

View file

@ -0,0 +1,39 @@
{
"name": "@keymanapp/langtags",
"description": "Keyman bundling of language tags from langtags.json",
"keywords": [
"keyboard",
"keyman",
"ldml",
"unicode"
],
"type": "module",
"types": "./build/src/main.d.ts",
"exports": {
".": {
"es6-bundling": "./src/main.ts",
"types": "./build/src/main.d.ts",
"default": "./build/src/main.js"
}
},
"files": [
"/build/src/"
],
"scripts": {
"build": "tsc -b",
"test": "echo no tests for langtags"
},
"author": "Marc Durdin <marc@keyman.com> (https://github.com/mcdurdin)",
"license": "MIT",
"bugs": {
"url": "https://github.com/keymanapp/keyman/issues"
},
"devDependencies": {
"typescript": "^5.4.5"
},
"repository": {
"type": "git",
"url": "git+https://github.com/keymanapp/keyman.git"
},
"sideEffects": false
}

View file

@ -0,0 +1,122 @@
/*
* Keyman is copyright (C) SIL Global. MIT License.
*
* Wraps langtags.json as a Javascript object
*/
import langtags from './imports/langtags.js';
// These interfaces and types correspond to https://github.com/silnrsi/langtags/blob/master/source/langtags_schema.json
export type LangTagBcp47 = string; // BCP 47
export type LangTagBcp47Variant = string; // BCP 47 variant
export type LangTagIso639_3 = string; // ISO639-3
export type LangTagIso3166_1 = string; // region
export type LangTagIso15924 = string; // script
export interface LangTag {
full: string; // should be LangTagBcp47?
iana?: string[];
iso639_3?: LangTagIso639_3;
latnnames?: string[];
localname?: string;
localnames?: string;
macrolang?: LangTagBcp47;
name?: string;
names?: string[];
nophonvars?: boolean;
obsolete?: boolean;
region?: LangTagIso3166_1;
regionname?: string;
regions?: LangTagIso3166_1[];
rod?: string;
script?: LangTagIso15924;
sldr?: boolean;
suppress?: boolean;
tag?: LangTagBcp47;
tags?: LangTagBcp47[];
unwritten?: boolean;
variants?: LangTagBcp47Variant[];
windows?: LangTagBcp47;
};
export interface LangTagConformance {
regions: LangTagIso3166_1[];
scripts: LangTagIso15924[];
tag: '_conformance';
};
export interface LangTagGlobalVar {
variants: LangTagBcp47Variant[];
tag: '_globalvar';
};
export interface LangTagPhonVar {
variants: LangTagBcp47Variant[];
tag: '_phonvar';
};
export interface LangTagVersion {
api: string;
date: string;
tag: '_version';
};
/**
* A map of all language tags, lower cased
*/
const langtagsByTag: Map<string, any> = new Map<string, any>();
/* metadata variables */
let conformance: LangTagConformance = undefined;
let globalvar: LangTagGlobalVar = undefined;
let phonvar: LangTagPhonVar = undefined;
let version: LangTagVersion = undefined;
const metadata = {
conformance: () => ({ regions: [...conformance.regions], scripts: [...conformance.scripts] }),
globalvar: () => ({ variants: [...globalvar.variants] }),
phonvar: () => ({ variants: [...phonvar.variants] }),
version: () => ({ api: version.api, date: version.date })
};
/**
* Build a dictionary of language tags from langtags.json. This takes under 10ms
* on a reasonable laptop.
*/
function preinit(): void {
for(const tag of langtags) {
if(tag.tag == '_conformance') {
conformance = tag;
} else if(tag.tag == '_globalvar') {
globalvar = tag;
} else if(tag.tag == '_phonvar') {
phonvar = tag;
} else if(tag.tag == '_version') {
version = tag;
} else if(tag.full && tag.tag) {
langtagsByTag.set(tag.tag.toLowerCase(), tag);
langtagsByTag.set(tag.full.toLowerCase(), tag);
if(tag.tags) {
for(const t of tag.tags) {
langtagsByTag.set(t.toLowerCase(), tag);
}
}
}
}
}
/**
* Find a LangTag from a given tag; matches on tag, tags, full properties,
* with a case-insensitive search.
* @param tag
* @returns LangTag or undefined if not found
*/
function getLangtagByTag(tag: string): LangTag {
return langtagsByTag.get(tag?.toLowerCase());
}
preinit();
export { langtags, getLangtagByTag, metadata };

View file

@ -0,0 +1,19 @@
{
"extends": "../../../tsconfig.base.json",
"compilerOptions": {
"outDir": "build/src/",
"rootDir": "src/",
"baseUrl": ".",
/*
following two settings allow us to import langtags.js. It cannot be a .ts
because the type is too complex for TypeScript to infer.
*/
"allowJs": true,
"noImplicitAny": false,
},
"include": [
"src/**/*.ts",
"src/imports/*.js", // Import langtags.json
],
}

View file

@ -30,8 +30,8 @@ builder_parse "$@"
function compile_schemas() {
# We need the schema files at runtime and bundled, so always copy it for all actions except `clean`
local schemas=(
"$KEYMAN_ROOT/resources/standards-data/ldml-keyboards/45/ldml-keyboard3.schema.json"
"$KEYMAN_ROOT/resources/standards-data/ldml-keyboards/45/ldml-keyboardtest3.schema.json"
"$KEYMAN_ROOT/resources/standards-data/ldml-keyboards/46/ldml-keyboard3.schema.json"
"$KEYMAN_ROOT/resources/standards-data/ldml-keyboards/46/ldml-keyboardtest3.schema.json"
"$KEYMAN_ROOT/common/schemas/kvks/kvks.schema.json"
"$KEYMAN_ROOT/common/schemas/kpj/kpj.schema.json"
"$KEYMAN_ROOT/common/schemas/kpj-9.0/kpj-9.0.schema.json"

View file

@ -0,0 +1,55 @@
/*
* Keyman is copyright (C) SIL Global. MIT License.
*
* Created by Dr Mark C. Sinclair on 2025-02-03
*
* Test code for kmx-plus.ts
*/
import 'mocha';
import { assert } from 'chai';
import { Strs, StrsItem, StrsOptions, DependencySections } from '../../../src/kmx/kmx-plus/kmx-plus.js';
const GOTHIC_A = new StrsItem("𐌰", 0x10330);
describe('Test of KMX Plus file', () => {
describe('Test of Strs', () => {
describe('Test of implicit constructor', () => {
it('can construct a Strs', () => {
const strs = new Strs();
assert.isNotNull(strs);
assert.deepEqual(strs.strings, [new StrsItem('')]);
assert.deepEqual(strs.allProcessedStrings, new Set<string>());
});
});
describe('Test of allocString()', () => {
it('can allocate a one-character string', () => {
const strs = new Strs();
strs['processString'] = stubStrsProcessString;
const csi = strs.allocString("𐌰", { singleOk: true });
assert.deepEqual(csi, GOTHIC_A);
});
it('can allocate an unknown string', () => {
const strs = new Strs();
strs['processString'] = stubStrsProcessString;
const si = strs.allocString("𐌰𐌱𐌲");
assert.deepEqual(si, new StrsItem("𐌰𐌱𐌲"));
assert.deepEqual(strs.strings, [new StrsItem(''), new StrsItem("𐌰𐌱𐌲")]);
});
it('can allocate a known string', () => {
const strs = new Strs();
strs['processString'] = stubStrsProcessString;
const one = strs.allocString("𐌰𐌱𐌲");
assert.deepEqual(one, new StrsItem("𐌰𐌱𐌲"));
assert.deepEqual(strs.strings, [new StrsItem(''), new StrsItem("𐌰𐌱𐌲")]);
const two = strs.allocString("𐌰𐌱𐌲");
assert.isTrue(two === one);
assert.deepEqual(strs.strings, [new StrsItem(''), new StrsItem("𐌰𐌱𐌲")]);
});
});
});
});
function stubStrsProcessString(s: string, opts: StrsOptions, sections: DependencySections) {
return s;
}

View file

@ -314,7 +314,6 @@ const
SRegValue_ActiveProject_Filename = 'project filename';
SRegValue_ActiveProject_SourcePath = 'source path';
SRegValue_AutomaticUpdates = 'automatic updates'; //CU
SRegValue_CheckForUpdates = 'check for updates'; // CU
SRegValue_LastUpdateCheckTime = 'last update check time'; // CU
SRegValue_ApplyNow = 'apply now'; // CU Start the install now even though it will require an restart

View file

@ -58,6 +58,8 @@ const
URLPath_KeymanDeveloper_KeymanForAndroidDownload = '/go/developer/'+SKeymanVersion+'/android-app';
URLPath_KeymanDeveloper_KeymanForIosDownload = '/go/developer/'+SKeymanVersion+'/ios-app';
URLPath_KeymanDeveloper_KeyboardSearchForCloneKeymanCloud = '/go/developer/'+SKeymanVersion+'/clone-keyboard';
URLPath_Support = '/go/'+SKeymanVersion+'/support';
URLPath_Privacy = '/go/'+SKeymanVersion+'/privacy';
URLPath_Privacy_Presentation = '/privacy';
@ -71,6 +73,10 @@ const
UrlPath_RegEx_MatchKeyboardsGo = '^http(?:s)?://keyman(?:-staging)?\.com(?:\.local)?/go/windows/[^/]+/download-keyboards';
// e.g. https://keyman-staging.com/go/windows/14.0/download-keyboards?version=14.0.146.0
// Cloning keyboards - Keyman Developer
URLSubPath_KeymanDeveloper_Clone_Keyboards = '/keyboards/';
URLSubPath_KeymanDeveloper_Clone_Keyboards_Custom = '/keyboards/h/';
function URLPath_PackageDownload(const PackageID, BCP47: string; IsUpdate: Boolean): string;
function API_Protocol: string; // = 'https';
@ -86,6 +92,8 @@ function MakeAPIURL(path: string): string;
function MakeKeymanURL(const path: string): string;
function URL_KmcMessage(const id: string): string;
implementation
uses
@ -102,6 +110,7 @@ const
S_UserAgent_Developer = 'Keyman Developer';
S_UserAgent_Diagnostics = 'Keyman for Windows Diagnostics';
S_Host_KmnSh = 'https://kmn.sh';
S_KeymanCom = 'https://keyman.com';
S_APIProtocol = 'https';
S_APIServer = 'api.keyman.com';
@ -114,6 +123,7 @@ const
const
URLPath_PackageDownload_Format = '/go/package/download/%0:s?platform=windows&tier=%1:s&bcp47=%2:s&update=%3:d';
URL_KeymanDeveloper_HelpKmcMessage_Format = S_Host_KmnSh+'/%0:s';
function API_UserAgent: string;
begin
@ -130,7 +140,6 @@ begin
Result := S_UserAgent_Diagnostics + '/' + GetVersionString;
end;
function MakeKeymanURL(const path: string): string;
begin
Result := KeymanCom_Protocol_Server + path;
@ -173,4 +182,9 @@ begin
Result := Format(URLPath_PackageDownload_Format, [URLEncode(PackageID), URLEncode(CKeymanVersionInfo.Tier), URLEncode(BCP47), IsUpdateInt]);
end;
function URL_KmcMessage(const id: string): string;
begin
Result := Format(URL_KeymanDeveloper_HelpKmcMessage_Format, [id.ToLower]);
end;
end.

View file

@ -27,6 +27,7 @@ uses
Winapi.Windows;
function GetVersionString: WideString;
function GetMajorMinorVersionString: string;
procedure GetVersionBuild(var vMajor, vMinor: Integer);
function GetVersionCopyright: WideString;
function GetFileVersionString(const FileName: WideString): WideString;
@ -54,6 +55,12 @@ begin
IntToStr(LoWord(VersionMinor));
end;
function GetMajorMinorVersionString: string;
begin
Result := IntToStr(HiWord(VersionMajor)) + '.' + // I3310
IntToStr(LoWord(VersionMajor));
end;
procedure GetVersionBuild(var vMajor, vMinor: Integer);
begin
vMajor := VersionMajor;

View file

@ -1,18 +1,18 @@
(*
Name: utilfiletypes
Copyright: Copyright (C) SIL International.
Documentation:
Description:
Documentation:
Description:
Create Date: 20 Jun 2006
Modified Date: 4 Dec 2006
Authors: mcdurdin
Related Files:
Dependencies:
Related Files:
Dependencies:
Bugs:
Todo:
Notes:
Bugs:
Todo:
Notes:
History: 20 Jun 2006 - mcdurdin - Initial version
01 Aug 2006 - mcdurdin - Refactor util functions into multiple units
04 Dec 2006 - mcdurdin - Add XML and HTML filetypes
@ -100,7 +100,15 @@ type
end;
type
TKeymanProjectType = (kptUnknown, kptBasic, kptLDMLKeyboard, kptImportWindowsKeyboard, kptWordlistLexicalModel);
TKeymanProjectType = (
kptUnknown,
kptBasic,
kptLDMLKeyboard,
kptImportWindowsKeyboard,
kptWordlistLexicalModel,
kptCloneLocalProject,
kptCloneKeymanCloudProject,
kptCloneGitHubProject);
implementation

View file

@ -16,11 +16,11 @@
#pragma once
#define LDML_BKSP_FLAGS_ERROR 0x1
#define LDML_CLDR_IMPLIED_FORMS_IMPORT "45/scanCodes-implied.xml"
#define LDML_CLDR_IMPLIED_KEYS_IMPORT "45/keys-Latn-implied.xml"
#define LDML_CLDR_IMPLIED_FORMS_IMPORT "46/scanCodes-implied.xml"
#define LDML_CLDR_IMPLIED_KEYS_IMPORT "46/keys-Latn-implied.xml"
#define LDML_CLDR_IMPORT_BASE "cldr"
#define LDML_CLDR_TEST_VERSION_LATEST "techpreview"
#define LDML_CLDR_VERSION_LATEST "45"
#define LDML_CLDR_VERSION_LATEST "46"
#define LDML_ELEM_FLAGS_ORDER_BITSHIFT 0x10
#define LDML_ELEM_FLAGS_ORDER_MASK 0xFF0000
#define LDML_ELEM_FLAGS_PREBASE 0x8

View file

@ -59,7 +59,7 @@ class Constants {
/**
* The current CLDR version
*/
readonly cldr_version_latest = '45';
readonly cldr_version_latest = '46';
/**
* The version for testdata files
*/
@ -564,8 +564,17 @@ class Constants {
/** maximum count of markers (not including 'any') */
readonly marker_max_count = this.marker_max_index - this.marker_min_index + 1;
/**
* List of versions to treat as if they were the 'latest' version.
*/
treatAsLatest(version: string): boolean {
return cldrTreatAsLatest.has(version);
}
};
/** There's no data or DTD change in 45, 46, 46.1, 47 so map them all to 46 at present. */
const cldrTreatAsLatest: Set<string> = new Set(['45', '46.1', '47']);
export const constants = new Constants();
// }

View file

@ -22,7 +22,7 @@ working on ‘layr’, using ‘disp’ as a model from https://github.com/keyma
## XML changes
- `resources/standards-data/ldml-keyboards/45/` : update / reimport / fix fixup script if needed
- `resources/standards-data/ldml-keyboards/46/` : update / reimport / fix fixup script if needed
- E/C: `common/web/types/src/ldml-keyboard/ldml-keyboard-xml.ts`
- add to `LKKeyboard` and subproperties as needed to support the structure on the XML side
- Now would be a good time to stop and make sure everything compiles. It didn’t, there was an unrelated issue with snprintf!

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE keyboardTest3 SYSTEM "../../../../../resources/standards-data/ldml-keyboards/45/dtd/ldmlKeyboardTest3.dtd">
<!DOCTYPE keyboardTest3 SYSTEM "../../../../../resources/standards-data/ldml-keyboards/46/dtd/ldmlKeyboardTest3.dtd">
<keyboardTest3 conformsTo="techpreview">
<info keyboard="k_001_tiny.xml" author="srl295" name="k_001_tiny" />
<!-- <repertoire chars="[ħ ថា]" type="simple" name="our-little-language" /> TODO-LDML: not working yet -->

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE keyboardTest3 SYSTEM "../../../../../resources/standards-data/ldml-keyboards/45/dtd/ldmlKeyboardTest3.dtd">
<!DOCTYPE keyboardTest3 SYSTEM "../../../../../resources/standards-data/ldml-keyboards/46/dtd/ldmlKeyboardTest3.dtd">
<keyboardTest3 conformsTo="techpreview">
<info keyboard="k_006_backspace.xml" author="Team Keyboard" name="backspace" />
<tests name="backspace">

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE keyboardTest3 SYSTEM "../../../../../resources/standards-data/ldml-keyboards/45/dtd/ldmlKeyboardTest3.dtd">
<!DOCTYPE keyboardTest3 SYSTEM "../../../../../resources/standards-data/ldml-keyboards/46/dtd/ldmlKeyboardTest3.dtd">
<keyboardTest3 conformsTo="techpreview">
<info keyboard="k_007_transform_rgx.xml" author="Team Keyboard" name="marker" />
<tests name="regex-tests">

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE keyboardTest3 SYSTEM "../../../../../resources/standards-data/ldml-keyboards/45/dtd/ldmlKeyboardTest3.dtd">
<!DOCTYPE keyboardTest3 SYSTEM "../../../../../resources/standards-data/ldml-keyboards/46/dtd/ldmlKeyboardTest3.dtd">
<keyboardTest3 conformsTo="techpreview">
<info keyboard="k_008_transform_norm.xml" author="Team Keyboard" name="transform-norm" />
<tests name="regex-test-dot0">

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE keyboardTest3 SYSTEM "../../../../../resources/standards-data/ldml-keyboards/45/dtd/ldmlKeyboardTest3.dtd">
<!DOCTYPE keyboardTest3 SYSTEM "../../../../../resources/standards-data/ldml-keyboards/46/dtd/ldmlKeyboardTest3.dtd">
<keyboardTest3 conformsTo="techpreview">
<info keyboard="k_009_transform_nfc.xml" author="Team Keyboard" name="transform-norm" />
<tests name="regex-test-dot0">

View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Note: similar to, but slightly ahead of, fr-t-k0-azerty -->
<!DOCTYPE keyboardTest3 SYSTEM "../../../../../resources/standards-data/ldml-keyboards/45/dtd/ldmlKeyboardTest3.dtd">
<!DOCTYPE keyboardTest3 SYSTEM "../../../../../resources/standards-data/ldml-keyboards/46/dtd/ldmlKeyboardTest3.dtd">
<keyboardTest3 conformsTo="techpreview">
<info keyboard="k_020_fr.xml" author="Team Keyboard" name="fr-test-updated" />
<repertoire name="simple-repertoire" chars="[a b c d e \u0022]" type="simple" /> <!-- verify that these outputs are all available from simple keys on any layer, for all form factors -->

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE keyboardTest3 SYSTEM "../../../../../resources/standards-data/ldml-keyboards/45/dtd/ldmlKeyboardTest3.dtd">
<!DOCTYPE keyboardTest3 SYSTEM "../../../../../resources/standards-data/ldml-keyboards/46/dtd/ldmlKeyboardTest3.dtd">
<keyboardTest3 conformsTo="techpreview">
<info keyboard="k_200_reorder_nod_Lana.xml" author="Team Keyboard" name="nod-Lana" />
<tests name="reorder-tests">

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE keyboardTest3 SYSTEM "../../../../../resources/standards-data/ldml-keyboards/45/dtd/ldmlKeyboardTest3.dtd">
<!DOCTYPE keyboardTest3 SYSTEM "../../../../../resources/standards-data/ldml-keyboards/46/dtd/ldmlKeyboardTest3.dtd">
<keyboardTest3 conformsTo="techpreview">
<info keyboard="k_201_reorder_esk.xml" author="Team Keyboard" name="esk-reorder-test" />
<tests name="basic">

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE keyboardTest3 SYSTEM "../../../../../resources/standards-data/ldml-keyboards/45/dtd/ldmlKeyboardTest3.dtd">
<!DOCTYPE keyboardTest3 SYSTEM "../../../../../resources/standards-data/ldml-keyboards/46/dtd/ldmlKeyboardTest3.dtd">
<keyboardTest3 conformsTo="techpreview">
<info keyboard="k_210_marker.xml" author="Team Keyboard" name="marker" />
<tests name="marker-tests">

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE keyboardTest3 SYSTEM "../../../../../resources/standards-data/ldml-keyboards/45/dtd/ldmlKeyboardTest3.dtd">
<!DOCTYPE keyboardTest3 SYSTEM "../../../../../resources/standards-data/ldml-keyboards/46/dtd/ldmlKeyboardTest3.dtd">
<keyboardTest3 conformsTo="techpreview">
<info keyboard="k_211_marker_escape.xml" author="Team Keyboard" name="marker" />
<tests name="marker-escape">

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE keyboardTest3 SYSTEM "../../../../../resources/standards-data/ldml-keyboards/45/dtd/ldmlKeyboardTest3.dtd">
<!DOCTYPE keyboardTest3 SYSTEM "../../../../../resources/standards-data/ldml-keyboards/46/dtd/ldmlKeyboardTest3.dtd">
<keyboardTest3 conformsTo="techpreview">
<info keyboard="k_212_marker_11057.xml" author="Team Keyboard" name="marker" />
<tests name="regression-11057">

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE keyboardTest3 SYSTEM "../../../../../resources/standards-data/ldml-keyboards/45/dtd/ldmlKeyboardTest3.dtd">
<!DOCTYPE keyboardTest3 SYSTEM "../../../../../resources/standards-data/ldml-keyboards/46/dtd/ldmlKeyboardTest3.dtd">
<keyboardTest3 conformsTo="techpreview">
<info keyboard="ldml_test.xml" author="srl295" name="test" />

View file

@ -5,8 +5,8 @@
#
# keyboards in resources/standards-data/ldml-keyboards/45/3.0/
# tests in resources/standards-data/ldml-keyboards/45/test/
# keyboards in resources/standards-data/ldml-keyboards/46/3.0/
# tests in resources/standards-data/ldml-keyboards/46/test/
tests_from_cldr = [
'ja-Latn',
'pt-t-k0-abnt2',
@ -57,7 +57,7 @@ tests += tests_from_cldr
# Setup kmc
kmc_root = meson.global_source_root() / '../developer/src/kmc'
ldml_root = meson.global_source_root() / '../resources/standards-data/ldml-keyboards/45'
ldml_root = meson.global_source_root() / '../resources/standards-data/ldml-keyboards/46'
ldml_data = join_paths(ldml_root, '3.0')
ldml_testdata = join_paths(ldml_root, 'test')
kmc_cmd = [node, '--enable-source-maps', kmc_root]

View file

@ -0,0 +1,34 @@
---
title: Clone GitHub Project Parameters Dialog
---
![Clone GitHub Project Parameters dialog](../images/ui/frmCloneGitHubProjectParameters.png)
Allows you to clone a project from a GitHub repository, giving the project a
new id, and organizing project files following the
[file layout](/developer/keyboards/) used in the
[Keyman keyboards repository](https://github.com/keymanapp/keyboards).
Keyboard and lexical model projects can be cloned.
Projects can also be cloned from the command line with [`kmc copy`](kmc).
### Parameters
GitHub URL:
: The full URL to a source project .kpj file to copy, on github.com.
Destination path
: Specifies the base path where the project folder will be created.
The project folder name will be the keyboard ID. If the folder
already exists, then you will be prompted before Keyman Developer
overwrites files inside it.
New project ID
: The base filename of the keyboard/model, project and package. This must
conform to the Keyman keyboard or lexical model identifier rules.
Relocate external files into new project folder
: When selected, if the project references files that are not inside the same
folder as the .kpj file, move these files into an `external` subfolder

View file

@ -0,0 +1,37 @@
---
title: Clone Keyman Cloud Project Parameters Dialog
---
![Clone Keyman Cloud Project Parameters dialog](../images/ui/frmCloneKeymanCloudProjectParameters.png)
Allows you to clone a project from Keyman Cloud, giving the project a
new id, and organizing project files following the
[file layout](/developer/keyboards/) used in the
[Keyman keyboards repository](https://github.com/keymanapp/keyboards).
Projects can also be cloned from the command line with [`kmc copy`](kmc).
Only keyboard projects can be cloned from Keyman Cloud at this time via this
dialog. Lexical model projects can be cloned from Keyman Cloud via the command
line.
Search for a keyboard using the standard keyman.com keyboard search in the upper
pane. Once you select a keyboard, and fill in the New Project Details, the OK
button will be enabled to allow you to finish the cloning process.
### Parameters
Destination path
: Specifies the base path where the project folder will be created.
The project folder name will be the keyboard ID. If the folder
already exists, then you will be prompted before Keyman Developer
overwrites files inside it.
New project ID
: The base filename of the keyboard, project and package. This must
conform to the Keyman keyboard identifier rules.
Relocate external files into new project folder
: When selected, if the project references files that are not inside the same
folder as the .kpj file, move these files into an `external` subfolder

View file

@ -0,0 +1,34 @@
---
title: Clone Local Project Parameters Dialog
---
![Clone Local Project Parameters dialog](../images/ui/frmCloneLocalProjectParameters.png)
Allows you to clone a project from your local filesystem, giving the project a
new id, and organizing project files following the
[file layout](/developer/keyboards/) used in the
[Keyman keyboards repository](https://github.com/keymanapp/keyboards).
Keyboard and lexical model projects can be cloned.
Projects can also be cloned from the command line with [`kmc copy`](kmc).
### Parameters
Source project filename
: The full path of the source project .kpj file to copy.
Destination path
: Specifies the base path where the project folder will be created.
The project folder name will be the keyboard ID. If the folder
already exists, then you will be prompted before Keyman Developer
overwrites files inside it.
New project ID
: The base filename of the keyboard/model, project and package. This must
conform to the Keyman keyboard or lexical model identifier rules.
Relocate external files into new project folder
: When selected, if the project references files that are not inside the same
folder as the .kpj file, move these files into an `external` subfolder

View file

@ -21,4 +21,28 @@ Creates a keyboard project that matches the folder structure used in the Keyman
Creates a lexical model project that matches the folder structure used in the Keyman [lexical models repository](https://github.com/keymanapp/lexical-models) and includes all basic files needed for a lexical model project. Clicking OK will open the [New Lexical Model Project Parameters dialog](new-lm-project-parameters).
The **Basic** and **Import Windows Keyboard** modes can also be driven by command line with [KMConvert](kmconvert).
## Clone local project
Copies a Keyman keyboard or lexical model project on your local filesystem into
a new folder, giving the project and included files a new name. Clicking OK will
open the [Clone Local Project Parameters dialog](clone-local-project-parameters).
## Clone project from Keyman Cloud
Copies a Keyman keyboard or lexical model project from keyman.com into a new
folder, giving the project and included files a new name. Clicking OK will
open the [Clone Keyman Cloud Project Parameters dialog](clone-keyman-cloud-project-parameters).
## Clone project from GitHub
Copies a Keyman keyboard or lexical model project from a GitHub repository into
a new folder, giving the project and included files a new name. Clicking OK will
open the [Clone GitHub Project Parameters dialog](clone-github-project-parameters).
# Command line access
[`kmc generate`](kmc) can be used to generate new Keyman keyboards, LDML
keyboards, and Wordlist lexical models from the command line.
[KMConvert](kmconvert) can also be used to import Windows keyboards from the
command line.

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 9.6 KiB

View file

@ -1,5 +1,5 @@
---
title: Keyman Developer 18.0 User Guide
title: Keyman Developer 19.0 User Guide
---
Need help using Keyman Developer to create your keyboard layouts? You'll
@ -8,7 +8,7 @@ and tutorials, and full reference information.
## Guides and Tutorials
- [What is Keyman Developer?](guides/intro)
- [What's new in 18.0](whats-new)
- [What's new in 19.0](whats-new)
- [Developing Keyman keyboard layouts](guides/develop)
- [Testing Keyman keyboards](guides/test)
- [Distributing Keyman keyboards](guides/distribute)

View file

@ -26,7 +26,7 @@ configuration or installation is required.
kmc is also available as an npm package,
[@keymanapp/kmc](https://npmjs.com/package/@keymanapp/kmc).
You'll need [node.js](https://nodejs.org/), version 18.0 or later.
You'll need [node.js](https://nodejs.org/), version 20.0 or later.
```shell
npm install -g @keymanapp/kmc

View file

@ -90,14 +90,33 @@ The following parameters are available:
: Prints the version number of kmc
`--no-error-reporting`, `--error-reporting`
`--color`, `--no-color`
: Enable or disable error reporting to keyman.com, overriding [user
settings](../../user-settings). Error reporting is for fatal errors in the
compiler, and not errors in compiled files. No user data is sent in error
: Controls colorization for log messages, using ANSI color controls. If both of
these settings are omitted, kmc will attempt to detect from console, and will
use colorization for interactive terminals, and no colorization when
redirection is being used.
`--error-reporting`, `--no-error-reporting`
: Enable or disable error reporting to keyman.com, overriding
[user settings](../../user-settings). Error reporting is for fatal errors in
the compiler, and not errors in compiled files. No user data is sent in error
reports, although some filenames and paths may be present in the diagnostic
data attached to the report.
`--log-format <logFormat>`
: Output log format. The available options are:
* `formatted` (default): emits log messages in a human-readable format
* `tsv`: emits log messages in UTF-8 tab-separated format. This format will be
stable across versions of kmc. The format has the following fields:
* filename
* line number
* severity
* code
* message
`-l <logLevel>`, `--log-level <logLevel>`
: Controls the level of logging to console for messages relating to the
@ -108,9 +127,12 @@ The following parameters are available:
* `hint`: emits compilation errors, warnings, and hints
* `info` (default): emits compilation errors, warnings, hints, and
informational messages
* `verbose`: emits all compilation messages, including verbose
informational messages
* `debug`: emits all compilation messages, plus internal debug messages
Logging of specific [messages](messages) can be controlled with `--message`, `-m`.
Logging of specific [messages](messages) can be controlled with `--message`,
`-m` (`build` command only).
Note that when warnings are treated as errors
(`--compiler-warnings-as-errors`, `-w`), they will still be logged as
@ -118,13 +140,6 @@ The following parameters are available:
## `kmc build` options
`--color`, `--no-color`
: Controls colorization for log messages, using ANSI color controls. If both of
these settings are omitted, kmc will attempt to detect from console, and will
use colorization for interactive terminals, and no colorization when
redirection is being used.
`-d`, `--debug`
: Include debug information in output files. Debug information is used for
@ -167,17 +182,6 @@ The following parameters are available:
: Turns off warnings (CWARN_HeaderStatementIsDeprecated,
CWARN_LanguageHeadersDeprecatedInKeyman10) for deprecated code styles
`--log-format <logFormat>`
: Output log format. The available options are:
* `formatted` (default): emits log messages in a human-readable format
* `tsv`: emits log messages in UTF-8 tab-separated format. This format will be
stable across versions of kmc. The format has the following fields:
* filename
* line number
* severity
* code
* message
`-o <filename>`, `--out-file <filename>`
@ -509,6 +513,12 @@ following sources:
: Show what would happen, without making changes
`-r, --relocate-external`
: Copy referenced files that are outside the project folder into an 'external'
folder within the new project, rather than just referencing them in their
current location.
### File copying, renaming, and structure rules
The **origin** project folder is the one that contains the .kpj file. When a

View file

@ -1,43 +1,5 @@
---
title: What's new in Keyman Developer 18.0
title: What's new in Keyman Developer 19.0
---
Keyman Developer 17 has a number of significant changes:
* `kmcomp` has been removed and replaced with `kmc`. Learn more in our [kmcomp migration guide](reference/kmc/cli/kmcomp-migration).
* Windows installer packages should now be built with
`kmc build windows-package-installer` and can no longer be built within
the Keyman Developer IDE. More details on how to use this are in the
[`kmc` reference documentation](reference/kmc/cli/reference#toc-kmc-build-windows-package-installer-additional-options).
* `.keyboard_info` files are now generated entirely from the package and
keyboard files. Extra fields are available in packages for license file,
welcome file, typing examples, related packages (including deprecated
packages), and additional font files. Keyboards in the Keyman Cloud
keyboard repository have already been updated; if you are an author of
one of these keyboards, you should pull the changes from the repository
before submitting future updates.
* Keyboard project files (.kpj) now have a new format which does not list
individual files, but instead includes all files in the same folder and
subfolders relative to the project file. This prevents projects from
including unrelated files, and reduces the number of files that change
when keyboards are updated.
* Keyman Developer will now open each project in a new window, allowing
the user to rapidly switch between multiple projects at the same time.
* The Keyman Keyboard and Lexical Model Cloud repositories have new build
scripts which run `kmc`. These run much, much faster than previously!
* The [`&displayMap`](/developer/language/reference/displaymap) store allows
keyboard developers to specify a font mapping to PUA for the On Screen
Keyboard and Touch Layout to resolve diacritic rendering issues.
* Keyman Developer has been updated to support Unicode 15.1.
* Additional non-printing characters have been added to the Touch Layout Editor.
* Virtual keys in output of rules have never worked properly or been officially
supported; Keyman Developer will now warn if you attempt to use this pattern.
Keyman Developer 19 has the following significant changes:

View file

@ -156,6 +156,7 @@ function do_publish() {
./common/web/utils/build.sh publish $DRY_RUN $NPM_PUBLISH
../../common/web/keyman-version/build.sh publish $DRY_RUN $NPM_PUBLISH
../../common/web/langtags/build.sh publish $DRY_RUN $NPM_PUBLISH
../../common/web/types/build.sh publish $DRY_RUN $NPM_PUBLISH
../../core/include/ldml/build.sh publish $DRY_RUN $NPM_PUBLISH
# end TODO

View file

@ -4,7 +4,6 @@ interface
type
TLexicalModelUtils = class
private
public
const SPackageNameDoesNotFollowLexicalModelConventions_Message: string =
'The package file %0:s does not follow the recommended model filename conventions. The name should be all lower case, '+
@ -31,6 +30,8 @@ type
class function CleanLexicalModelIDComponent(const Name: string): string; static;
class function IsCleanLexicalModelIDComponent(const Name: string): Boolean; static;
class function IsValidLexicalModelID(ID: string; EnforceCaseAndWhitespace: Boolean): Boolean; static;
class function ExtractFileExt(filename: string): string; static;
class function RemoveFileExt(filename: string): string; static;
class function ChangeFileExt(filename, ext: string): string; static;
@ -51,11 +52,20 @@ const
// The model ID SHOULD adhere to this pattern (see also developer/src/kmc-model):
// author .bcp47 .uniq
MODEL_ID_PATTERN = '^[a-z_][a-z0-9_]*\.[a-z_][a-z0-9_-]*\.[a-z_][a-z0-9_]*$';
MODEL_ID_PATTERN_JS = '^[a-z_][a-z0-9_]*\.[a-z_][a-z0-9_-]*\.[a-z_][a-z0-9_]*\.model\.js$';
MODEL_ID_PATTERN_TS = '^[a-z_][a-z0-9_]*\.[a-z_][a-z0-9_-]*\.[a-z_][a-z0-9_]*\.model\.ts$';
MODEL_ID_PATTERN_PACKAGE = '^[a-z_][a-z0-9_]*\.[a-z_][a-z0-9_-]*\.[a-z_][a-z0-9_]*\.model\.(kps|kmp)$';
MODEL_ID_PATTERN_PROJECT = '^[a-z_][a-z0-9_]*\.[a-z_][a-z0-9_-]*\.[a-z_][a-z0-9_]*\.model\.kpj$';
class function TLexicalModelUtils.IsValidLexicalModelID(ID: string;
EnforceCaseAndWhitespace: Boolean): Boolean;
begin
if not EnforceCaseAndWhitespace then
ID := LowerCase(Trim(ID));
Result := TRegEx.IsMatch(ID, MODEL_ID_PATTERN);
end;
class function TLexicalModelUtils.DoesJSFilenameFollowLexicalModelConventions(
const Name: string): Boolean;
begin

View file

@ -1,4 +1,4 @@
import { CompilerError, CompilerErrorMask } from '@keymanapp/developer-utils';
import { CompilerError, CompilerErrorMask, CompilerEvent, dedentCompilerMessageDetail } from '@keymanapp/developer-utils';
import {assert, expect} from 'chai';
//
@ -37,8 +37,24 @@ export function verifyCompilerMessagesObject(source: Record<string,any>, namespa
const c = o[1].toUpperCase() + '_' + o[2];
expect(m[c]).to.be.a('number', `Expected constant name ${c} to exist`);
const v = m[key]('','','','','','','','','','','','' /* ignore arguments*/);
const v: CompilerEvent = m[key]('','','','','','','','','','','','' /* ignore arguments*/);
expect(v.code).to.equal(m[c], `Function ${key} returns the wrong code`);
assert.isNotNull(v.message, `Function ${key} returns an invalid message: '${v.message}'`);
assert.isNotEmpty(v.message, `Function ${key} returns an empty message: '${v.message}'`);
// Verify layout of compiler detail lines
if(v.detail) {
const detail = dedentCompilerMessageDetail(v).split('\n');
for(const line of detail) {
// Prevent lines over 80 characters -- we don't rewrap at this stage
// Don't check lines with URLs because they may be forced to go long
if(!line.includes('http')) {
assert.isAtMost(line.length, 80, `Function ${key} returns a message detail with at least one line longer than 80 characters: '${line}'`);
}
}
}
}
else if(typeof m[key] == 'number') {
const o = /^(DEBUG|VERBOSE|INFO|HINT|WARN|ERROR|FATAL)_([A-Za-z0-9_]+)$/.exec(key);

View file

@ -291,10 +291,8 @@ export interface CompilerMessageOverride {
level: CompilerErrorSeverityOverride;
};
export interface CompilerCallbackOptions {
logLevel?: CompilerLogLevel;
logFormat?: CompilerLogFormat;
color?: boolean; // null or undefined == use console default
export interface CompilerCallbackOptions extends CompilerBaseOptions {
// TODO: these overlap with CompilerOptions, should refactor
compilerWarningsAsErrors?: boolean;
messageOverrides?: CompilerMessageOverrideMap;
};
@ -401,6 +399,19 @@ export const CompilerMessageSpec = (code: number, message: string, detail?: stri
detail,
});
/**
* Remove initial whitespace from compiler detail messages, to enable
* indented formatting of message detail strings inside the message
* definitions
* @param event
* @returns dedented event detail
*/
export function dedentCompilerMessageDetail(event: CompilerEvent) {
// TODO(lowpri): dedent may be too naive -- should use least
// non-zero whitespace line as amount to dedent
return (event.detail ?? '').replace(/^[ ]+/gm, '');
}
export const CompilerMessageDef = (param: any) => String(param ?? `<param>`);
export const CompilerMessageSpecWithException = (code: number, message: string, exceptionVar: any, detail?: string) : CompilerEvent => ({

View file

@ -52,7 +52,7 @@ export { defaultCompilerOptions, CompilerBaseOptions, CompilerOptions, CompilerE
ALL_COMPILER_LOG_FORMATS, CompilerLogFormat,
CompilerMessageOverride,
CompilerMessageOverrideMap,
dedentCompilerMessageDetail,
KeymanCompilerArtifact,
KeymanCompilerArtifactOptional,
KeymanCompilerArtifacts,

View file

@ -231,6 +231,10 @@ export class LDMLKeyboardXMLSourceFileReader {
this.callbacks.reportMessage(CommonTypesMessages.Error_ImportInvalidPath({base, path, subtag}));
return false;
}
if (constants.treatAsLatest(paths[0])) {
/** There's no data or DTD change in 45, 46, 46.1, 47 so map them all to 46 at present. */
paths[0] = constants.cldr_version_latest;
}
importData = this.readImportFile(paths[0], paths[1]);
} else {
// local import

View file

@ -64,11 +64,17 @@ export class KeymanSentry {
}
}
static async captureException(e: any): Promise<never> {
/**
* capture an exception for Sentry; note that in local environments, this will normally
* throw the exception rather than sending to Sentry
* @param e
* @param force if true, reports to Sentry even in local environments
*/
static async captureException(e: any, force?: boolean): Promise<never> {
if(isInit) {
// For local development, we don't want to bury the trace; we need the cast to avoid
// TS2367 (comparison appears to be unintentional)
if((KEYMAN_VERSION.VERSION_ENVIRONMENT as string) == 'local') {
if(!force && (KEYMAN_VERSION.VERSION_ENVIRONMENT as string) == 'local') {
throw e;
}

View file

@ -2,6 +2,6 @@
<keyboard3 xmlns="https://schemas.unicode.org/cldr/45/keyboard3" locale="und" conformsTo="45">
<info author="srl295" indicator="🙀" layout="qwerty" name="Test Minimal Keyboard"/>
<keys>
<import base="" path="keys-Zyyy-morepunctuation.xml"/>
<import path="keys-Zyyy-morepunctuation.xml"/>
</keys>
</keyboard3>

View file

@ -2,6 +2,6 @@
<keyboard3 xmlns="https://schemas.unicode.org/cldr/45/keyboard3" locale="und" conformsTo="45">
<info author="srl295" indicator="🙀" layout="qwerty" name="Test Minimal Keyboard"/>
<keys>
<import base="" path="keys-Zyyy-DOESNOTEXIST.xml"/>
<import path="keys-Zyyy-DOESNOTEXIST.xml"/>
</keys>
</keyboard3>

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE keyboardTest3 SYSTEM "../../../../../resources/standards-data/ldml-keyboards/45/dtd/ldmlKeyboardTest3.dtd">
<!DOCTYPE keyboardTest3 SYSTEM "../../../../../resources/standards-data/ldml-keyboards/46/dtd/ldmlKeyboardTest3.dtd">
<keyboardTest3 conformsTo="techpreview">
<!--
Read by:

View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Note: similar to, but slightly ahead of, fr-t-k0-azerty -->
<!DOCTYPE keyboardTest3 SYSTEM "../../../../../resources/standards-data/ldml-keyboards/45/dtd/ldmlKeyboardTest3.dtd">
<!DOCTYPE keyboardTest3 SYSTEM "../../../../../resources/standards-data/ldml-keyboards/46/dtd/ldmlKeyboardTest3.dtd">
<keyboardTest3 conformsTo="techpreview">
<info keyboard="k_020_fr.xml" author="Team Keyboard" name="fr-test-updated" />
<repertoire name="simple-repertoire" chars="[a b c d e \u0022]" type="simple" /> <!-- verify that these outputs are all available from simple keys on any layer, for all form factors -->

View file

@ -32,7 +32,7 @@ describe('ldml keyboard xml reader tests', function () {
instancePath: '/keyboard3/conformsTo',
keyword: 'enum',
message: `must be equal to one of the allowed values`,
params: 'allowedValues="45"',
params: 'allowedValues="45,46"', // this has to be kept in sync with the DTD
})],
},
{

View file

@ -59,7 +59,7 @@
<Directory Id="dirNode" Name="node.js" />
<Directory Id="dirProjects" Name="Projects" />
<Directory Id="dir_import" Name="import">
<Directory Id="dir_import_45" Name="45" />
<Directory Id="dir_import_46" Name="46" />
</Directory>
</Directory>
</Directory>
@ -198,7 +198,7 @@
</ComponentGroup>
<!-- LDML Imports -->
<ComponentGroup Id="LdmlImports" Directory="dir_import_45" Source="..\..\..\resources\standards-data\ldml-keyboards\45\import">
<ComponentGroup Id="LdmlImports" Directory="dir_import_46" Source="..\..\..\resources\standards-data\ldml-keyboards\46\import">
<Component>
<File Id="LdmlImportReadme" Name="README.md" KeyPath="yes" />
</Component>

View file

@ -1,6 +1,6 @@
# Bundled node.js
We are bundling node 18.14.1 here as of Keyman 17.0.52.
We are bundling node 20.16.1 here as of Keyman 18.0.156-alpha
Note that if we change the node version, we will need to
rebuild the binary modules in /developer/src/server/src/win32/

View file

@ -4,7 +4,7 @@
* Copy a keyboard or lexical model project
*/
import { CloudUrls, GitHubUrls, CompilerCallbacks, CompilerLogLevel, KeymanCompiler, KeymanCompilerArtifact, KeymanCompilerArtifacts, KeymanCompilerResult, KeymanDeveloperProject, KeymanDeveloperProjectOptions, KPJFileReader, KPJFileWriter, KpsFileReader, KpsFileWriter, ValidIds } from "@keymanapp/developer-utils";
import { CloudUrls, GitHubUrls, CompilerCallbacks, KeymanCompiler, KeymanCompilerArtifact, KeymanCompilerArtifacts, KeymanCompilerResult, KeymanDeveloperProject, KeymanDeveloperProjectOptions, KPJFileReader, KPJFileWriter, KpsFileReader, KpsFileWriter, ValidIds, CompilerBaseOptions } from "@keymanapp/developer-utils";
import { KeymanFileTypes } from "@keymanapp/common-types";
import { CopierMessages } from "./copier-messages.js";
@ -19,12 +19,7 @@ type CopierFunction = (
* @public
* Options for the Keyman Developer project copier
*/
export interface CopierOptions /* not inheriting from CompilerBaseOptions */ {
/**
* Reporting level to console, used by NodeCompilerCallbacks (not used in
* compiler modules; all messages are still reported to the internal log)
*/
logLevel?: CompilerLogLevel;
export interface CopierOptions extends CompilerBaseOptions {
/**
* output path where project folder will be created
*/
@ -33,6 +28,12 @@ export interface CopierOptions /* not inheriting from CompilerBaseOptions */ {
* dryRun: show what would happen
*/
dryRun: boolean;
/**
* Copy referenced files that are outside the project folder into an
* 'external' folder within the new project, rather than just referencing them
* in their current location
*/
relocateExternalFiles?: boolean;
};
/**
@ -78,7 +79,6 @@ export class KeymanProjectCopier implements KeymanCompiler {
isLocalOrigin(): boolean {
return this.githubRef == undefined;
}
relocateExternalFiles: boolean = false; // TODO-COPY: support
public async init(callbacks: CompilerCallbacks, options: CopierOptions): Promise<boolean> {
if(!callbacks || !options) {
@ -357,6 +357,7 @@ export class KeymanProjectCopier implements KeymanCompiler {
const subFilename = this.normalizePath(this.resolveFilename(project.projectFilename, normalizedFilePath));
const copier = this.copiers[<KeymanFileTypes.Source> file.fileType] ?? this.copyGenericFile.bind(this);
// Ignore errors because we will continue to do a best effort
this.callbacks.reportMessage(CopierMessages.Verbose_CopyingFile({from: normalizedFilePath, to: subFilename}));
await copier(project, subFilename, subOutputPath, source, result);
}
@ -559,20 +560,25 @@ export class KeymanProjectCopier implements KeymanCompiler {
let subOutputPath: string;
if(this.callbacks.path.isAbsolute(subFilenameRelative) || subFilenameRelative.startsWith('..')) {
if(this.isLocalOrigin() && !this.relocateExternalFiles) {
if(this.isLocalOrigin() && !this.options.relocateExternalFiles) {
// Reference outside the project structure, do not attempt to normalize or copy,
// but we do need to update references for the new output path
return this.normalizePath(this.callbacks.path.relative(result.artifacts[parentFilename].filename, subFilenameAbsolute));
const destinationFilename =
this.normalizePath(this.callbacks.path.relative(result.artifacts[parentFilename].filename, subFilenameAbsolute));
this.callbacks.reportMessage(CopierMessages.Verbose_CopyingFile({from: originalSubfilename, to: destinationFilename}));
return destinationFilename;
} else {
// Reference outside the project structure, we will copy into a subfolder of the project;
// should be relative to root of remote repo, e.g. release/k/khmer_angkor references release/shared/fonts/...,
// so should go external/release/shared/fonts/...
subOutputPath = this.normalizePath(this.callbacks.path.join(this.options.outPath, 'external', this.callbacks.path.dirname(subFilenameAbsolute)));
const relativePath = this.callbacks.path.dirname(subFilenameRelative).replace(/\.\.[/\\]/g, '');
subOutputPath = this.normalizePath(this.callbacks.path.join(this.options.outPath, 'external', relativePath));
}
} else {
subOutputPath = this.normalizePath(this.callbacks.path.join(outputPath, this.callbacks.path.dirname(originalSubfilename)));
}
this.callbacks.reportMessage(CopierMessages.Verbose_CopyingFile({from: originalSubfilename, to: subFilename}));
await this.copyGenericFile(project, subFilename, subOutputPath, source, result);
// Even if the subfile is missing, we'll still continue the overall copy

View file

@ -19,9 +19,10 @@ const SevFatal = CompilerErrorSeverity.Fatal | Namespace;
*/
export class CopierMessages {
static FATAL_UnexpectedException = SevFatal | 0x0001;
static Fatal_UnexpectedException = (o:{e: any}) => m(
static Fatal_UnexpectedException = (o:{e: any}) => CompilerMessageSpecWithException(
this.FATAL_UnexpectedException,
null, o.e ?? 'unknown error'
null,
o.e ?? 'unknown error'
);
static INFO_CopyingProject = SevInfo | 0x0002;
@ -123,81 +124,90 @@ export class CopierMessages {
static ERROR_CannotDownloadFolderFromGitHub = SevError | 0x0012;
static Error_CannotDownloadFolderFromGitHub = (o:{ref: string, message?: string, cause?: string}) => m(
this.ERROR_CannotDownloadFolderFromGitHub,
`The folder '${def(o.ref)}' could not be downloaded: ${def(o.message)} ${def(o.cause)}`,
`An error was encountered attempting to download a folder from GitHub API. Check the
provided error details for the cause.`
);
`The folder '${def(o.ref)}' could not be downloaded: ${def(o.message)} ${def(o.cause)}`, `
An error was encountered attempting to download a folder from GitHub API. Check
the provided error details for the cause.
`);
static ERROR_FolderDownloadedFromGitHubIsNotAValidFolder = SevError | 0x0013;
static Error_FolderDownloadedFromGitHubIsNotAValidFolder = (o:{ref: string}) => m(
this.ERROR_FolderDownloadedFromGitHubIsNotAValidFolder,
`The path '${def(o.ref)}' does not appear to be a folder on GitHub`,
`The provided path may be a file or may not exist. Check the reference
before trying again.`
);
`The path '${def(o.ref)}' does not appear to be a folder on GitHub`, `
The provided path may be a file or may not exist. Check the reference
before trying again.
`);
static WARN_CannotDownloadFileFromGitHub = SevWarn | 0x0014;
static Warn_CannotDownloadFileFromGitHub = (o:{ref: string, message?: string, cause?: string}) => m(
this.WARN_CannotDownloadFileFromGitHub,
`The file '${def(o.ref)}' could not be downloaded: ${def(o.message)} ${def(o.cause)}`,
`An error was encountered attempting to download a file from GitHub. Check the
provided error details for the cause.`
);
`The file '${def(o.ref)}' could not be downloaded: ${def(o.message)} ${def(o.cause)}`, `
An error was encountered attempting to download a file from GitHub. Check the
provided error details for the cause.
`);
static ERROR_InvalidCloudKeyboardId = SevError | 0x0015;
static Error_InvalidCloudKeyboardId = (o:{id: string}) => m(
this.ERROR_InvalidCloudKeyboardId,
`The keyboard identifier '${def(o.id)}' is not a valid keyboard identifier`,
`Keyboard identifiers on Keyman Cloud can only use the characters a-z, 0-9, and _.`
);
`The keyboard identifier '${def(o.id)}' is not a valid keyboard identifier`, `
Keyboard identifiers on Keyman Cloud can only use the characters
\`a\`-\`z\`, \`0\`-\`9\`, and \`_\`.
`);
static ERROR_CouldNotRetrieveFromCloud = SevError | 0x0016;
static Error_CouldNotRetrieveFromCloud = (o:{id: string, message?: string, cause?: string}) => m(
this.ERROR_CouldNotRetrieveFromCloud,
`Details for keyboard or model identified by '${def(o.id)}' could not be downloaded: ${def(o.message)} ${def(o.cause)}`,
`An error was encountered attempting to retrieve keyboard or model details from Keyman Cloud API. Check the
provided error details for the cause.`
);
`Details for keyboard or model identified by '${def(o.id)}' could not be `+
`downloaded: ${def(o.message)} ${def(o.cause)}`, `
An error was encountered attempting to retrieve keyboard or model details from
Keyman Cloud API. Check the provided error details for the cause.
`);
static ERROR_KeymanCloudReturnedInvalidData = SevError | 0x0017;
static Error_KeymanCloudReturnedInvalidData = (o:{id: string}) => m(
this.ERROR_KeymanCloudReturnedInvalidData,
`Keyman Cloud API returned invalid data for keyboard or model identified by '${def(o.id)}'`,
`There may be a network error or a server error. Retry your request later or contact
Keyman Support for assistance.`
);
`Keyman Cloud API returned invalid data for keyboard or model identified by '${def(o.id)}'`, `
There may be a network error or a server error. Retry your request later or
contact Keyman Support for assistance.
`);
static ERROR_CloudDoesNotHaveSource = SevError | 0x0018;
static Error_CloudDoesNotHaveSource = (o:{id: string}) => m(
this.ERROR_CloudDoesNotHaveSource,
`The keyboard or model identified by '${def(o.id)}' does not have source available`,
`Legacy keyboards in Keyman Cloud do not have source available. Check the Keyman keyboard catalog
at keyman.com for further details. Some new keyboards or models may be available as binary-only.`
);
`The keyboard or model identified by '${def(o.id)}' does not have source available`, `
Legacy keyboards in Keyman Cloud do not have source available. Check the Keyman
keyboard catalog at https://keyman.com/keyboards for further details. Some new
keyboards or models may be available as binary-only.
`);
static ERROR_CannotDownloadRepoFromGitHub = SevError | 0x0019 ;
static Error_CannotDownloadRepoFromGitHub = (o:{ref: string, message?: string, cause?: string}) => m(
this.ERROR_CannotDownloadRepoFromGitHub,
`The repository at '${def(o.ref)}' could not be accessed: ${def(o.message)} ${def(o.cause)}`,
`An error was encountered attempting to download details about a repository from GitHub API.
Check the provided error details for the cause.`
);
`The repository at '${def(o.ref)}' could not be accessed: ${def(o.message)} ${def(o.cause)}`, `
An error was encountered attempting to download details about a repository
from GitHub API. Check the provided error details for the cause.
`);
//------------------------------------------------------------------------------|
// max length of detail message lines (checked by verifyCompilerMessagesObject) |
//------------------------------------------------------------------------------|
static ERROR_CouldNotFindDefaultBranchOnGitHub = SevError | 0x001A;
static Error_CouldNotFindDefaultBranchOnGitHub = (o:{ref: string}) => m(
this.ERROR_CouldNotFindDefaultBranchOnGitHub,
`The default branch could not be found for the GitHub repository '${def(o.ref)}'`,
`The repository may be private, or you may have a typo in the owner or repository name.`
);
`The default branch could not be found for the GitHub repository '${def(o.ref)}'`, `
The repository may be private, or you may have a typo in the owner or
repository name.
`);
static INFO_CannotDownloadBinaryFileFromGitHub = SevInfo | 0x001B;
static Info_CannotDownloadBinaryFileFromGitHub = (o:{ref: string, message?: string, cause?: string}) => m(
this.INFO_CannotDownloadBinaryFileFromGitHub,
`The Keyman binary file '${def(o.ref)}' could not be downloaded: ${def(o.message)} ${def(o.cause)}. This is not normally a problem`,
`In most repositories, Keyman binary files such as .kmx, .kmp, .js are not included.
This is not normally a problem, as the files can be built from the source. Check
the provided error details for more details.`
);
`The Keyman binary file '${def(o.ref)}' could not be downloaded: ${def(o.message)} `+
`${def(o.cause)}. This is not normally a problem`, `
In most repositories, Keyman binary files such as .kmx, .kmp, .js are not
included. This is not normally a problem, as the files can be built from the
source. Check the provided error details for more details.
`);
static VERBOSE_DownloadingFile = SevVerbose | 0x001C;
static Verbose_DownloadingFile = (o:{filename: string, url: string}) => m(
@ -231,4 +241,10 @@ export class CopierMessages {
`The output file '${def(o.filename)}' has two different possible source files.`,
);
static VERBOSE_CopyingFile = SevVerbose | 0x0021;
static Verbose_CopyingFile = (o:{from: string, to: string}) => m(
this.VERBOSE_CopyingFile,
`Copying file '${def(o.from)}' to '${def(o.to)}'`,
);
};

View file

@ -13,6 +13,7 @@ import { TestCompilerCallbacks } from '@keymanapp/developer-test-helpers';
import { KeymanProjectCopier } from '../src/KeymanProjectCopier.js';
import { makePathToFixture } from './helpers/index.js';
import { GitHubRef } from './cloud.js';
import { KpsFileReader } from '@keymanapp/developer-utils';
const { TEST_SAVE_ARTIFACTS, TEST_SAVE_FIXTURES } = env;
let outputRoot: string = '/an/imaginary/root/';
@ -269,8 +270,8 @@ describe('KeymanProjectCopier', function() {
"/release/j/jawa/source/jawa2.bmp": "source/jawa2.bmp",
"/release/j/jawa/source/readme.htm": "source/readme.htm",
"/release/j/jawa/source/welcome/welcome.htm": "source/welcome/welcome.htm",
"/release/shared/fonts/noto/Java/NotoSansJavanese-Bold.ttf": "external/release/shared/fonts/noto/Java/NotoSansJavanese-Bold.ttf",
"/release/shared/fonts/noto/Java/NotoSansJavanese-Regular.ttf": "external/release/shared/fonts/noto/Java/NotoSansJavanese-Regular.ttf",
"/release/shared/fonts/noto/Java/NotoSansJavanese-Bold.ttf": "external/shared/fonts/noto/Java/NotoSansJavanese-Bold.ttf",
"/release/shared/fonts/noto/Java/NotoSansJavanese-Regular.ttf": "external/shared/fonts/noto/Java/NotoSansJavanese-Regular.ttf",
// NOTE: the following files are not included because this is a 1.0
// project and they are not referenced by any files:
// * HISTORY.md
@ -523,6 +524,103 @@ describe('KeymanProjectCopier', function() {
]);
});
it('should copy external files into an "external" folder when relocateExternalFiles is true', async function() {
const copier = new KeymanProjectCopier();
assert.isTrue(await copier.init(callbacks, {
dryRun: false,
outPath: 'organized',
relocateExternalFiles: true,
}));
const result = await copier.run(makePathToFixture('projects','external','alephwithbeth','alephwithbeth.kpj'));
assert.sameMembers(Object.keys(result.artifacts).map(k => result.artifacts[k].filename), [
'organized', // kmc-copy:outputPath
'organized/source/organized.keyman-touch-layout',
'organized/source/organized.kmn',
'organized/source/organized.ico',
'organized/source/organized.kvks',
'organized/source/organized.kps',
'organized/source/readme.htm',
// files in 'another-folder/' get moved to 'external/'
'organized/external/another-folder/welcome/welcome.htm',
'organized/external/another-folder/welcome/KeyboardLayout.png',
'organized/external/another-folder/welcome/KeyboardLayoutAlt.png',
'organized/external/another-folder/welcome/KeyboardLayoutShift.png',
// build artifact files get moved to build/
'organized/build/organized.js',
'organized/build/organized.kvk',
'organized/build/organized.kmx',
// 'organized/build/organized.kmp', // TODO-COPY: do we need to copy this file as well?
'organized/LICENSE.md',
'organized/organized.kpj',
'organized/HISTORY.md',
'organized/README.md',
'organized/source/help/organized.php',
'organized/source/help/KeyboardLayout.png',
'organized/source/help/KeyboardLayoutAlt.png',
'organized/source/help/KeyboardLayoutShift.png',
]);
// Verify that .kps has the appropriate paths
const reader = new KpsFileReader(callbacks);
const kpsFilename = Object.keys(result.artifacts).find(k => result.artifacts[k].filename == 'organized/source/organized.kps')!;
const kps = reader.read(result.artifacts[kpsFilename].data);
assert.isDefined(kps.Package.Files?.File.find(file => file.Name == '../external/another-folder/welcome/welcome.htm'));
assert.isDefined(kps.Package.Files?.File.find(file => file.Name == '../external/another-folder/welcome/KeyboardLayout.png'));
assert.isDefined(kps.Package.Files?.File.find(file => file.Name == '../external/another-folder/welcome/KeyboardLayoutAlt.png'));
assert.isDefined(kps.Package.Files?.File.find(file => file.Name == '../external/another-folder/welcome/KeyboardLayoutShift.png'));
});
it('should leave external files in their original location when relocateExternalFiles is false', async function() {
const copier = new KeymanProjectCopier();
assert.isTrue(await copier.init(callbacks, {
dryRun: false,
outPath: 'organized',
relocateExternalFiles: false,
}));
const result = await copier.run(makePathToFixture('projects','external','alephwithbeth','alephwithbeth.kpj'));
assert.sameMembers(Object.keys(result.artifacts).map(k => result.artifacts[k].filename), [
'organized', // kmc-copy:outputPath
'organized/source/organized.keyman-touch-layout',
'organized/source/organized.kmn',
'organized/source/organized.ico',
'organized/source/organized.kvks',
'organized/source/organized.kps',
'organized/source/readme.htm',
// files in 'another-folder/' stay there, so they are not referenced
// 'organized/external/welcome.htm',
// 'organized/external/KeyboardLayout.png',
// 'organized/external/KeyboardLayoutAlt.png',
// 'organized/external/KeyboardLayoutShift.png',
// build artifact files get moved to build/
'organized/build/organized.js',
'organized/build/organized.kvk',
'organized/build/organized.kmx',
// 'organized/build/organized.kmp', // TODO-COPY: do we need to copy this file as well?
'organized/LICENSE.md',
'organized/organized.kpj',
'organized/HISTORY.md',
'organized/README.md',
'organized/source/help/organized.php',
'organized/source/help/KeyboardLayout.png',
'organized/source/help/KeyboardLayoutAlt.png',
'organized/source/help/KeyboardLayoutShift.png',
]);
// we'll check the .kps for its references, these are relative to the test/organized/source? folder
const reader = new KpsFileReader(callbacks);
const kpsFilename = Object.keys(result.artifacts).find(k => result.artifacts[k].filename == 'organized/source/organized.kps')!;
const kps = reader.read(result.artifacts[kpsFilename].data);
assert.isDefined(kps.Package.Files?.File.find(file => file.Name == '../../../test/fixtures/projects/external/another-folder/welcome/welcome.htm'));
assert.isDefined(kps.Package.Files?.File.find(file => file.Name == '../../../test/fixtures/projects/external/another-folder/welcome/KeyboardLayout.png'));
assert.isDefined(kps.Package.Files?.File.find(file => file.Name == '../../../test/fixtures/projects/external/another-folder/welcome/KeyboardLayoutAlt.png'));
assert.isDefined(kps.Package.Files?.File.find(file => file.Name == '../../../test/fixtures/projects/external/another-folder/welcome/KeyboardLayoutShift.png'));
});
// TODO-COPY: additional tests
it.skip('should copy a standalone .kmn into a new project', async function() {});
it.skip('should copy a standalone .kmn and .kps into a new project', async function() {});

View file

@ -0,0 +1,35 @@
alephwithbeth Change History
====================
1.0.6 (2024-06-11)
------------------
* Remove source files from package
1.0.5 (2023-02-28)
------------------
* replaced hyphen with maqef
* added a touch-optimized keyboard layout for phone
1.0.4 (2023-02-13)
------------------
* Removed unnecessary version numbers.
* added description
1.0.3 (2023-02-11)
------------------
* Removed the faulty auto correct feature.
* Captialized all words of the title.
* Added title to .kmn
1.0.2 (2023-02-09)
------------------
* Renamed keyboard to "Aleph with Beth"
1.0.1 (2023-02-08)
----------------
* Updated author and website (in kps)
* Added copyrights where needed.
1.0 (2023-01-28)
----------------
* Created by Cameron Briggs

View file

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright © 2023-2024 Cameron Briggs
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,25 @@
alephwithbeth keyboard
==============
Description
-----------
A Hebrew keyboard designed to intuitively match the standard QWERTY layout.
Copyright
---------
See [LICENSE.md](LICENSE.md)
Supported Platforms
-------------------
* Windows
* macOS
* Linux
* Web
* iPhone
* iPad
* Android phone
* Android tablet
* Mobile devices
* Desktop devices
* Tablet devices

View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<KeymanDeveloperProject>
<Options>
<Version>2.0</Version>
<BuildPath>$PROJECTPATH\build</BuildPath>
<CompilerWarningsAsErrors>True</CompilerWarningsAsErrors>
<CheckFilenameConventions>True</CheckFilenameConventions>
</Options>
</KeymanDeveloperProject>

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,40 @@
{
"id": "alephwithbeth",
"name": "Aleph With Beth",
"license": "mit",
"isRTL": true,
"authorName": "Cameron Briggs",
"description": "<p>alephwithbeth keyboard</p>",
"languages": {
"he": {
"examples": [],
"languageName": "Hebrew",
"displayName": "Hebrew"
}
},
"lastModifiedDate": "2025-01-27T23:04:56.765Z",
"packageFilename": "alephwithbeth.kmp",
"packageFileSize": 244476,
"jsFilename": "alephwithbeth.js",
"jsFileSize": 23534,
"packageIncludes": [
"documentation",
"welcome",
"visualKeyboard"
],
"version": "1.0.6",
"encodings": [
"unicode"
],
"platformSupport": {
"windows": "full",
"macos": "full",
"linux": "full",
"desktopWeb": "full",
"ios": "full",
"android": "full",
"mobileWeb": "full"
},
"minKeymanVersion": "14.0",
"helpLink": "https://help.keyman.com/keyboard/alephwithbeth"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

@ -0,0 +1,215 @@
store(&TARGETS) 'any'
store(&NAME) 'Aleph With Beth'
store(&BITMAP) 'alephwithbeth.ico'
store(&VISUALKEYBOARD) 'alephwithbeth.kvks'
store(letters-with-final-forms) "כמנפצ"
store(sofit-forms-of-letters) "ךםןףץ"
store(presentation-dageshed-letters-with-final-forms) "כּפּ"
store(presentation-dageshed-sofit-forms-of-letters) "ךּףּ"
store(dagesh) 'ּ'
c store(vowels) "ְֱֲֳִֵֶַׇׇָֹֻׅ"
store(normal-dageshed-letters-with-final-forms) "מּנּצּ"
store(normal-undageshed-sofit-forms-of-letters) "םןץ"
store(punctuation) U+0591 .. U+05AF U+05BD .. U+05BF U+05C0 U+05C4
store(whitespace) [K_SPACE] [K_ENTER] [K_TAB]
store(&KMW_RTL) '1'
store(&LAYOUTFILE) 'alephwithbeth.keyman-touch-layout'
store(&COPYRIGHT) '© Cameron Briggs'
store(&KEYBOARDVERSION) '1.0.6'
store(&KMW_HELPTEXT) 'Aleph With Beth'
begin Unicode > use(main)
group(main) using keys
+ [SHIFT K_I] > nul
+ [ALT K_BKQUOTE] > nul
+ [ALT K_B] > nul
+ [ALT K_V] > nul
+ [ALT K_C] > nul
+ [ALT K_Z] > nul
+ [ALT K_L] > nul
+ [ALT K_J] > nul
+ [ALT K_F] > nul
+ [ALT K_D] > nul
+ [ALT K_S] > nul
+ [ALT K_U] > nul
+ [ALT K_Y] > nul
+ [ALT K_T] > nul
+ [ALT K_R] > nul
+ [ALT K_W] > nul
+ [ALT K_Q] > nul
+ [SHIFT K_BKSLASH] > '|'
+ [SHIFT K_EQUAL] > '+'
+ [SHIFT K_HYPHEN] > '_'
+ [SHIFT K_0] > ')'
+ [SHIFT K_9] > '('
+ [SHIFT K_SLASH] > '?'
+ [SHIFT K_QUOTE] > '"'
+ [SHIFT K_BKQUOTE] > nul
+ [K_EQUAL] > '='
+ [K_HYPHEN] > '־'
+ [K_0] > '0'
+ [K_9] > '9'
+ [K_8] > '8'
+ [K_7] > '7'
+ [K_6] > '6'
+ [K_5] > '5'
+ [K_4] > '4'
+ [K_3] > '3'
+ [K_2] > '2'
+ [K_1] > '1'
+ [K_PERIOD] > '.'
+ [K_SLASH] > '/'
+ [K_QUOTE] > U+0027
+ [SHIFT K_COLON] > '״'
+ [ALT K_A] > 'ֲ'
+ [SHIFT K_RBRKT] > '}'
+ [SHIFT K_LBRKT] > '{'
+ [K_BKSLASH] > '\'
+ [K_RBRKT] > ']'
+ [K_LBRKT] > '['
+ [SHIFT K_R] > 'רּ'
+ [SHIFT CTRL K_SPACE] > ' '
+ [SHIFT K_SPACE] > ' '
+ [K_SPACE] > ' '
+ [ALT K_SPACE] > ' '
+ [SHIFT K_8] > '֝'
+ [SHIFT K_7] > '֬'
+ [SHIFT K_6] > '֔'
+ [SHIFT K_5] > '֞'
+ [SHIFT K_4] > '֜'
+ [SHIFT K_3] > '֨'
+ [SHIFT K_2] > '֘'
+ [SHIFT K_1] > '!'
+ [ALT K_SLASH] > '״'
+ [ALT K_PERIOD] > '̈'
+ [ALT K_COMMA] > '֫'
+ [ALT K_QUOTE] > 'ׄ'
+ [ALT K_COLON] > '׃'
+ [ALT K_H] > '̊'
+ [ALT K_G] > '•'
+ [ALT K_BKSLASH] > '֫'
+ [ALT K_RBRKT] > '֒'
+ [ALT K_LBRKT] > '֔'
+ [ALT K_I] > 'ֽ'
+ [ALT K_O] > 'ֳ'
+ [ALT K_E] > 'ֱ'
+ [ALT K_EQUAL] > '◌'
+ [ALT K_HYPHEN] > 'ֿ'
+ [ALT K_0] > '֯'
+ [ALT K_9] > '֓'
+ [ALT K_8] > '֕'
+ [ALT K_7] > '֡'
+ [ALT K_6] > '֟'
+ [ALT K_5] > '֩'
+ [ALT K_4] > '֠'
+ [ALT K_3] > '֙'
+ [ALT K_2] > '֮'
+ [ALT K_1] > '֗'
+ [ALT K_N] > 'ן'
+ [ALT K_P] > 'ף'
+ [ALT K_K] > 'ך'
+ [ALT K_M] > 'ם'
+ [ALT K_X] > 'ץ'
+ [SHIFT K_PERIOD] > 'ּ'
+ [SHIFT K_COMMA] > nul
+ [SHIFT K_M] > 'מּ'
+ [SHIFT K_N] > 'נּ'
+ [SHIFT K_B] > 'בּ'
+ [SHIFT K_V] > 'שּׁ'
+ [SHIFT K_C] > 'שּׂ'
+ [SHIFT K_X] > 'צּ'
+ [SHIFT K_Z] > 'זּ'
+ [SHIFT K_L] > 'לּ'
+ [SHIFT K_K] > 'כּ'
+ [SHIFT K_J] > 'וַיּ'
+ [SHIFT K_H] > 'הּ'
+ [SHIFT K_G] > 'גּ'
+ [SHIFT K_F] > 'טּ'
+ [SHIFT K_D] > 'דּ'
+ [SHIFT K_S] > 'סּ'
+ [SHIFT K_A] > 'ָ'
+ [SHIFT K_P] > 'פּ'
+ [SHIFT K_O] > 'וֹ'
+ [SHIFT K_U] > 'ֻ'
+ [SHIFT K_Y] > 'יּ'
+ [SHIFT K_T] > 'תּ'
+ [SHIFT K_E] > 'ֵ'
+ [SHIFT K_W] > 'וּ'
+ [SHIFT K_Q] > 'קּ'
+ [K_BKQUOTE] > '֑'
+ [K_COMMA] > 'א'
+ [K_M] > 'מ'
+ [K_N] > 'נ'
+ [K_B] > 'ב'
+ [K_V] > 'שׁ'
+ [K_C] > 'שׂ'
+ [K_X] > 'צ'
+ [K_Z] > 'ז'
+ [K_COLON] > 'ְ'
+ [K_L] > 'ל'
+ [K_K] > 'כ'
+ [K_J] > 'ח'
+ [K_H] > 'ה'
+ [K_G] > 'ג'
+ [K_F] > 'ט'
+ [K_D] > 'ד'
+ [K_S] > 'ס'
+ [K_A] > 'ַ'
+ [K_P] > 'פ'
+ [K_O] > 'ֹ'
+ [K_I] > 'ִ'
+ [K_U] > 'ע'
+ [K_Y] > 'י'
+ [K_T] > 'ת'
+ [K_R] > 'ר'
+ [K_E] > 'ֶ'
+ [K_W] > 'ו'
+ [K_Q] > 'ק'
c Transform final letters
any(letters-with-final-forms) + any(whitespace) > index(sofit-forms-of-letters,1)
c Transform final letters with a dagesh
any(letters-with-final-forms) $dagesh + any(whitespace) > index(sofit-forms-of-letters,1) $dagesh
any(presentation-dageshed-letters-with-final-forms) + any(whitespace) > index(presentation-dageshed-sofit-forms-of-letters,1)
any(normal-dageshed-letters-with-final-forms) + any(whitespace) > index(normal-undageshed-sofit-forms-of-letters,1) $dagesh
c Transform final letters with a vowel
c any(letters-with-final-forms) any(vowels) + any(whitespace) > index(sofit-forms-of-letters,1) index(vowels,2)
c Transform final letters with a vowel and a dagesh
c any(letters-with-final-forms) any(vowels) $dagesh + any(whitespace) > index(sofit-forms-of-letters,1) index(vowels,2) $dagesh
c any(letters-with-final-forms) $dagesh any(vowels) + any(whitespace) > index(sofit-forms-of-letters,1) $dagesh index(vowels,3)
c any(presentation-dageshed-letters-with-final-forms) any(vowels) + any(whitespace) > index(presentation-dageshed-sofit-forms-of-letters,1) index(vowels,2)
c any(normal-dageshed-letters-with-final-forms) any(vowels) + any(whitespace) > index(normal-undageshed-sofit-forms-of-letters,1) $dagesh index(vowels,2)
c Transform final letters with punctuation
any(letters-with-final-forms) any(punctuation) + any(whitespace) > index(sofit-forms-of-letters,1) index(punctuation,2)
c Transform final letters with punctuation and a dagesh
any(letters-with-final-forms) any(punctuation) $dagesh + any(whitespace) > index(sofit-forms-of-letters,1) index(punctuation,2) $dagesh
any(letters-with-final-forms) $dagesh any(punctuation) + any(whitespace) > index(sofit-forms-of-letters,1) $dagesh index(punctuation,3)
any(presentation-dageshed-letters-with-final-forms) any(punctuation) + any(whitespace) > index(presentation-dageshed-sofit-forms-of-letters,1) index(punctuation,2)
any(normal-dageshed-letters-with-final-forms) any(punctuation) + any(whitespace) > index(normal-undageshed-sofit-forms-of-letters,1) $dagesh index(punctuation,2)
c Transfrom final letters with a punctuation and a vowel
c any(letters-with-final-forms) any(punctuation) any(vowels) + any(whitespace) > index(sofit-forms-of-letters,1) index(punctuation,2) index(vowels,3)
c any(letters-with-final-forms) any(vowels) any(punctuation) + any(whitespace) > index(sofit-forms-of-letters,1) index(vowels,2) index(punctuation,3)
c Transform final letters with punctuation, a vowel, and a dagesh (phew!)
c any(letters-with-final-forms) any(punctuation) any(vowels) $dagesh + any(whitespace) > index(sofit-forms-of-letters,1) index(punctuation,2) index(vowels,3) $dagesh
c any(letters-with-final-forms) any(punctuation) $dagesh any(vowels) + any(whitespace) > index(sofit-forms-of-letters,1) index(punctuation,2) $dagesh index(vowels,4)
c any(letters-with-final-forms) any(vowels) any(punctuation) $dagesh + any(whitespace) > index(sofit-forms-of-letters,1) index(vowels,2) index(punctuation,3) $dagesh
c any(letters-with-final-forms) any(vowels) $dagesh any(punctuation) + any(whitespace) > index(sofit-forms-of-letters,1) index(vowels,2) $dagesh index(punctuation,4)
c any(letters-with-final-forms) $dagesh any(punctuation) any(vowels) + any(whitespace) > index(sofit-forms-of-letters,1) $dagesh index(punctuation,3) index(vowels,4)
c any(letters-with-final-forms) $dagesh any(vowels) any(punctuation) + any(whitespace) > index(sofit-forms-of-letters,1) $dagesh index(vowels,3) index(punctuation,4)
c any(presentation-dageshed-letters-with-final-forms) any(punctuation) any(vowels) + any(whitespace) > index(presentation-dageshed-sofit-forms-of-letters,1) index(punctuation,2) index(vowels,3)
c any(presentation-dageshed-letters-with-final-forms) any(vowels) any(punctuation) + any(whitespace) > index(presentation-dageshed-sofit-forms-of-letters,1) index(vowels,2) index(punctuation,3)
c any(normal-dageshed-letters-with-final-forms) any(punctuation) any(vowels) + any(whitespace) > index(normal-undageshed-sofit-forms-of-letters,1) $dagesh index(punctuation,2) index(vowels,3)
c any(normal-dageshed-letters-with-final-forms) any(vowels) any(punctuation) + any(whitespace) > index(normal-undageshed-sofit-forms-of-letters,1) $dagesh index(vowels,2) index(punctuation,3)

View file

@ -0,0 +1,95 @@
<?xml version="1.0" encoding="utf-8"?>
<Package>
<System>
<KeymanDeveloperVersion>17.0.325.0</KeymanDeveloperVersion>
<FileVersion>7.0</FileVersion>
</System>
<Options>
<ExecuteProgram></ExecuteProgram>
<ReadMeFile>readme.htm</ReadMeFile>
<LicenseFile>..\LICENSE.md</LicenseFile>
<WelcomeFile>..\..\another-folder\welcome\welcome.htm</WelcomeFile>
<MSIFileName></MSIFileName>
<MSIOptions></MSIOptions>
<FollowKeyboardVersion/>
</Options>
<StartMenu>
<Folder></Folder>
<Items/>
</StartMenu>
<Info>
<Version URL=""></Version>
<Name URL="">Aleph With Beth</Name>
<Copyright URL="">© Cameron Briggs</Copyright>
<Author URL="">Cameron Briggs</Author>
<WebSite URL="https://freehebrew.online">https://freehebrew.online</WebSite>
<Description URL="">alephwithbeth keyboard</Description>
</Info>
<Files>
<File>
<Name>readme.htm</Name>
<Description>File readme.htm</Description>
<CopyLocation>0</CopyLocation>
<FileType>.htm</FileType>
</File>
<File>
<Name>..\..\another-folder\welcome\welcome.htm</Name>
<Description>File welcome.htm</Description>
<CopyLocation>0</CopyLocation>
<FileType>.htm</FileType>
</File>
<File>
<Name>..\build\alephwithbeth.js</Name>
<Description>File alephwithbeth.js</Description>
<CopyLocation>0</CopyLocation>
<FileType>.js</FileType>
</File>
<File>
<Name>..\build\alephwithbeth.kvk</Name>
<Description>File alephwithbeth.kvk</Description>
<CopyLocation>0</CopyLocation>
<FileType>.kvk</FileType>
</File>
<File>
<Name>..\build\alephwithbeth.kmx</Name>
<Description>Keyboard alephwithbeth</Description>
<CopyLocation>0</CopyLocation>
<FileType>.kmx</FileType>
</File>
<File>
<Name>..\..\another-folder\welcome\KeyboardLayout.png</Name>
<Description>File KeyboardLayout.png</Description>
<CopyLocation>0</CopyLocation>
<FileType>.png</FileType>
</File>
<File>
<Name>..\..\another-folder\welcome\KeyboardLayoutAlt.png</Name>
<Description>File KeyboardLayoutAlt.png</Description>
<CopyLocation>0</CopyLocation>
<FileType>.png</FileType>
</File>
<File>
<Name>..\..\another-folder\welcome\KeyboardLayoutShift.png</Name>
<Description>File KeyboardLayoutShift.png</Description>
<CopyLocation>0</CopyLocation>
<FileType>.png</FileType>
</File>
<File>
<Name>..\LICENSE.md</Name>
<Description></Description>
<CopyLocation>0</CopyLocation>
<FileType>.md</FileType>
</File>
</Files>
<Keyboards>
<Keyboard>
<Name>Aleph With Beth</Name>
<ID>alephwithbeth</ID>
<Version>1.0.4</Version>
<Languages>
<Language ID="he">Hebrew</Language>
</Languages>
</Keyboard>
</Keyboards>
<Strings/>
</Package>

View file

@ -0,0 +1,138 @@
<?xml version="1.0" encoding="utf-8"?>
<visualkeyboard>
<header>
<version>10.0</version>
<kbdname>alephwithbeth</kbdname>
<flags/>
</header>
<encoding name="unicode" fontname="Arial" fontsize="-12">
<layer shift="S">
<key vkey="K_1">!</key>
<key vkey="K_QUOTE">"</key>
<key vkey="K_3">֨</key>
<key vkey="K_4">֜</key>
<key vkey="K_5">֞</key>
<key vkey="K_7">֬</key>
<key vkey="K_9">(</key>
<key vkey="K_0">)</key>
<key vkey="K_8">֝</key>
<key vkey="K_EQUAL">+</key>
<key vkey="K_COLON">״</key>
<key vkey="K_PERIOD">ּ</key>
<key vkey="K_SLASH">?</key>
<key vkey="K_2">֘</key>
<key vkey="K_A">ָ</key>
<key vkey="K_B">בּ</key>
<key vkey="K_C">שּׂ</key>
<key vkey="K_D">דּ</key>
<key vkey="K_E">ֵ</key>
<key vkey="K_F">טּ</key>
<key vkey="K_G">גּ</key>
<key vkey="K_H">הּ</key>
<key vkey="K_J">וַיּ</key>
<key vkey="K_K">כּ</key>
<key vkey="K_L">לּ</key>
<key vkey="K_M">מּ</key>
<key vkey="K_N">נּ</key>
<key vkey="K_O">וֹ</key>
<key vkey="K_P">פּ</key>
<key vkey="K_Q">קּ</key>
<key vkey="K_R">רּ</key>
<key vkey="K_S">סּ</key>
<key vkey="K_T">תּ</key>
<key vkey="K_U">ֻ</key>
<key vkey="K_V">שּׁ</key>
<key vkey="K_W">וּ</key>
<key vkey="K_X">צּ</key>
<key vkey="K_Y">יּ</key>
<key vkey="K_Z">זּ</key>
<key vkey="K_6">֔</key>
<key vkey="K_HYPHEN">_</key>
<key vkey="K_LBRKT">{</key>
<key vkey="K_BKSLASH">|</key>
<key vkey="K_RBRKT">}</key>
</layer>
<layer shift="">
<key vkey="K_QUOTE">'</key>
<key vkey="K_COMMA">א</key>
<key vkey="K_PERIOD">.</key>
<key vkey="K_SLASH">/</key>
<key vkey="K_1">1</key>
<key vkey="K_2">2</key>
<key vkey="K_3">3</key>
<key vkey="K_4">4</key>
<key vkey="K_5">5</key>
<key vkey="K_6">6</key>
<key vkey="K_7">7</key>
<key vkey="K_8">8</key>
<key vkey="K_9">9</key>
<key vkey="K_COLON">ְ</key>
<key vkey="K_EQUAL">=</key>
<key vkey="K_LBRKT">[</key>
<key vkey="K_BKSLASH">\</key>
<key vkey="K_RBRKT">]</key>
<key vkey="K_BKQUOTE">֑</key>
<key vkey="K_A">ַ</key>
<key vkey="K_B">ב</key>
<key vkey="K_C">שׂ</key>
<key vkey="K_D">ד</key>
<key vkey="K_E">ֶ</key>
<key vkey="K_F">ט</key>
<key vkey="K_G">ג</key>
<key vkey="K_H">ה</key>
<key vkey="K_I">ִ</key>
<key vkey="K_J">ח</key>
<key vkey="K_K">כ</key>
<key vkey="K_L">ל</key>
<key vkey="K_M">מ</key>
<key vkey="K_N">נ</key>
<key vkey="K_O">ֹ</key>
<key vkey="K_P">פ</key>
<key vkey="K_Q">ק</key>
<key vkey="K_R">ר</key>
<key vkey="K_S">ס</key>
<key vkey="K_T">ת</key>
<key vkey="K_U">ע</key>
<key vkey="K_V">שׁ</key>
<key vkey="K_W">ו</key>
<key vkey="K_X">צ</key>
<key vkey="K_Y">י</key>
<key vkey="K_Z">ז</key>
<key vkey="K_HYPHEN">־</key>
<key vkey="K_0">0</key>
</layer>
<layer shift="A">
<key vkey="K_QUOTE">ׄ</key>
<key vkey="K_COMMA">֫</key>
<key vkey="K_HYPHEN">ֿ</key>
<key vkey="K_PERIOD">̈</key>
<key vkey="K_SLASH">״</key>
<key vkey="K_0">֯</key>
<key vkey="K_1">֗</key>
<key vkey="K_2">֮</key>
<key vkey="K_3">֙</key>
<key vkey="K_4">֠</key>
<key vkey="K_5">֩</key>
<key vkey="K_6">֟</key>
<key vkey="K_7">֡</key>
<key vkey="K_8">֕</key>
<key vkey="K_9">֓</key>
<key vkey="K_COLON">׃</key>
<key vkey="K_EQUAL">◌</key>
<key vkey="K_LBRKT">֔</key>
<key vkey="K_BKSLASH">֫</key>
<key vkey="K_RBRKT">֒</key>
<key vkey="K_A">ֲ</key>
<key vkey="K_E">ֱ</key>
<key vkey="K_G">•</key>
<key vkey="K_H">̊</key>
<key vkey="K_I">ֽ</key>
<key vkey="K_K">ך</key>
<key vkey="K_M">ם</key>
<key vkey="K_N">ן</key>
<key vkey="K_O">ֳ</key>
<key vkey="K_P">ף</key>
<key vkey="K_X">ץ</key>
</layer>
</encoding>
</visualkeyboard>

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

View file

@ -0,0 +1,22 @@
<?php
$pagename = 'Aleph with Beth Keyboard Help';
$pagetitle = $pagename;
$pagestyle = <<<END
.center { display: block; margin-left: auto; margin-right: auto; width: 75%; }
END;
require_once('header.php');
?>
<body>
<h1>Start Using Aleph with Beth</h1>
<h1>Desktop Layout</h1>
<h2>Base layout</h2>
<img src="KeyboardLayout.png" alt="Keyboard layout" class="center">
<h2>Layout when shift is pressed</h2>
<img src="KeyboardLayoutShift.png" alt="Keyboard layout (when shift key is pressed)" class="center">
<h2>Layout when alt is pressed</h2>
<img src="KeyboardLayoutAlt.png" alt="Keyboard layout (when alt key is pressed)" class="center">

View file

@ -0,0 +1,22 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>alephwithbeth</title>
<style type="text/css">
p { font: 10pt Tahoma; }
h1 { font: bold 16pt Tahoma; color: #4444cc; margin-bottom: 2px }
h2 { font: bold 12pt Tahoma; color: #4444cc; }
</style>
</head>
<body>
<h1>Aleph With Beth</h1>
<p>A Hebrew keyboard designed to intuitively match the standard QWERTY layout.</p>
<p>© Cameron Briggs</p>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

View file

@ -0,0 +1,28 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Start Using alephwithbeth</title>
<style type="text/css">
p { font: 10pt Tahoma; }
h1 { font: bold 16pt Tahoma; color: #4444cc; margin-bottom: 2px }
h2 { font: bold 12pt Tahoma; color: #4444cc; }
.center { display: block; margin-left: auto; margin-right: auto; width: 75%; }
</style>
</head>
<body>
<h1>Start Using Aleph With Beth</h1>
<h1>Keyboard Layouts</h1>
<h2>Base layout</h2>
<img src="KeyboardLayout.png" alt="Keyboard layout" class="center">
<h2>Layout when shift is pressed</h2>
<img src="KeyboardLayoutShift.png" alt="Keyboard layout (when shift key is pressed)" class="center">
<h2>Layout when alt is pressed</h2>
<img src="KeyboardLayoutAlt.png" alt="Keyboard layout (when alt key is pressed)" class="center">
</body>
</html>

View file

@ -13,6 +13,7 @@ THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
builder_describe "Build Keyman kmc-generate module" \
"@/common/web/keyman-version" \
"@/common/web/langtags" \
"@/common/web/types" \
"@/developer/src/common/web/test-helpers" \
"@/developer/src/common/web/utils" \

View file

@ -27,7 +27,8 @@
"dependencies": {
"@keymanapp/common-types": "*",
"@keymanapp/developer-utils": "*",
"@keymanapp/keyman-version": "*"
"@keymanapp/keyman-version": "*",
"@keymanapp/langtags": "*"
},
"devDependencies": {
"@keymanapp/developer-test-helpers": "*",

View file

@ -4,7 +4,7 @@
* Base interfaces and classes for generating Keyman Developer source files
*/
import { CompilerCallbacks, CompilerLogLevel, KeymanCompilerArtifact, KeymanCompilerArtifacts, KeymanCompilerResult } from "@keymanapp/developer-utils";
import { CompilerBaseOptions, CompilerCallbacks, KeymanCompilerArtifact, KeymanCompilerArtifacts, KeymanCompilerResult } from "@keymanapp/developer-utils";
import { GeneratorMessages } from './generator-messages.js';
import { KeymanTargets } from "@keymanapp/common-types";
@ -13,12 +13,7 @@ import { KeymanTargets } from "@keymanapp/common-types";
* @public
* Options for the Keyman Developer project generator
*/
export interface GeneratorOptions /* not inheriting from CompilerBaseOptions */ {
/**
* Reporting level to console, used by NodeCompilerCallbacks (not used in
* compiler modules; all messages are still reported to the internal log)
*/
logLevel?: CompilerLogLevel;
export interface GeneratorOptions extends CompilerBaseOptions {
/**
* identifier (basename) of the keyboard or model
*/

View file

@ -6,6 +6,7 @@
import { KeymanTargets } from "@keymanapp/common-types";
import KEYMAN_VERSION from "@keymanapp/keyman-version";
import { getLangtagByTag } from "@keymanapp/langtags";
import { AbstractGenerator, GeneratorArtifacts } from "./abstract-generator.js";
import { GeneratorMessages } from "./generator-messages.js";
@ -26,13 +27,13 @@ export class BasicGenerator extends AbstractGenerator {
protected preGenerate() {
const dt = new Date();
this.languageTags = this.options.languageTags.length
this.languageTags = this.options.languageTags?.length
? this.options.languageTags.map(tag => new Intl.Locale(tag).minimize().toString())
: [this.DEFAULT_LOCALE];
// Verify that targets passed in are valid
let failed = false;
for(const target of this.options.targets) {
for(const target of this.options.targets ?? []) {
if(!KeymanTargets.AllKeymanTargets.includes(target)) {
this.callbacks.reportMessage(GeneratorMessages.Error_InvalidTarget({target}));
failed = true;
@ -42,7 +43,7 @@ export class BasicGenerator extends AbstractGenerator {
return false;
}
if(this.options.targets.includes(KeymanTargets.KeymanTarget.any) || this.options.targets.length == 0) {
if(this.options.targets?.includes(KeymanTargets.KeymanTarget.any) || !this.options.targets?.length) {
this.resolvedTargets = KeymanTargets.AllKeymanTargets.filter(target => target != KeymanTargets.KeymanTarget.any);
} else {
this.resolvedTargets = [].concat(this.options.targets);
@ -72,8 +73,12 @@ export class BasicGenerator extends AbstractGenerator {
}
private getLanguageName(tag: string) {
// TODO-GENERATE: probably need to use langtags.json
return new Intl.Locale(tag).language;
const langtag = getLangtagByTag(tag);
if(!langtag) {
// Fallback to Intl.Locale, probably will be undefined
return new Intl.Locale(tag).language;
}
return langtag.name;
}
private generateLanguageListForPackage(): string {

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