+
+This article will continue the guide to creating a touch keyboard layout. In
+particular, we'll look in more detail at how keys are arranged, just what can be
+specified for each key, and lastly, how this all looks in the JSON code used to
+define the long press layout.
+
+## Key and Key Layer Organization
+
+There are two issues that are immediately apparent when considering key layout
+on touch devices.
+
+First, on smaller touch devices, such as phones, if we try to display the same
+arrangement of keys that is used for a typical desktop keyboard, the keys are so
+small that it is difficult to reliably select the wanted key. If used in
+'portrait' view, key widths are too narrow for our 'fat fingers':
+
+![](../../images/touch_amharic_keyboard_4.png)
+
+Or if in 'landscape' view, key heights are too small:
+
+![](../../images/touch_amharic_keyboard_5.png)
+
+The situation is improved markedly if we limit the number of keys per row to ten
+and have no more than four key rows:
+
+![](../../images/touch_amharic_keyboard_6.png)
+
+and, in landscape view:
+
+![](../../images/touch_amharic_keyboard_7.png)
+
+So which keys can be eliminated, and which keys must be on the base (default)
+layer? This brings us to the second point. When using phones and other touch
+layout devices rather than desktop keyboards, text entry is most often
+single-handed, which makes it best to avoid using the 'shift' layer for entering
+normal text. Secondary keyboard layers will, of course, still be usually needed
+for uppercase, numerals and symbols, but will be used much less frequently.
+
+Most desktop keyboards (for European languages, at least) are already laid out
+with no more than ten letter (or digit) keys per row, with the remaining keys
+being used for accented letters, punctuation and other non-letter input. So the
+obvious choice is to move non-letter keys (and accented letters) either to a
+secondary key layer, or to long press ('pop-up') keys. The GFF Amharic desktop
+keyboard is fortunately also arranged with only ten letters per key row (other
+keys being used for punctuation, etc.), as the many different characters in the
+Amharic 'abugida' are generated by multi-letter sequences rather than being
+displayed on separate keys. The Geez script does not have both upper-case and
+lower-case forms, so those initial consonants that require use of the 'shift'
+layer on the desktop keyboard have been added to the corresponding base-layer
+key as long press keys:
+
+![](../../images/touch_amharic_keyboard_8.png)
+
+Arrangement of punctuation and other non-letter keys is more flexible as mobile
+users are generally familiar with using long press keys or a secondary key layer
+for finding and entering digits and special characters. However, some
+punctuation characters are used so frequently that they need to be on the base
+layer. For the GFF Amharic keyboard, the most frequently used punctuation
+characters can be output from the base layer using standard or long press keys.
+The Geez word space character, in particular, is so frequently used that it was
+considered useful to add it to the bottom key, adjacent to the space bar, as is
+sometimes done for other scripts, such as Japanese, on desktop (physical)
+keyboards.
+
+## Arranging keys with the layout editor
+
+The Keyman Developer layout editor really makes it quite easy to try different
+key layouts and choose what is best for your keyboard. The image below
+highlights just how, for any selected key, using the clickable icons circled, a
+key row can be added above (1) or below (2), a key added before (3) or after
+(4), the selected key deleted (5), and how an array of long press keys
+(sometimes called "subkeys") can be added (6).
+
+![](../../images/touch_amharic_keyboard_9.png)
+
+## Key properties
+
+For each visual key, the appearance and behaviour is determined by a number of
+properties:
+
+### Key code
+
+Each key must be given an identifying key code which is unique to the key layer.
+Key codes by and large correspond to the virtual key codes used when creating a
+keyboard program for a desktop keyboard, and should start with `K_`, for keys
+mapped to standard Keyman virtual key names, e.g. `K_HYPHEN`, and `T_` or `U_`
+for user-defined names, e.g. `T_ZZZ`. If keyboard rules exist matching the key
+code in context, then the output from the key will be determined by the
+processing of those rules. It is usually best to include explicit rules to
+manage the output from each key, but if no rules matching the key code are
+included in the keyboard program, and the key code matches the pattern
+`U_xxxx[_yyyy...]` (where `xxxx` and
+`yyyy` are 4 to 6-digit hex strings), then the Unicode characters
+`U+xxxx` and `U+yyyy` will be output. As of Keyman 15, you
+can use more than one Unicode character value in the id (earlier versions
+permitted only one). The key code is always required, and a default code will
+usually be generated automatically by Keyman Developer.
+
+- `K_xxxx` is used for a standard Keyman Desktop key name, e.g.
+ `K_W`, `K_ENTER`. You cannot make up your own `K_xxxx` names.
+ Many of the `K_` ids have overloaded output behaviour, for instance, if no
+ rule is matched for `K_W`, Keyman will output 'w' when it is touched. The
+ standard key names are listed in [Virtual Keys and Virtual Character
+ Keys](/developer/language/guide/virtual-keys "Virtual Keys and Virtual
+Character Keys"). Typically, you would use only the "common" virtual key
+ codes.
+
+- `T_xxxx` is used for any user defined names, e.g. `T_SCHWA`. If you wanted
+ to use it, `T_ENTER` would also be valid. If no rule matches it, the key
+ will have no output behaviour.
+
+- `U_####[_####]` is used as a shortcut for a key that will output those
+ Unicode values, if no rule matches it. This is similar to the overloaded
+ behaviour for `K_` ids. Thus `####` must be valid Unicode characters. E.g.
+ `U_0259` would generate a schwa if no rule matches. It is still valid to
+ have a rule such as `+ [U_0259] > ...`
+
+As noted above, some `K_xxxx` codes emit characters, if no rule is defined.
+There are also some codes which have special functions:
+
+
+
+
+
Identifier
+
Meaning
+
+
+
+
+
`K_ENTER`
+
Submit a form, or add a new line (multi-line); the key action may vary depending on the situation.
+
+
+
`K_BKSP`
+
Delete back a single character. This key, if held down, will repeat. It is the only key code which triggers
+ repeat behavior.
+
+
+
`K_LOPT`
+
Open the language menu (aka Globe key).
+
+
+
`K_ROPT`
+
Hide the on screen keyboard.
+
+
+
`K_TAB`, `K_TABBACK`, `K_TABFWD`
+
Move to next or previous element in a form. Note that these key functions are normally
+ implemented outside the touch layout, so should not typically be used. `K_TAB` will go to previous
+ element if used with the `shift` modifier.
+
+
+
+
+Any key can be used to switch keyboard layers (see
+[`nextlayer`](#toc-nextlayer)), but the following layer-switching key codes have
+been added for switching to some commonly used secondary layers. Note that these
+keys have no specific meaning; you must still set the `nextlayer` property on
+the key.
+
+
+
+
+
Identifier
+
Meaning
+
+
+
+
+
`K_NUMERALS`
+
Switch to a numeric layer
+
+
+
`K_SYMBOLS`
+
Switch to a symbol layer
+
+
+
`K_CURRENCIES`
+
Switch to a currency layer
+
+
+
`K_SHIFTED`
+
Switch to a shift layer
+
+
+
`K_ALTGR`
+
Switch to a right-alt layer (desktop compatibility)
+
+
+
+
+### Key text
+
+The key text is simply the character (or characters) that you want to appear on
+the key cap. This will usually be the same as the characters generated when the
+key is touched, unless contextual rules are used to generate output according to
+a multi-key sequence, as will be true for the GFF Amharic keyboard. Unicode
+characters can be specified either as a string using a target font or using the
+standard hex notation `\uxxxx`. This may be sometimes more convenient, for
+example, for characters from an uninstalled font, or for diacritic characters
+that do not render well alone.
+
+A number of special text labels are recognized as identifying special purpose
+keys, such as Shift, Backspace, Enter, etc., for which icons are more
+appropriately used than a text label. A special font including these icons is
+included with Keyman and automatically embedded and used in any web page using
+Keyman. The list of icons in the font will probably be extended in future, but
+for now the following special labels are recognized:
+
+
+
+
+
Text String
+
Key Cap
+
Key Purpose
+
+
+
+
+
`*Shift*`
+
+
Select Shift layer (inactive). Use on the Shift key to indicate that it switches to the shift layer.
+
+
+
`*Shifted*`
+
+
Select Shift layer (active). Use on the Shift key on the shift layer to switch back to the default layer.
+
+
+
`*ShiftLock*`
+
+
Switch to Caps layer (inactive). Not commonly used; generally double-tap on Shift key is used to access the
+ caps layer.
+
+
+
`*ShiftedLock*`
+
+
Switch to Caps layer (active). Use on the Shift key on the caps layer to switch back to the default layer.
+
+
+
+
`*Enter*`
+
or
+
Return or Enter key (shape determined by writing system direction)
+
+
+
`*LTREnter*`
+
+
Return or Enter key (left-to-right script shape)
+
+
+
`*RTLEnter*`
+
+
Return or Enter key (right-to-left script shape)
+
+
+
`*BkSp*`
+
or
+
Backspace key (shape determined by writing system direction)
+
+
+
`*LTRBkSp*`
+
+
Backspace key (left-to-right script shape)
+
+
+
`*RTLBkSp*`
+
+
Backspace key (right-to-left script shape)
+
+
+
`*Menu*`
+
+
Globe key; display the language menu. Use on the `K_LOPT` key.
+
+
+
`*Hide*`
+
+
Hide the on screen keyboard. Use on the `K_ROPT` key.
+
+
+
`*ABC*`
+
+
Select alphabetic layer (Uppercase)
+
+
+
`*abc*`
+
+
Select alphabetic layer (Lowercase)
+
+
+
`*123*`
+
+
Select the numeric layer
+
+
+
`*Symbol*`
+
+
Select the symbol layer
+
+
+
`*Currency*`
+
+
Select the currency symbol layer
+
+
+
`*ZWNJ*`
+
(iOS) or (Android)
+
Zero Width Non Joiner (shape determined by current platform)
+
+
+
`*ZWNJiOS*`
+
+
Zero Width Non Joiner (iOS style shape)
+
+
+
`*ZWNJAndroid*`
+
+
Zero Width Non Joiner (Android style shape)
+
+
+
`*ZWNJGeneric*`
+
+
Zero Width Non Joiner (not platform-specific)
+
+
+
`*Sp*`
+
+
Regular space
+
+
+
`*NBSp*`
+
+
No-Break Space
+
+
+
`*NarNBSp*`
+
+
Narrow No-Break Space
+
+
+
`*EnQ*`
+
+
En Quad
+
+
+
`*EmQ*`
+
+
Em Quad
+
+
+
`*EnSp*`
+
+
En Space
+
+
+
`*EmSp*`
+
+
Em Space
+
+
+
`*PunctSp*`
+
+
Punctuation Space
+
+
+
`*ThSp*`
+
+
Thin Space
+
+
+
`*HSp*`
+
+
Hair Space
+
+
+
`*ZWSp*`
+
+
Zero Width Space
+
+
+
`*ZWJ*`
+
+
Zero Width Joiner
+
+
+
`*WJ*`
+
+
Word Joiner
+
+
+
`*CGJ*`
+
+
Combining Grapheme Joiner
+
+
+
`*LTRM*`
+
+
Left-to-right Mark
+
+
+
`*RTLM*`
+
+
Right-to-left Mark
+
+
+
`*SH*`
+
+
Soft Hyphen
+
+
+
`*HTab*`
+
+
Horizontal Tabulation
+
+
+
+
+
+The following additional symbols are also available, but intended for working
+with legacy desktop layouts, and not recommended for general use:
+
+
+
+### Key type
+
+The general appearance of each key is determined by the key type, which is
+selected (in Keyman Developer) from a drop-down list. While generally behavior
+is not impacted by the key type, Spacer keys cannot be selected.
+
+
+
+
+
Key Type
+
Value
+
Meaning
+
+
+
+
+
Default
+
`0`
+
Any normal key that emits a character
+
+
+
Special
+
`1`
+
The frame keys such as Shift, Enter, BkSp.
+
+
+
Special (active)
+
`2`
+
A frame key which is currently active, such as the Shift key on the shift layer.
+
+
+
Deadkey
+
`8`
+
Does not impact behavior, but colors the key differently to indicate it has a special function, such as a
+ desktop-style deadkey.
+
+
+
Blank
+
`9`
+
A blank key, which may be used to maintain a layout shape. Usually colored differently. Does not impact
+ behavior.
+
+
+
Spacer
+
`10`
+
Does not render the key, but leaves a same-sized gap in its place. The key cannot be selected.
+
+
+
+
+The colour, shading and borders of each key type is actually set by a style
+sheet which can be customized by the page developer.
+
+### font-family
+
+If a different font is required for a particular key text, the `font-family`
+name can be specified. The font used to display icons for the special keys (as
+mentioned above) does not need to be specified, as it will be automatically
+applied to a key that uses any of the special key text labels.
+
+### font-size
+
+If a particular key cap text requires a different font size from the default for
+the layout, it should be specified in em units. This can be helpful if a the key
+text is either an unusually large character or, alternatively, a word or string
+of several characters that would not normally fit on the key.
+
+### width
+
+The layout is scaled to fit the widest row of keys in the device width, assuming
+a default key width of 100 units. Keys that are to be wider or narrower than the
+default width should have width specified as a percentage of the default width.
+For any key row that is narrower than the widest row, the width of the last key
+in the row will be automatically increased to align the right hand side of the
+key with the key with the right edge of the keyboard. However, where this is not
+wanted, a "spacer" key can be inserted to leave a visible space instead. As
+shown in the above layouts, where the spacer key appears on the designer screen
+as a narrow key, but will not be visible in actual use.
+
+### pad
+
+Padding to the left of each key can be adjusted, and specified as a percentage
+of the default key width. If not specified, a standard padding of 5% of the key
+width is used between adjacent keys.
+
+### layer
+
+To simplify correspondence with desktop keyboards and avoid the need for using a
+separate keyboard mapping program, touch layout keys can specify a desktop
+keyboard layer that the keystroke should be interpreted as coming from. Layer
+names of `shift`, `ctrl`, `alt`, `ctrlshift`, `altshift`, `ctrlalt` and
+`ctrlaltshift` can be used to simulate use of the appropriate modifier keys when
+processing rules.
+
+### nextlayer
+
+The virtual keys `K_SHIFT`, `K_CONTROL`, `K_MENU`, etc. are normally used to
+switch to another key layer, which is implied by the key code. The left and
+right variants of those key codes, and also additional layer-switching keys
+mentioned above (`K_NUMERALS`, `K_SYMBOLS`, `K_CURRENCIES`, `K_ALTGR`) can also
+be used to automatically switch to the appropriate key layer instead of
+outputting a character. However, it is sometimes useful for a key to output a
+character first, then switch to a new layer, for example, switching back to the
+default keyboard layer after a punctuation key on a secondary layer had been
+used. Specifying the `nextlayer` for a key allows a different key layer to be
+selected automatically following the output of the key. Of course, that can be
+manually overridden by switching to a different layer if preferred.
+
+Another way the `nextlayer` property can be used is for a non-standard layer
+switching key. So, for example, for the GFF Amharic keyboard phone layout,
+switching back to the base layer uses a `T_ALPHA` key code, in which `nextlayer`
+is set as default. In this case, it is also necessary to add a rule to the
+keyboard program:
+
+```keyman
++ [T_ALPHA] > nul
+```
+
+to ensure that the key's scan code is ignored by the keyboard mapping.
+
+When a key in a touch layout definition includes a **Next Layer** control, this
+takes precedence over setting layer via the
+[`layer`](/developer/language/reference/layer) store (as the **Next Layer**
+control is applied once the rule has finished processing).
+
+### subkey
+
+Arrays of longpress 'subkeys' or pop-up keys can be defined for any key, and
+will appear momentarily after the key is touched if not immediately released.
+This provides a major advantage over physical desktop keyboards in that many
+more keys can be made available from a single layer, without cluttering up the
+basic appearance of the layout. For the GFF Amharic keyboard, we have already
+noted how such subkey arrays are used to manage the extra keys that, on the
+desktop keyboard, would appear in the shift layer. But they are also used to
+provide another way to enter the two different types of each syllable-initial
+vowels (glottal or pharyngeal), as a visual alternative to pressing the key
+twice.
+
+The same properties that are defined for standard keys can also be specified for
+each subkey except that the width of each key in a subkey array will always be
+the same as the width of the key that causes the subkeys to be shown, and key
+spacing always uses the default padding value.
+
+The GFF Amharic keyboard, like many others, is mnemonic, so it is useful to also
+display the standard key cap letter that would appear on the key of a desktop
+keyboard. This is enabled globally in the On-Screen layout editor and applies to
+both the On-Screen keyboard and touch layouts.
+
+## Representing (and editing) the visual layout with JSON code
+
+In case you are wondering, 'Why do I need to know that?', the reason is that,
+just as with keyboard mapping code, it is sometimes easier to edit a text
+specification than to use the GUI layout design tool. Keyman Developer switches
+seamlessly between the visual layout tool and the code editor, unless, of
+course, careless editing of the code results in invalid JSON syntax!
+
+The GFF Amharic phone layout code starts as:
+
+```javascript
+ {
+ "phone": {
+ "font": "Tahoma",
+ "layer": [{
+ "id": "default",
+ "row": [{
+ "id": 1,
+ "key": [{
+ "id": "K_Q",
+ "text": "ቅ",
+ "pad": "0"
+ }, {
+ "id": "K_W",
+ "text": "ው"
+ },
+
+ . . .
+```
+
+As long as standard JSON syntax is remembered - nested braces {…}, quoted
+strings "…" for both element names and values, element or object arrays in
+square brackets […], and no trailing comma after the last element in an array -
+it is quite easy to understand a layout, which will usually comprise a list of
+two separate JSON objects for tablet and phone.
+
+Now you're ready to create a great touch layout for your own Keyman keyboard!
+
+Other articles on developing touch layouts:
+
+- [Creating a touch keyboard layout for Amharic - part 1](creating-a-touch-keyboard-layout-for-amharic)
+- [How to test your keyboard layout — touch and desktop](../test/keyboard-touch-and-desktop)
+
+You can distribute your keyboard to other users by following the instructions in
+this article:
+
+- [Distribute keyboards to Keyman applications](../distribute/packages)
diff --git a/developer/docs/help/guides/develop/creating-a-touch-keyboard-layout-for-amharic.md b/developer/docs/help/guides/develop/creating-a-touch-keyboard-layout-for-amharic.md
new file mode 100644
index 00000000000..3dc96dfc950
--- /dev/null
+++ b/developer/docs/help/guides/develop/creating-a-touch-keyboard-layout-for-amharic.md
@@ -0,0 +1,124 @@
+---
+title: Creating a Touch Keyboard Layout Part 1
+---
+
+This article describes the approach taken and some of the issues encountered when adapting the existing GFF‑AMH‑7 Amharic keyboard for easier use on phone and tablet touch screen devices.
+
+A widely used Keyman Desktop phonetic keyboard for Amharic (developed by Geez Frontier Foundation) was also compiled and made available for use with KeymanWeb. When used with web browsers on desktop computers, the behaviour of the keyboard was identical to use with Keyman Desktop, but use of the same Amharic keyboard on smart phones or other touch screen devices revealed a number of weaknesses in the layout.
+
+### Background
+
+The desktop layout has a default layer and a shift layer which are fairly similar:
+
+![](../../images/touch_amharic_keyboard.png)
+
+
+Several keycaps have more than one Geez script letter, indicating that pressing the key twice changes the character output. Geez script is better understood as an Abugida, rather than an alphabet, since each consonant-vowel syllable is a modification of the original letter rather than having separate letters for consonant and vowel. So when entering Amharic, the form of a consonant changes from the default syllable form to the appropriate syllable character for the consonant and vowel pressed. However, as each vowel can also occur in stand-alone syllables as either pharyngeal or glottal forms, the two separate syllables for each vowel are shown on the "a", "e", "i", "o" and "u" keys.
+
+But what about the extra characters on the "s" and "h" keys? Amharic (like most Semitic languages) is rich in fricative sounds, and Geez has four separate, distinct phonemes that are roughly similar to the English
+"s", and three similar to "h". The desktop layout handles this by using
+repeated keystrokes in combination with shift to select the alternate
+consonants (before typing the appropriate vowel). That works for a
+desktop keyboard, but not for a touch screen device, for which it is
+often impractical to hold a shift key while touching another key.
+
+Small format touch-screen devices such as phones have a further
+difficulty in that if they display the full number of key rows and keys
+in each row that appear in the desktop layout, the keys are too small
+for convenient use.
+
+On the other hand, touch screen devices offer one very significant
+advantage over layouts based on a physical keyboard - it is quite easy
+to configure dynamic touch screen layouts to display contextually
+appropriate character alternates in separate key windows as each key is
+touched.
+
+### Design goals
+
+With the above background, the following design goals were set for
+developing a touch screen layout for Amharic:
+- Maintain the same basic phonetic layout functionality that users are familiar with.
+- Instead of having "default" and "shift" layers, design the layout so that all alphabetic text and frequently used punctuation can be entered from a single layer, moving less commonly used punctuation and symbols to the second "symbol" layer.
+- Use a dynamic window rather than the shift key to select the initial "s" or "h" consonant for a syllable.
+- Use a separate key (adjacent to the space bar) for the Ethiopic word-space character, instead of requiring shift + space to be pressed.
+- For tablet devices, increase key size slightly by removing most non-letter keys from each row, moving them either to dynamic ("pop-up") key windows, or to the symbol layer.
+- For phones and other small-format devices, further increase the key size by not displaying the digits in the default (alphabetic) layer, and moving many of the less commonly used characters to dynamic windows, reducing the number of key rows by one for each layer.
+
+### Implementation
+
+Achieving the design goals required making a number of decisions for
+each format (tablet and phone), in particular:
+
+- Which keys can be removed from the default layer?
+- Which keys can be removed from the symbol layer?
+- Which "pop-up" keys should be grouped with each base character?
+
+Keyman Developer 17's Touch Layout editor greatly simplifies the process
+of developing the JSON layout file that specifies the touch layout for
+each device, making it easy to move keys around, create pop-up key
+arrays, and adjust key widths, labels and other parameters. While the
+graphical interface does not allow moving entire key rows between layers
+or device types, that is easily managed using the code window.
+
+To keep a keyboard compatible with the desktop version, it is important
+to use the same key identifiers and "shift states". So, no matter
+whether a particular character occurs in a visible row or in a pop-up
+array, it should usually use the same code as on the original desktop
+layout keyboard.
+
+Where a key did not occur in the original layout and does not correspond
+to a desktop layout key, identifiers will usually be something like
+T\_<name> where <name> will typically be a Unicode character
+name, or an obvious contraction of it. Note that this is just a rule of
+thumb and key identifiers can be anything that makes sense. Such keys
+are distinct from the K\_<name> identifiers that are associated
+with physical keys, and should normally have a mapping rule added
+explicitly to the mapping program. If no rule is added, output will
+default to the keycap text, but this is not recommended.
+
+All key "rotations" - pressing the same key repeatedly to select from
+alternate output characters - in the original desktop keyboard program
+are still supported, but have been supplemented by pop-up key arrays,
+allowing a user to visually select the wanted character instead of
+stepping through alternates. Similarly, Ethiopian numerals can be
+selected from a pop-up key array attached to each numeral key, as well
+as being output by pressing the single quote key before a digit key.
+
+The final tablet default keyboard layer, as displayed in the Touch
+Layout editor in the previous release of Keyman Developer 10, now appears
+as:
+
+![](../../images/touch_amharic_keyboard_2.png)
+
+Here the editor is shown with the "s" key selected, displaying the
+pop-up key array with the four different Geez characters that can be
+produced, each of which will combine as necessary with any subsequently
+typed vowel.
+
+For the small format (e.g. iPhone) layout, the two keyboard layers are:
+
+![](../../images/touch_amharic_keyboard_3.png)
+
+For these layouts, the row length has been reduced to the minimum number
+of ten keys, moving backspace to another row. The numerals only appear
+on the second layer, as is usual with phone layouts. All less-frequently
+used keys are now available only in pop-up key windows.
+
+### Summary
+
+With these principles guiding all the changes made, the Amharic keyboard
+now adapts to various device form factors, making use of device-specific
+usability factors, while retaining the same conceptual input model as
+the original keyboard. This means that users can use the keyboard layout
+on different devices with a minimum of difficulty. These methods should
+be applicable to other keyboard layouts.
+
+Other articles on developing touch layouts:
+
+- [Creating a touch keyboard layout for Amharic - the nitty gritty](creating-a-touch-keyboard-layout-for-amharic-the-nitty-gritty)
+- [How to test your keyboard layout — touch and desktop](../test/keyboard-touch-and-desktop)
+
+You can distribute your keyboard to other users by following the
+instructions in this article:
+
+- [Distribute keyboards to Keyman applications](../distribute/packages)
\ No newline at end of file
diff --git a/developer/docs/help/guides/develop/imx/imxdll.md b/developer/docs/help/guides/develop/imx/imxdll.md
new file mode 100644
index 00000000000..8d4fe029bcd
--- /dev/null
+++ b/developer/docs/help/guides/develop/imx/imxdll.md
@@ -0,0 +1,115 @@
+---
+title: DLL Exports
+---
+
+The DLL is called from Keyman with `LoadLibrary()`. All functions are
+then found with `GetProcAddress()`. You must ensure that the function
+exports do not have ordinals encoded in the names. The best way to
+accomplish this in C/C++ is to use a .def file.
+
+## DLL group function exports
+
+The function declaration for the DLL group function is:
+
+```c
+BOOL WINAPI KeyEvent(HWND hwndFocus, WORD KeyStroke, WCHAR KeyChar, DWORD ShiftFlags);
+```
+
+Note that `KeyEvent()` is a placeholder for any name that you wish to
+use. You can have multiple exports for Keyman use in a single DLL.
+
+| | |
+|------------|-------|
+| hwndFocus | The currently focused window. You will probably never have a need to use this. |
+| KeyStroke | The virtual key code for the current key. |
+| KeyChar | The character code for the current key (based on US English layout). This will be `0` if _KeyStroke_ does not generate a character (e.g. function keys). |
+| ShiftFlags |The shift state for the current key. The following shift states are possible: (see the table below) |
+
+| Flag | Value | Description |
+|--------------|----------|----------------------------------------|
+| LCTRLFLAG | `0x0001` | Left Control Flag |
+| RCTRLFLAG | `0x0002` | Right Control Flag |
+| LALTFLAG | `0x0005` | Left Alt Flag |
+| RALTFLAG | `0x0008` | Right Alt Flag |
+| K_SHIFTFLAG | `0x0010` | Shift flag |
+| K_CTRLFLAG | `0x0020` | Ctrl flag (unused here; see l/r flags) |
+| K_ALTFLAG | `0x0040` | Alt flag (unused here; see l/r flags) |
+| CAPITALFLAG | `0x0100` | Caps lock on |
+| NUMLOCKFLAG | `0x0400` | Num lock on |
+| SCROLLFLAG | `0x1000` | Scroll lock on |
+| ISVIRTUALKEY | `0x4000` | It is a Virtual Key Sequence |
+
+## Optional DLL Exports
+
+Keyman recognises a number of other exports, if they are defined in the
+DLL. None of these are required. These functions will be called when a
+keyboard that references the DLL is manipulated. They will not be called
+for keyboards that do not reference the DLL.
+
+The following exports are available:
+
+### KeymanIMInit
+
+```c
+BOOL WINAPI KeymanIMInit(PSTR keyboardname);
+```
+
+`KeymanIMInit()` is called once when the keyboard identified by
+`keyboardname` is loaded for a given process. It is called for each
+process in which the keyboard is loaded.
+
+### KeymanIMDestroy
+
+```c
+BOOL WINAPI KeymanIMDestroy(PSTR keyboardname);
+```
+
+This is called once when the keyboard identified by `keyboardname` is
+unloaded in a given process. It is called when the process exits
+normally, or when Keyman refreshes its keyboard list after keyboards are
+added or removed. If the keyboard is subsequently reloaded,
+`KeymanIMInit()` will be called again.
+
+### KeymanIMActivate
+
+```c
+BOOL WINAPI KeymanIMActivate(PSTR keyboardname);
+```
+
+This function is called whenever the user or a program activates the
+keyboard. It is never called before `KeymanIMInit()` (unless
+`KeymanIMInit()` is not exported). This is an appropriate place to
+switch on permanently-visible IMC windows. `KeymanIMActivate()` can also
+be called when switching processes and the target process has a related
+keyboard already active.
+
+### KeymanIMDeactivate
+
+```c
+BOOL WINAPI KeymanIMDeactivate(PSTR keyboardname);
+```
+
+This function is called when the user or a program switches off a
+related keyboard. It is always called before `KeymanIMActivate()` for
+the next keyboard. It will also be called when the user activates
+another process, to give the DLL a chance to hide top-most IMC windows.
+
+### KeymanIMConfigure
+
+```c
+BOOL WINAPI KeymanIMConfigure(PSTR keyboardname, HWND hwndParent);
+```
+
+`KeymanIMConfigure()` is called when the user clicks the Configure
+button in Keyman Configuration to configure the DLL-specific
+functionality for the keyboard. The appropriate behaviour is to display
+a dialog box, and save the settings in the registry.
+
+This function is separate from all the other functions. It can be called
+when there are no keyboards loaded, or even if Keyman itself is not
+loaded. You should not attempt to call Keyman32.dll from this function.
+
+## See also
+
+- [DLL Interface for Keyman - Introduction](index)
+- [The imlib.cpp library module](imxlib)
\ No newline at end of file
diff --git a/developer/docs/help/guides/develop/imx/imxlib.md b/developer/docs/help/guides/develop/imx/imxlib.md
new file mode 100644
index 00000000000..178ae0f3eff
--- /dev/null
+++ b/developer/docs/help/guides/develop/imx/imxlib.md
@@ -0,0 +1,208 @@
+---
+title: The imlib.cpp library module
+---
+
+imlib.cpp, included in the development kit, contains a set of useful
+functions for interfacing to Keyman.
+
+## PrepIM
+
+```c
+BOOL PrepIM(void);
+```
+
+`PrepIM()` initialises the Keyman32 imports. You should not call any of
+the Keyman imports without calling `PrepIM()` first. If `PrepIM()`
+fails, you should exit without doing any processing.
+
+## IMDefWindowProc
+
+```c
+BOOL IMDefWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, LRESULT *lResult);
+```
+
+`IMDefWindowProc()` should be called from an IMC window procedure (see
+section titled Input Method Composition windows). If it returns `TRUE`
+you should return the value stored in `lResult` without any further
+processing. `IMDefWindowProc()` mostly manages window activation and
+movement.
+
+## Keyman Imports
+
+The DLL can call Keyman functions to interact with Keyman and the target
+application. It should not attempt to directly control the application
+as Keyman will be doing this. You should never call any of the functions
+here from the `KeymanIMConfigure()` callback.
+
+You can use `PrepIM()`, declared in imlib.cpp to get access to the the
+Keyman functions. When using imlib.cpp, the functions are declared as
+pointers, so you need to dereference them to call them in C (e.g. for
+KMGetContext, call `(*KMGetcontext)(buf,len);`)
+
+## KMGetContext
+
+```c
+BOOL WINAPI KMGetContext(PWSTR buf, DWORD len);
+```
+
+`KMGetContext()` returns the last `len-1` UTF-16 codepoints of the
+context stack. If there are not enough characters in the context stack,
+it will return as many as it can. On success, the `buf` variable will be
+null terminated.
+
+The context stack can contain a special code for deadkeys. See
+`KMQueueAction()` for a way to output a deadkey. The code sequence for a
+deadkey is (3 words):
+
+```
+UC_SENTINEL, CODE_DEADKEY, deadkeyID
+```
+
+`UC_SENTINEL` is `0xFFFF`; `CODE_DEADKEY` is `0x0008`; `deadkeyID` can
+be any value from `0x0001` to `0xFFFE`.
+
+> ### Note
+Changes in 8.0.333.0: a potential buffer overflow has been corrected.
+The size of the buffer pointed to by `buf` should be `WCHAR[len+1]`
+to allow for terminating null. Keyman now counts supplementary plane
+characters as 2 UTF-16 codepoints rather than as a single codepoint.
+Keyman no longer returns partial deadkey code sequences.
+
+## KMSetOutput
+
+```c
+BOOL WINAPI KMSetOutput(PWSTR buf, DWORD backlen);
+```
+
+`KMSetOutput()` is a wrapper for `KMQueueAction()`. It simplifies the
+process of deleting contextual characters and outputting a new string.
+The results will not be output to the screen until the current function
+returns. If called within the context of an IMC window, the results will
+not be output to the screen until the window posts the
+`wm_keymanim_close` message.
+
+`buf` is a pointer to a null-terminated string of characters to output.
+`backlen` is the number of characters to backspace from the current
+context before displaying `buf`.
+
+This function modifies the context returned from `KMGetContext()`, even
+if the output is not yet on the screen.
+
+Internally, this function does the following:
+
+```c
+while(backlen-- > 0) KMQueueAction(QIT_BACK, 0);
+while(*buf) KMQueueAction(QIT_CHAR, *buf++);
+```
+
+## KMQueueAction
+
+```c
+BOOL WINAPI KMQueueAction(int itemType, DWORD dwData);
+```
+
+`KMQueueAction()` lets you send any Keyman action to a target
+application. This can be virtual keys, characters, shift keys up and
+down, deadkeys, beeps, or backspaces (a special case of virtual keys).
+
+| | |
+|------------------|----------------|
+| itemType code | Description |
+| `QIT_VKEYDOWN` | Simulate any key press on the keyboard; `dwData` is the virtual key code |
+| `QIT_VKEYUP` | Simulate any key release on the keyboard; `dwData` is the virtual key code |
+| `QIT_VSHIFTDOWN` | Simulate pressing a set of shift keys. `dwData` can be a combination of the following flags: `LCTRLFLAG, RCTRLFLAG, LALTFLAG, RALTFLAG, K_SHIFTFLAG, K_CTRLFLAG, K_ALTFLAG` |
+| `QIT_VSHIFTUP` | Release the shift state, `dwData` is the same as the previous flags. |
+| `QIT_CHAR` | `dwdata` is any `WCHAR`. |
+| `QIT_DEADKEY` | `dwData` is any value from `0x0001` to `0xFFFE`. This can be matched in the context with `KMGetContext()`. |
+| `QIT_BELL` | `dwData` should be zero (`0`). |
+| `QIT_BACK` | `dwData` should be zero (`0`). |
+
+## KMHideIM
+
+```c
+BOOL WINAPI KMHideIM(HWND hwndIM);
+```
+
+`KMHideIM()` hides the IMC window referred to by `hwndIM` and ensures
+that Keyman processes input from the keyboard through the correct
+method. You should call this rather than hiding the window manually with
+`ShowWindow(hwnd, SW_HIDE);` or post the message `wm_keymanim_close` to
+hide the window.
+
+## KMDisplayIM
+
+```c
+BOOL WINAPI KMDisplayIM(HWND hwndIM, BOOL FCaptureAll);
+```
+
+`KMDisplayIM()` displays the IMC window referred to by `hwndIM`. It does
+not do any movement of the window. If the `FCaptureAll` flag is set, all
+keyboard input (character-generating keys only) will be redirected to
+the IMC window until the message `wm_keymanim_close` is posted,
+`KMHideIM()` is called, or `KMDisplayIM()` with `FCaptureAll` set to
+`FALSE`.
+
+## KMGetKeyboardPath
+
+```c
+BOOL WINAPI KMGetKeyboardPath(PSTR keyboardname, PWSTR dir, DWORD length);
+```
+
+This function returns the full path to the keyboard referred to by
+`keyboardname`. The buffer `dir` should be 260 characters long.
+
+## KMGetActiveKeyboard
+
+```c
+BOOL WINAPI KMGetActiveKeyboard(PSTR keyboardname, DWORD length);
+```
+
+This function can be called while processing to determine which is the
+active keyboard. Alternatively, use the callbacks `KeymanIMActivate()`
+and `KeymanIMDeactivate()`.
+
+## KMSendDebugString
+
+```c
+BOOL WINAPI KMSendDebugString(PSTR str);
+```
+
+This function outputs the string `str` to the Keyman debug window or
+debug log file (usually %USERPROFILE%\Desktop\keymanlog\system\*.log).
+
+## The Input Method Composition window
+
+The IMC window can be shown or hidden at any time that the associated
+keyboard is active. This means that you can have an IMC window
+permanently open or open at appropriate times.
+
+The keyboard IMSample included with Keyman is a good example of
+manipulating the IMC display.
+
+The window should be created invisible, most probaly as a popup window.
+The window can use `KMGetContext()`, `KMSetOutput()` at any time, but
+output will not be put to the screen until it has posted (not sent)
+`wm_keymanim_close` to itself.
+
+```c
+PostMessage(hwnd, wm_keymanim_close, (WPARAM) FSuccess, (LPARAM) FActuallyClose);
+```
+
+Keyman will manage the window display, focus, and message loop. The
+window procedure should set the position and size appropriately.
+
+Keyman will recognise this window and any child windows to be part of
+the IM and will not attempt to process any input that goes through the
+window.
+
+The IMC window must not take focus at any time.
+
+## Limitations
+
+- Clicks outside the window will cancel the IM and lose context.
+- Switching applications will cancel the IM and lose context.
+
+## See also
+
+- [DLL Interface for Keyman - Introduction](index)
+- [DLL Exports](imxdll)
\ No newline at end of file
diff --git a/developer/docs/help/guides/develop/imx/index.md b/developer/docs/help/guides/develop/imx/index.md
new file mode 100644
index 00000000000..6bc606b8f91
--- /dev/null
+++ b/developer/docs/help/guides/develop/imx/index.md
@@ -0,0 +1,101 @@
+---
+title: Input Method Extensions
+---
+
+Keyman's multiple group processing is powerful, but sometimes you need
+to be able to do something a bit more complex, such as a dictionary
+lookup. Keyman's DLL interface let you do this. You can call a function
+in a DLL in the same way as you call another group. The function can
+read the context, deadkeys and the current keystroke, and output
+characters, deadkeys, virtual keys, beeps and other items.
+
+The DLL interface also allows you to create a popup Input Method
+Composition (IMC) window. This window allows the user to select visually
+the characters they are wishing to input. The window can be set to be
+visible when the keyboard is active, or after an appropriate key
+sequence. When the window is visible, it can be set to capture all
+keyboard input, or be passive.
+
+## File locations
+
+The DLL should be placed in any of the following locations:
+
+- The same directory as the .kmx file (e.g. use a package to install it)
+- The Keyman program directory (same place as keyman32.dll)
+- Anywhere on the path (such as the Windows directory)
+
+The best option is the first, as you can then include the DLL in a
+Keyman package for easy installation and uninstallation.
+
+Full x64 support for IMX DLLs was introduced in Keyman Desktop
+8.0.333.0. An x64 version of the DLL may be included simply by giving it
+the file extension .x64.dll, with the same base name as the x86 DLL.
+Keyman will call the appropriate DLL.
+
+## General usage information
+
+DLL functions used in place of groups are called DLL group functions.
+
+All strings, apart from keyboard names, are passed as WCHAR, regardless
+of whether the active window is a Unicode window or not. In Keyman 8 and
+earlier, ANSI characters are represented as 16-bit WCHAR, with high bits
+zeroed out. In Keyman 9, ANSI keyboards are translated to Unicode at
+install time, through the CP-1252 code page.
+
+The DLL will be loaded for each process in which the keyboard is
+activated. Remember that the DLL will not share memory between these
+processes by default, so if you have large memory requirements, you
+should use memory mapped files or possibly SHARDATA segments to minimize
+the memory consumption.
+
+DLL group functions are called in a fairly time-critical environment. It
+is important that you minimise the processing time in these functions.
+It is essential that you avoid any window focus or activation – message
+boxes are definitely out of the question. For debugging purposes, there
+is a Keyman32.dll function exported for writing to the logfile (see the
+section titled Keyman32 imports).
+
+DLLs can handle multiple keyboards at once. The keyboards are identified
+by a name which is the filename of the keyboard, minus path and
+extension. For example, given c:\keyman\imsample\imsample.kmx, the
+keyboard name is imsample. These are the same names that Keyman uses
+internally, for example in the registry and directory names.
+
+## Registry settings
+
+Parameters for the DLL can be stored in two locations in the registry.
+They should always be stored under HKEY_CURRENT_USER, as the user will
+not have permission to change machine-wide settings, and the settings
+should not affect other users. The following locations are recommended:
+
+> HKEY_CURRENT_USER\Software\Tavultesoft\Keyman Engine\9.0\IMX\\[DLLName]
+
+> HKEY_CURRENT_USER\Software\Tavultesoft\Keyman Engine\9.0\Installed Keyboards\\[kbdname]
+
+The first key should be used for settings that pertain to any keyboard
+associated with the DLL. The second key should be used for any
+keyboard-specific settings. Values stored under the second key should be
+prefixed with the name of the dll, so that they will not conflict with
+Keyman or other dll values.
+
+## The .kmn interface
+
+Inside a .kmn file, you define the DLL group function interface as follows:
+
+```keyman
+store(DLLFunction) "myfile.dll:KeyEvent"
+```
+
+You can use this anywhere where you would place the use statement
+(except in the begin statement), with the call statement. For example,
+
+```keyman
++ 'a' > call(DLLFunction)
+```
+
+A single .kmn file can reference multiple DLL group functions, in a single or multiple DLLs.
+
+## See also
+
+- [DLL Exports](imxdll)
+- [The imlib.cpp library module](imxlib)
diff --git a/developer/docs/help/guides/develop/imx/web.md b/developer/docs/help/guides/develop/imx/web.md
new file mode 100644
index 00000000000..fa98ba40321
--- /dev/null
+++ b/developer/docs/help/guides/develop/imx/web.md
@@ -0,0 +1,4 @@
+---
+title: IMX Interface for KeymanWeb
+---
+
\ No newline at end of file
diff --git a/developer/docs/help/guides/develop/imx/web/index.md b/developer/docs/help/guides/develop/imx/web/index.md
new file mode 100644
index 00000000000..9453c5a01de
--- /dev/null
+++ b/developer/docs/help/guides/develop/imx/web/index.md
@@ -0,0 +1,5 @@
+---
+title: Guidelines for building IMX for KeymanWeb
+---
+
+* TODO: Write to fill requirements for .call_js, see call() statement for details.
diff --git a/developer/docs/help/guides/develop/index.md b/developer/docs/help/guides/develop/index.md
new file mode 100644
index 00000000000..8d8490c42c1
--- /dev/null
+++ b/developer/docs/help/guides/develop/index.md
@@ -0,0 +1,19 @@
+---
+title: Developing Keyboards
+---
+
+[Keyman keyboard tutorial](tutorial)
+
+[An advanced keyboard development example](advanced-keyboard-development-example)
+
+[Creating a simple touch keyboard layout](touch-keyboard-tutorial)
+
+[Creating a touch keyboard layout for Amharic - part 1](creating-a-touch-keyboard-layout-for-amharic)
+
+[Creating a touch keyboard layout for Amharic - part 2](creating-a-touch-keyboard-layout-for-amharic-the-nitty-gritty)
+
+[Input Method Extensions (IMX)](imx)
+
+------------------------------------------------------------------------
+
+[Keyman Developer 16 Tutorial](https://lingtran.net/Keyman-Developer-16-Tutorial) (external on Lingtran.net)
\ No newline at end of file
diff --git a/developer/docs/help/guides/develop/touch-keyboard-tutorial/index.md b/developer/docs/help/guides/develop/touch-keyboard-tutorial/index.md
new file mode 100644
index 00000000000..bc7c8e792f2
--- /dev/null
+++ b/developer/docs/help/guides/develop/touch-keyboard-tutorial/index.md
@@ -0,0 +1,54 @@
+---
+title: Creating a Simple Touch keyboard
+---
+
+## Introduction and design
+
+If you have experience in designing Keyman keyboards for computers, the
+interface for designing mobile keyboards looks very similar, but it
+hides the reality of additional steps necessary for mobile keyboards.
+This tutorial attempts to walk you through the process.
+
+The [Quick French tutorial](../tutorial/) shows how to create a simple
+desktop keyboard for typing accented characters used in French and other
+European languages. It is possible to create a touch screen format of
+that keyboard, but it is probably unnecessary. The default keyboards for
+both Android and iOS devices have a way to type these accented
+characters. Press and hold on a vowel, and a popup menu will give you a
+choice of accents to put on that letter.
+
+For this tutorial we will demonstrate something usable for the Fulfulde
+language cluster spoken across many Sahelian countries of Africa.
+Fulfulde is usually written using the Latin alphabet, but there are a
+few characters added to the usual list of alphabetical characters. So
+this is a useful example for any language wanting to add a few
+characters to the standard Latin keyboard. Here are the characters we
+are going to add, together with their Unicode values.
+
+| | | | | | | | | |
+|-----|----------|-----|----------|-----|-----|----------|-----|----------|
+| `ɓ` | `U+0253` | `Ɓ` | `U+0181` | | `ɗ` | `U+0257` | `Ɗ` | `U+018A` |
+| `ŋ` | `U+014B` | `Ŋ` | `U+014A` | | `ƴ` | `U+01B4` | `Ƴ` | `U+01B3` |
+
+Here we will likely want to proceed differently than with the desktop
+keyboard. The usual approach for a desktop keyboard is to create a
+deadkey. The keyboards I have used for Fulfulde (going back to decades
+ago) used the / key as the deadkey.
+/b gave ɓ,
+/d gave ɗ, and so on.
+
+It would be possible to use that desktop keyboard logic on touch
+screens. But your mobile device users will find these rules annoying.
+Instead of two keys they would probably have to press four, since the
+standard alphabetical keyboard on most touch devices does not have the
+slash key. So they would have to press the key to switch to the numeric
+keyboard, then press slash, then press the key to go back to the
+alphabetical keyboard, then the letter. What touch screen users would
+appreciate is something that uses the great feature of touch screens,
+the long press or press and hold on a letter, to see analogous letters.
+So we’ll set up a touch screen keyboard that lets you press and hold ‘d’
+to see the two hooked d characters, press and hold ‘b’ to see the hooked
+b characters, and so on.
+
+[Next: Making, testing and distributing the touch
+keyboard](making-touch-keyboard)
diff --git a/developer/docs/help/guides/develop/touch-keyboard-tutorial/making-touch-keyboard.md b/developer/docs/help/guides/develop/touch-keyboard-tutorial/making-touch-keyboard.md
new file mode 100644
index 00000000000..5039acb2624
--- /dev/null
+++ b/developer/docs/help/guides/develop/touch-keyboard-tutorial/making-touch-keyboard.md
@@ -0,0 +1,361 @@
+---
+title: Making a touchscreen keyboard
+---
+
+## Setting up your Keyman project
+
+This is a useful convention for keeping track of your files:
+
+1. Create a folder on your hard drive for all your Keyman developer
+ files
+2. In that folder, create a folder for this project, named after the
+ language. Use all lowercase letters for compatibility with other
+ platforms.
+3. Now create a project in Keyman Developer with the same name as the
+ new folder, and save it inside the new folder
+
+Now you will be able to use this project to organize and create any
+files for desktop, mobile, web or other. It also facilitates uploading
+the files to the Keyman website later on.
+
+## Add touch to an existing keyboard or create a new keyboard?
+
+You can add a mobile touchscreen keyboard to an existing desktop
+keyboard, or you can create a new touchscreen keyboard from scratch. You
+should be aware that there will be cross-over between mobile and desktop
+keyboards. (In Keyman terms, "desktop" includes laptop computers or any
+device with a physical keyboard). Your mobile device can have an
+external keyboard, which will use the desktop Keyman keyboard. So the
+Keyman strategy is to always bundle touch keyboards with desktop
+keyboards to make them work on multiple platforms.
+
+If creating from scratch, you click the New icon in the Keyman Developer
+toolbar (or select File > New from the menu), and specify the file
+name for your keyboard. We recommend a name using only lowercase
+letters, numbers and underscore. I’ll choose “mobile_fulfulde.kmn” .
+
+![choose new file
+type](../../../images/simpleTouchKeyboard_1.png)
+
+## Adding the required metadata
+
+After creating the file, Keyman Developer will ask you to name the
+keyboard. This name can include spaces and upper case letters if
+desired. I’ll name this one “Fulfulde for Mobile”.
+
+![keyboard
+metadata](../../../images/simpleTouchKeyboard_2.png)
+
+The first thing you need to do is to tell Keyman Developer this keyboard
+will include some kind of mobile device. You do this in the Targets
+list just below the keyboard name. I’ll select “mobile ” in the list,
+(towards the end). You see in the picture above that Keyman Developer
+checked the “windows” box by default, I can uncheck that or leave it
+checked if I am going to add a Windows keyboard to this set.
+
+![mobile target](../../../images/simpleTouchKeyboard_3.png )
+
+This will include all mobile devices, both Android and iOS, both phones
+and tablets. It is possible if needed to select Android only or iOS only
+if needed, and to develop a different keyboard layout for tablets than
+for phones, but for this example, with only a few characters needed to
+be added to the default keyboards, there is no need for that kind of
+complexity.
+
+(If you are adding a mobile layout to an existing keyboard file, you
+would open that file in Keyman Developer, go to the Targets box and
+check “mobile” or whatever mobile targets you wanted to specify, then
+follow along from here).
+
+## Adding the touch optimized feature
+
+The next step is to add the touch-optimised feature to our keyboard
+setup. If you scroll to the bottom of the details tab, there is the list
+of features to add. I’ll click the Add button and choose “touch
+optimised keyboard.”
+
+![touch-optimised](../../../images/simpleTouchKeyboard_4.png)
+
+Keyman developer asks me to choose one of three templates for the mobile
+layout.
+
+![three templates](../../../images/simpleTouchKeyboard_5.png)
+
+The “template-latin” layout includes many accented characters, like the
+default keyboards for Android and iOS do. The “template-basic” layout
+has no extra characters, and the template-traditional has a few extra
+characters (mostly punctuation symbols if you long-press period or full
+stop). For this example, I’ll choose “template-basic” because accented
+characters are not needed for Fulfulde. But if I knew that many eventual
+users might want to be able to type French or other European languages
+as well as Fulfulde, and wouldn’t like to have to switch the keyboard to
+do so, I could choose “template-latin”.
+
+## Defining one or several touch layouts
+
+I select the template and click OK. Keyman Developer now adds a new tab
+in the left column, the Touch Layout tab.
+
+![touch layout](../../../images/simpleTouchKeyboard_6.png)
+
+That is where we need to go now to add characters to our touch layout.
+But before doing that, there is one important thing to take note of in
+the touch layout. By default, Keyman Developer has created a layout for
+phones and another layout for tablets. They all start out the same, but
+as I add characters, I will have to make sure I add all the characters
+to both layouts, or I’ll discover that my keyboard does not work for all
+device sizes.
+
+In the image, you see in the Platform box that Keyman Developer is currently showing the
+tablet layout. This box is where I’d choose which layout I’d design in.
+
+But I’m going to make this keyboard simple, and just have one layout for
+both tablets and phones. So in the platform box, I am going to click
+the “minus” button, to delete the tablet layout. When there is only one
+mobile layout, Keyman Developer will compile that layout for all mobile
+devices. (I could also have chosen to delete the phone layout and done
+my layout design in the tablet layout).
+
+![platform list](../../../images/simpleTouchKeyboard_7.png)
+
+## Adding longpress characters
+
+Now I can start adding characters. I’ll click on the “y” key in the
+layout, then I’ll look at the “longpress keys” area at the lower
+left.
+
+![add longpress](../../../images/simpleTouchKeyboard_8.png)
+
+I click that, and Keyman Developer adds a new key below the keyboard
+layout.
+
+![longpress added](../../../images/simpleTouchKeyboard_9.png)
+
+I'm going to use this key for the y with hook. I click inside it to make it active, then I’ll go look for my y
+with hook character in the character map to the right. If I type “y
+hook” in the search box, Keyman Developer shows the character I’m
+looking for.
+
+![adding keycap](../../../images/simpleTouchKeyboard_10.png)
+
+If I double click on the lower case y with hook, that copies the character
+to the keycap box of my new key. (I could also click and drag the character from the character map to the key).
+
+![keycap added](../../../images/simpleTouchKeyboard_11.png)
+
+## New key info
+
+There are several important pieces of information that got copied over. If we
+look at the properties of the new key to the right, we see that this new key has
+the “text” ƴ (what appears on the keycap), the “Unicode value” U+01B4 and the
+“ID” U_01B4. All of these were copied from the character map.
+
+NOTE: If you copy one character from the character map to a new key, then
+realize you copied the wrong character, dragging a second character may not
+replace all the needed information into the key. If a key has a character in it
+already, it works better to hold Ctrl as you drag the new character
+into it.
+
+I could add the other three characters; hook b, hook d, and eng, as longpress
+characters as well. I could select the regular letter I want to longpress,
+click plus in the longpress keys area and copy the needed character. Here for
+example is the b with hook:
+
+![keycap added](../../../images/simpleTouchKeyboard_12.png)
+
+After I have added my four lowercase characters as longpress keys, the keyboard
+looks like this. Even when I don't have that letter selected, I can see which
+keys have longpress keys by the dot beside the letter.
+
+![keycap added](../../../images/simpleTouchKeyboard_12-2.png)
+
+[See the Keyman Language guide on virtual keys](../../../../language/guide/virtual-keys#toc-virtual-keys-and-touch-layouts)
+
+## Adding uppercase characters
+
+I could do two different things for the upper case letters. I could add them as
+longpress keys beside their lowercase equivalents, by pressing the plus key
+again to add a second longpress character to each letter.
+
+This would mean users would not have to press Shift to be able to type the upper
+case hook y. But since a user needs to press Shift for all the regular Latin
+upper case letters, it might make more sense to put the uppercase letters into
+the Shift layer.
+
+I can change to the shift layer by finding the layer box at lower left, and
+choosing `shift` instead of `default`.
+
+![changing to shift layer](../../../images/simpleTouchKeyboard_13.png)
+
+Now I see the upper case letters on the keyboard, and I can add longpress
+characters to these in the same way I did on the default layer. I click the
+letter, then click `+` to add a key, then copy the character info from the
+character map. Adding my upper case y hook to Y would look like this:
+
+![new key added to row](../../../images/simpleTouchKeyboard_14.png)
+
+(If I had clicked on the green triangle to the left of my first key, the
+new key would be added to the left of that key).
+
+For this new key, I go through the same steps. I click in the empty box
+on the new key, then double click the upper case y with hook in the
+character map to put that in the keycap. I also change the code of this
+new key to “U_01B3”
+
+![keycap and code added to new
+key](../../../images/simpleTouchKeyboard_15.png)
+
+## Another option for upper case characters
+
+I might also add the upper case character to a longpress character on
+the upper-case Y key, because some users will press the Shift key then
+look for the letter to type. To do this, I change the layer at the top
+of the tablet layout to "shift" from "default".
+
+![changing
+layer](../../../images/simpleTouchKeyboard_15b.png)
+
+Now I click the Y key, and add a longpress character, then add the upper
+case y with hook to the keycap and add the keycode as shown before.
+
+![key added to longpress
+Y](../../../images/simpleTouchKeyboard_16b.png)
+
+You can design your keyboard either way, with the upper case characters
+in the same longpress row as the lower case, or in a longpress row off
+of the shift layer. The rest of this tutorial shows the upper case
+characters in the same row as the lower case, but I don't mean to
+present this as the best way. If you do put upper case characters in the
+shift layer, you'll have to remember to change the layer back to
+"default" when you want to add a lower case letter, then back to "shift"
+when adding an upper case letter. You could put the upper case
+characters in both places, if you think that best.
+
+Now I’ll add the two d hook characters as a longpress popup for the d
+key. I click the d key in the layout, click “Add longpress popup”, add
+the lower case hook d to the keycap and change the code to “U_0257”;
+then click the plus sign to add a second key, add the upper case hook D
+to the keycap and change its code to “U_018A”. The end result should
+look like this:
+
+![longpress row on
+d](../../../images/simpleTouchKeyboard_16.png)
+
+I’ll do the similar step to add two characters as a longpress popup for
+“b”
+
+![longpress row on
+b](../../../images/simpleTouchKeyboard_17.png)
+
+And two characters as a longpress popup for “n”. (Tip, to search for ŋ
+in the character map, type “eng”).
+
+![longpress row on
+n](../../../images/simpleTouchKeyboard_18.png)
+
+When no keys are selected in the keyboard layout, I can still see which
+ones I have set up longpress popups for, because Keyman Developer shows
+a faint gray line at the top right corner of the key. So you should have
+a faint diagonal line on your y, d, b and n keys.
+
+![lines on longpress
+keys](../../../images/simpleTouchKeyboard_19.png)
+
+## Replacing the Unicode and Code automatically
+
+1. Hold the Ctrl key while dragging and
+ dropping a character from the Character Map to the touch layout
+ editor, and it will replace the Code as well as the character.
+2. Hold the Shift key while dropping, and it
+ will only add the character to the key.
+3. Finally, hold
+ Ctrl+Shift, this
+ will add the character to the key AND update the Code to match the
+ full set of characters represented on the key.
+
+## Compiling your keyboard
+
+Now click Save to save your work, and compile the keyboard. (Keyboard
+> Compile Keyboard, or press F7). Hopefully you’ll see it compiled
+with no error messages:
+
+![successful
+compile](../../../images/simpleTouchKeyboard_20.png)
+
+If you see a warning like:
+
+```
+Warning: line 0 warning 2092: Key " T_new_579" on layer "default", platform "phone", is a custom key but has no corresponding rule in the source.
+```
+
+This shows you forgot to enter the correct keycode for a new key you
+added. Double check your longpress popup keys for one with a “T_new_NNN”
+code, and add in the desired “U_NNNN” code.
+
+You might also see a message like
+
+```
+Error: line 0 error 405A: Key " U+0181" on "phone", layer "default" has an invalid identifier.
+```
+
+This shows you attempted to change the keycode but mistyped the code.
+I’ve gotten this more than once, because I am used to typing `U+0181`to
+refer to a Unicode value, but the correct syntax for a keycode in Keyman
+Developer is `U_0181`
+
+# Testing your keyboard
+
+Other Keyman help pages describe how you can test your keyboard:
+
+- [On a virtual device in the Chrome web browser on your
+ computer](../../test/keyboard-touch-mobile-emulator)
+- [On your own device accessing the keyboard over the
+ network](../../test/keyboard-touch-and-desktop)
+
+If your device cannot connect to your computer on the local network (for
+instance if your computer is on a wired office network and your device
+is on the wireless office network), the above technique for sharing may
+not work. You can put your keyboard in a package and share it over the
+Internet with your device, as described below.
+
+# Packaging and distributing your keyboard
+
+Another Keyman help file describes [how to build a package
+file.](../../distribute/tutorial)
+
+## BCP 47 tags for Fulfulde
+
+[Step 3](../../distribute/tutorial/step-3) of the package building
+tutorial describes adding the BCP 47 language tag to your package. When
+I came to the step of adding a tag for Fulfulde, I had to do some
+research. I naively thought "Fulfulde" was a clear enough label for the
+language. It is not, since it is a language chain rather than one
+language. The BCP 47 tag in many cases is the same as the [SIL
+Ethnologue code](https://www.ethnologue.com), so that is where I had to
+go to find the tags I needed. In the end I added four tags for four
+Fulfulde languages:
+
+![BCP 47 tags](../../../images/simpleTouchKeyboard_36.png)
+
+I also added the script designator `Latn` for Latin script. This is
+recommended for Windows, which sometimes fails to install the desktop
+keyboard if there is not a script designator on the BCP 47 tag. We
+recommend that you always put in the script tag unless there is a
+specific reason not to.
+
+Some languages have only two letters in their BCP 47 tag. For instance,
+the Hausa language has an Ethnologue code `hau` but the BCP 47 tag is
+`ha`. Keyman Developer knows about this, and will change the tag to `ha`
+if I put in the Ethnologue tag.
+
+# Next steps
+
+The two following articles on Amharic describe a more complex
+touchscreen example where quite frequently two keys are pressed to get
+one distinct character. (Amharic is a Abugida or syllabary script, where
+one character can represent a consonant and following vowel).
+
+- [Creating a touch keyboard layout for Amharic - part
+ 1](../creating-a-touch-keyboard-layout-for-amharic)
+- [Creating a touch keyboard layout for Amharic - part
+ 2](../creating-a-touch-keyboard-layout-for-amharic-the-nitty-gritty)
\ No newline at end of file
diff --git a/developer/docs/help/guides/develop/tutorial/index.md b/developer/docs/help/guides/develop/tutorial/index.md
new file mode 100644
index 00000000000..3642ddc1ed5
--- /dev/null
+++ b/developer/docs/help/guides/develop/tutorial/index.md
@@ -0,0 +1,56 @@
+---
+title: Keyboard Tutorial
+---
+
+[Step 1: Planning the Keyboard](step-1)
+
+[Step 2: Writing the Header](step-2)
+
+[Step 3: The Keyboard Header](step-3)
+
+[Step 4: The Keyboard Body](step-4)
+
+[Step 5: Rules with Context](step-5)
+
+[Step 6: Stores, 'any', and 'index'](step-6)
+
+[Step 7: Testing the Keyboard](step-7)
+
+[Step 8: Deadkeys](step-8)
+
+[Step 9: The Finished Keyboard](step-9)
+
+## Overview
+
+Welcome! In this tutorial, you will learn the basics of Keyman keyboards
+and create a simple French keyboard which can be used with any physical
+keyboard layout.
+
+The keyboard will use a basic English layout, and add some deadkeys to
+define vowel diacritics and a few other characters.
+
+While this tutorial is based on a physical keyboard such as used on a
+desktop or laptop computer, the principles of the keyboard language that
+you will learn are applicable to developing touch layouts as well.
+
+This keyboard will use Unicode. Unicode is a character encoding standard
+that supports most of the world's scripts, and includes support for
+user-defined scripts. Unicode is the accepted standard for text encoding
+in modern applications and operating systems.
+
+In Keyman Developer keyboard source files, Unicode characters are
+specified with `U+xxxx`, where `xxxx` is a four-digit hexadecimal
+number.
+
+At the bottom of each page in the tutorial, will be a link to both the
+previous page and the next page. You can use these links to work your
+way through the tutorial. You may also find links to reference
+information, which you can select to learn more about a particular
+aspect of creating Keyman keyboards.
+
+## Let's begin
+
+Let's get started! Move on to the next topic to begin the first step,
+planning the keyboard.
+
+- [Continue with Step 1: Planning the Keyboard](step-1)
\ No newline at end of file
diff --git a/developer/docs/help/guides/develop/tutorial/step-1.md b/developer/docs/help/guides/develop/tutorial/step-1.md
new file mode 100644
index 00000000000..642fa916bba
--- /dev/null
+++ b/developer/docs/help/guides/develop/tutorial/step-1.md
@@ -0,0 +1,71 @@
+---
+title: Step 1: Planning the Keyboard
+---
+
+## Choosing the characters
+
+First of all, we need to decide which characters we want the keyboard to
+produce. Next, we must find out the codes used to represent them, using
+a program such as Character Map, or with the Character Map in Keyman
+Developer (go to **View**, **Character Map**).
+
+French uses the same 26 letters as English, with some additions. As our
+keyboard is based on English, we only need to work with these additional
+letters. Note that for completeness, we will design our keyboard to
+produce a few other accented vowels that are not used in French. Also,
+we want our keyboard to include the angled quotes `«` and `»`.
+
+These characters, with both uppercase and lowercase forms, are listed in
+the table below along with their Unicode codes.
+
+| | | | | | | | | |
+|-----|----------|-----|----------|-----|-----|----------|-----|----------|
+| `À` | `U+00C0` | `à` | `U+00E0` | | `Á` | `U+00C1` | `á` | `U+00E1` |
+| `È` | `U+00C8` | `è` | `U+00E8` | | `É` | `U+00C9` | `é` | `U+00E9` |
+| `Ì` | `U+00CC` | `ì` | `U+00EC` | | `Í` | `U+00CD` | `í` | `U+00ED` |
+| `Ò` | `U+00D2` | `ò` | `U+00F2` | | `Ó` | `U+00D3` | `ó` | `U+00F3` |
+| `Ù` | `U+00D9` | `ù` | `U+00F9` | | `Ú` | `U+00DA` | `ú` | `U+00FA` |
+| `Â` | `U+00C2` | `â` | `U+00E2` | | `Ä` | `U+00C4` | `ä` | `U+00E4` |
+| `Ê` | `U+00CA` | `ê` | `U+00EA` | | `Ë` | `U+00CB` | `ë` | `U+00EB` |
+| `Î` | `U+00CE` | `î` | `U+00EE` | | `Ï` | `U+00CF` | `ï` | `U+00EF` |
+| `Ô` | `U+00D4` | `ô` | `U+00F4` | | `Ö` | `U+00D6` | `ö` | `U+00F6` |
+| `Û` | `U+00DB` | `û` | `U+00FB` | | `Ü` | `U+00DC` | `ü` | `U+00FC` |
+| `Ý` | `U+00DD` | `ý` | `U+00FD` | | `Ç` | `U+00C7` | `ç` | `U+00E7` |
+| `«` | `U+00AB` | `»` | `U+00BB` | | | | | |
+
+If you are not familiar with the hexadecimal (base-16) numbering system,
+don't worry: you can use the Character Map in Keyman Developer to find
+the character you want, and then drag-and-drop or copy-and-paste its
+character code into your keyboard.
+
+Note that you must be careful to use the right character: The Unicode
+standard has many characters with the same shape as another, but a
+different meaning; an example of this is the Greek capital letter Sigma
+(`U+03A3` Σ) and the mathematical summation symbol (`U+2211`, ∑). An application
+supporting Unicode would treat these two characters differently. If in
+doubt whether a character is the right one, you can look up the
+reference tables at [www.unicode.org](http://www.unicode.org/).
+
+## Designing the layout
+
+After choosing the characters we want our keyboard to use, we must
+decide how we want the user to be able to enter them. For some
+languages, you might replace each letter on the English keyboard with a
+letter from the language. In this case, however, most of the letters are
+accented vowels, we will use two keystrokes for each: one for the
+accent, and one for the vowel.
+
+| Character | Keystrokes |
+|-------------|-------------------------------|
+| `À, à, ...` | back-quote (`), then the vowel key. |
+| `Á, á, ...` | quote ('), then the vowel key. |
+| `Â, â, ...` | caret (^), then the vowel key. |
+| `Ä, ä, ...` | double-quote ("), then the vowel key. |
+| `Ç, ç` | quote ('), then lower- or uppercase C. |
+| `«, »` | two less-than (<<) or greater-than (>>) symbols. |
+
+Now that we have decided which character to use and how the user can
+enter them, we can start to write the keyboard.
+
+- [Continue with Step 2: Writing the Header](step-2)
+- [Back to the Introduction](index)
\ No newline at end of file
diff --git a/developer/docs/help/guides/develop/tutorial/step-2.md b/developer/docs/help/guides/develop/tutorial/step-2.md
new file mode 100644
index 00000000000..5867973fb39
--- /dev/null
+++ b/developer/docs/help/guides/develop/tutorial/step-2.md
@@ -0,0 +1,53 @@
+---
+title: Step 2: Writing the Header
+---
+
+## Overview of a keyboard file
+
+A keyboard file is divided into two sections: the **header** and the
+**rules** section. The header section defines the name of the keyboard,
+its bitmap, and other general settings. The rules are used to define how
+the keyboard responds to keystrokes from the user, and are divided into
+groups. We will start by writing the header.
+
+## The keyboard header
+
+The keyboard header is the first part of a keyboard; it consists of
+statements that help Keyman identify the keyboard and set default
+options for it. Each statement in the header must be on a separate line.
+While there is no technical requirement to put header statements at the
+start of a keyboard source file, keeping them there helps you identify
+them easily, and keeps them consistent with keyboard programs other
+people might write.
+
+We will begin to write the keyboard. If Keyman Developer is not already
+running, start it now. Create a new keyboard file, navigate to the
+**Layout** tab, and click on the **Code** tab at the bottom of the
+screen to switch to the Code view.
+
+Type or paste the following code into the keyboard file. This is the
+header of our keyboard file.
+
+```keyman
+c Simplified French Keyboard for Keyman 9.0
+c
+c This keyboard program uses a simplified set of keys
+c for typing French, especially for those who don't know the
+c standard French keyboard.
+c
+c NOTE: This keyboard was created from the Keyman keyboard
+c programming tutorial.
+
+store(&Version) "9.0" c This keyboard is for use with Keyman 9.0
+store(&Name) "Quick French"
+store(&Bitmap) "qfrench.ico"
+store(&MnemonicLayout) "1" c This keyboard uses a mnemonic layout.
+
+begin Unicode > use(Main)
+```
+
+An explanation of the various parts of the header follows on the next
+page.
+
+- [Continue with Step 3: The Keyboard Header](step-3)
+- [Back to Step 1: Planning the Keyboard](step-1)
\ No newline at end of file
diff --git a/developer/docs/help/guides/develop/tutorial/step-3.md b/developer/docs/help/guides/develop/tutorial/step-3.md
new file mode 100644
index 00000000000..81777aec6a0
--- /dev/null
+++ b/developer/docs/help/guides/develop/tutorial/step-3.md
@@ -0,0 +1,99 @@
+---
+title: Step 3: The Keyboard Header
+---
+
+## Comments
+
+```keyman
+c Simplified French Keyboard for Keyman 9.0
+```
+
+Most of the header in this example is made up of
+[comments](/developer/language/guide/comments). A comment is used
+to make notes about the keyboard, or to provide information on the
+workings of the keyboard. The comments are readable by anyone looking at
+the source code of the keyboard.
+
+A comment always starts with a lowercase `c`, followed by one or more
+spaces, and continues to the end of the line. Keyman Developer will
+ignore comments when compiling a keyboard.
+
+Comments can take up a whole line, or can start in the middle of the
+line. The latter is useful for making short notes about individual
+lines. As you can see we have used both kinds of comments in the header.
+
+## The `&Version` store
+
+```keyman
+store(&Version) "9.0" c This keyboard is for use with Keyman 9.0
+```
+
+The [`&Version` store](/developer/language/reference/version)
+identifies the Keyman version for which this keyboard was written; this
+keyboard is for use with Keyman 9.0. The `&Version` store is an optional
+part of the keyboard header, but if present, it should be the first
+store in the file.
+
+## The `&Name` store
+
+```keyman
+store(&Name) "Quick French"
+```
+
+The [`&Name` store](/developer/language/reference/name) specifies
+a descriptive name for the keyboard, which can be up to eighty
+characters long. The name we have given to this keyboard is
+`"Quick French"`. The `&Name` store is not required but is highly
+recommended!
+
+## The `&Bitmap` store
+
+```keyman
+store(&Bitmap) "qfrench.ico"
+```
+
+The optional [`&Bitmap` store](/developer/language/reference/bitmap)
+tells Keyman which image to use for the keyboard's icon. The picture
+should be in the standard Windows .ico format, and should contain at
+least a single 16x16 pixel image. It can also contain higher resolution
+images for high resolution "High DPI" displays. If you use a modern icon
+editor, the icon can use alpha transparency.
+For this keyboard we will be using the following bitmap:
+![](../../../images/tutorial_keyboard_qfrench.gif); it is
+found in the Keyman Developer folder, under
+`Samples\Examples\qfrench.ico` - you should copy it into the same folder
+in which you will save your keyboard.
+
+## The `&MnemonicLayout` store
+
+```keyman
+store(&MnemonicLayout) "1"
+```
+
+The [`&MnemonicLayout` store](/developer/language/reference/mnemoniclayout) tells Keyman that
+the layout is meant to conform to the user's keyboard layout; for
+example, if the user presses the quote key ' on
+their keyboard (whether they are using a US English, UK English, French,
+German, Swedish, or other keyboard) it should work in the same way. The
+opposite of this is a positional layout (which is the default if this
+store omitted), which is intended for keyboards for which there is not
+necessarily a correspondence between what is printed on the physical
+keyboard and what is output when that key is pressed.
+
+## The `begin` statement
+
+```keyman
+begin Unicode > use(Main)
+```
+
+The [`begin` statement](/developer/language/reference/begin) tells
+Keyman which group of rules to process first when it receives a
+keystroke. The use of multiple groups is an advanced feature, and
+unnecessary for this tutorial, so we will use a single group, called
+`Main`. The `begin` statement is required in every keyboard, and marks the
+start of the keyboard body. The `begin` statement also tells Keyman
+which encoding to use for the keyboard. Nearly all keyboards will use
+`Unicode`, today.
+
+- [Continue with Step 4: The Keyboard Body](step-4)
+- [Back to Step 2: Writing the Header](step-2)
\ No newline at end of file
diff --git a/developer/docs/help/guides/develop/tutorial/step-4.md b/developer/docs/help/guides/develop/tutorial/step-4.md
new file mode 100644
index 00000000000..e8a861ae1cd
--- /dev/null
+++ b/developer/docs/help/guides/develop/tutorial/step-4.md
@@ -0,0 +1,102 @@
+---
+title: Step 4: The Keyboard Body
+---
+
+The body of the keyboard is the most important part: it determines the
+behaviour of the keyboard. The body consists of groups, which in turn
+contain one or more rules which define the responses of the keyboard to
+certain keystrokes.
+
+## Groups
+
+There are two types of groups:
+* groups that process the keys pressed and the context.
+* groups that process only the context.
+
+For simple keyboards, the latter type of group will not be required. A group begins
+with a [`group` statement](/developer/language/reference/group), and
+ends either at the start of another group, or at the end of the keyboard
+file.
+
+We will only use one group in the Quick French keyboard, called `Main`.
+We mark the start of it with the `group` statement below. Add this line
+to the keyboard if it's not already there.
+
+```keyman
+group(Main) using keys
+```
+
+The `using keys` clause tells Keyman that this group will process
+keystrokes.
+
+## Basic Rules
+
+A rule tells Keyman the output to produce for a certain input. A rule
+consists of three parts: the **context**, the **key**, and the
+**output**.
+
+* The **context** specifies the conditions under which a rule will act. If
+what is shown in the document to the left of the cursor matches the
+context of a rule, the rule will be processed.
+* The **key** specifies which keystroke the rule will act upon.
+* The **output** determines the characters that are produced by a rule.
+The output replaces the matched context in the document.
+
+## Simple rules
+
+The simplest rules in Keyman consist of just a key and output, as below.
+(The examples in this section are just for illustration, and do not form
+part of the Quick French keyboard).
+
+```keyman
++ "a" > "ä"
+```
+
+In this rule, the key is a, and the output is `"ä"`. A simple rule
+begins with a plus sign, followed by the key, a greater-than symbol
+(suggesting "becomes"), and finally the output. As you might guess, this
+rule will change a lowercase a key typed by the
+user into **ä**.
+
+The key and output can be written as a character in single or double
+quotes (as above), or as its Unicode character code, or using a named
+constant. The rule above could have also been written any of the
+following ways, among others:
+
+```keyman
++ 'a' > 'ä'
+
++ U+0061 > U+00E4
+
+store(ADIERESIS) 'ä'
++ 'a' > $ADIERESIS
+```
+
+You can also write the key in one form and the context in another.
+
+## Rules with longer output
+
+The output of a rule is not limited to a single character. You could,
+for example, write a rule such as the following:
+
+```keyman
++ "f" > "ph"
+```
+
+This would change any f keys typed into **ph**.
+If the output of a rule consists of more than one character, you can
+write the characters in different ways if necessary, with a space
+separating each part. You can specify multiple characters in quotes, but
+if you use the Unicode codes to write the characters, you must separate
+each with a space:
+
+```keyman
++ "f" > U+0070 U+0068
+
++ "f" > U+0070 "h"
+```
+
+These rules are functionally identical to the one [further above](#toc-simple-rules).
+
+- [Continue with Step 5: Rules with Context](step-5)
+- [Back to Step 3: The Keyboard Header](step-3)
\ No newline at end of file
diff --git a/developer/docs/help/guides/develop/tutorial/step-5.md b/developer/docs/help/guides/develop/tutorial/step-5.md
new file mode 100644
index 00000000000..88a76bce340
--- /dev/null
+++ b/developer/docs/help/guides/develop/tutorial/step-5.md
@@ -0,0 +1,76 @@
+---
+title: Step 5: Rules with Context
+---
+
+## Rules with Context
+
+Very often we want a keyboard to produce different output based on more
+than just the last keystroke. For example, in the Quick French keyboard,
+we want the key e to produce one of è, é,
+ë, ê, or just e, depending on what was typed immediately before
+it. To do this we must make our rules look at the context.
+
+The context is the output from previous rules; that is, the characters
+that are displayed on the screen. We can make a rule work with only
+certain context by putting this before the plus sign in the rule:
+
+```keyman
+"^" + "e" > "ê"
+```
+
+With this rule, whenever an e is typed, if it
+was preceded by a caret (^), the output will be ê. It is important
+to remember that the context consists of **output
+from previous rules**, not the previous keystrokes. To emphasize
+this point, consider the four rules below:
+
+```keyman
++ "a" > "b"
++ "b" > "c"
+"b" + "c" > "d"
+"c" + "d" > "e"
+```
+
+With these rules, typing bc would produce the
+output `cc`, and not `d`, as you might initially expect. This is because
+the key b is converted by the second rule into
+the output `c`, while the third rule expects a context of b, and not
+c; we would have to type ac to get `d`.
+
+However, if a key has no matching rule, the output will be the same as
+the key: so the output `e` will be produced for either of the inputs cd (because c by itself has no rule), and bd.
+
+## Continuing the Quick French keyboard
+
+Now we know how to create context-dependent rules, we can continue
+making the Quick French keyboard. Let's start with the rules for
+acute-accented characters, using the ANSI codes from the table we
+prepared earlier:
+
+```keyman
+c lowercase characters with acute accent
+"'" + "a" > U+00E1
+"'" + "e" > U+00E9
+"'" + "i" > U+00ED
+"'" + "o" > U+00F3
+"'" + "u" > U+00FA
+"'" + "y" > U+00FD
+
+c uppercase characters with acute accent
+"'" + "A" > U+00C1
+"'" + "E" > U+00C9
+"'" + "I" > U+00CD
+"'" + "O" > U+00D3
+"'" + "U" > U+00DA
+"'" + "Y" > U+00DD
+```
+
+We can also create similar rules for the other thirty-odd accented
+characters.
+
+As you can see, even for a simple keyboard like this we quickly end up
+with a large number of rules, which makes for clumsiness. We can make
+things simpler using stores, and the `any()` and `index()` statements.
+
+- [Continue with Step 6: Stores, `any()`, and `index()`](step-6)
+- [Back to Step 4: The Keyboard Body](step-4)
\ No newline at end of file
diff --git a/developer/docs/help/guides/develop/tutorial/step-6.md b/developer/docs/help/guides/develop/tutorial/step-6.md
new file mode 100644
index 00000000000..95ade8767c6
--- /dev/null
+++ b/developer/docs/help/guides/develop/tutorial/step-6.md
@@ -0,0 +1,116 @@
+---
+title: Step 6: Stores, any(), and index()
+---
+
+When we have many similar rules, as in the last example on the previous
+page, we can group them together into one rule by using stores. A store
+is a set of characters that are grouped under a single name. Stores are
+used in rules with the [`any()`](/developer/language/reference/any) and
+[`index()`](/developer/language/reference/index) statements. We create a
+store with a [`store()` statement](/developer/language/reference/store):
+
+```keyman
+store(vowels) "aeiou"
+```
+
+This creates a store called `vowels`, which contains the five lowercase
+vowels.
+> #### Note:
+We could also have written the content of the store
+using ANSI or Unicode character codes, in the same way as the output.
+
+The `any()` statement is used to match a character from a specific
+store. For example, the following rule will replace any vowel with a
+period, when used with the store above:
+
+```keyman
++ any(vowels) > "."
+```
+
+The `any()` statement can be used in the context or in the key part of a
+rule. It cannot be used in the output.
+
+The second statement that is used with stores is the `index()`
+statement. It is usually used in the output of a rule, and will output
+the character from a particular store at the same position as the
+character matched by a specified `any()` statement. This is best shown
+with an example; this rule will convert all input to uppercase:
+
+```keyman
+store(lowercase) "abcdefghijklmnopqrstuvwxyz"
+store(uppercase) "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+
++ any(lowercase) > index(uppercase,1)
+```
+
+When a letter, such as j is typed, the `any()`
+statement finds its position in the `lowercase` store; the `index()`
+statement then gets this index from the `any()` statement, and outputs
+the character at the same position in the `uppercase` store, in this
+case `J`.
+
+The `index()` statement has two parts: the store from which it takes the
+output character, and the offset of the `any()` statement that it gets
+the character position from. This offset is found by counting the
+characters and statements in the context and key parts of the rule up to
+the `any()` statement. Again, a few examples may help to illustrate
+this:
+
+```keyman
+"a" + any(somestore) > index(otherstore,2) c The 'any' statement
+ c is character #2
+"ab" any(somestore) + "c" > index(otherstore,3) c The 'any' statement
+ c is character #3
+
+c Here the 'index' statement references the second 'any' statement used,
+c which is character #4
+U+0041 any(somestore) "B" + any(otherstore) > index(thirdstore,4)
+
+c You can have multiple 'index' statements in the output, which can
+c reference the same or different 'any's
+any(store1) + any(store2) > index(store1,2) index(store2,1) index(store3,2)
+```
+
+## Using stores in the Quick French keyboard
+
+We can now reduce the number of rules needed for the Quick French
+keyboard by using stores. We will make five stores: one for the
+unaccented vowels, and one each for vowels with acute accents, grave
+accents, circumflexes, and diereses. For clarity, the `group()`
+statement is repeated below:
+
+```keyman
+group(Main) using keys
+
+store( plainvowels ) 'a' 'e' 'i' 'o' 'u' 'A' 'E' 'I' 'O' 'U'
+store( acutevowels ) U+00E1 U+00E9 U+00ED U+00F3 U+00FA U+00C1 U+00C9 U+00CD U+00D3 U+00DA
+store( gravevowels ) U+00E0 U+00E8 U+00EC U+00F2 U+00F9 U+00C0 U+00C8 U+00CC U+00D2 U+00D9
+store( circumvowels ) U+00E2 U+00EA U+00EE U+00F4 U+00FB U+00C2 U+00CA U+00CE U+00D4 U+00DB
+store( dresisvowels ) U+00E4 U+00EB U+00EF U+00F6 U+00FC U+00C4 U+00CB U+00CF U+00D6 U+00DC
+
+"'" + any( plainvowels ) > index( acutevowels, 2 )
+"`" + any( plainvowels ) > index( gravevowels, 2 )
+"^" + any( plainvowels ) > index( circumvowels, 2 )
+'"' + any( plainvowels ) > index( dresisvowels, 2 )
+```
+
+This is far clearer than the long list of rules that we used earlier.
+Obviously we should add one or two more ordinary rules to produce uppercase
+and lower-case ç, ý, and also the angled quotes « and ». Then we
+will have almost finished the keyboard:
+
+```keyman
+"'" + "y" > U+00FD c Acute-accented Y
+"'" + "Y" > U+00DD
+
+"'" + "c" > U+00E7 c C-cedilla
+"'" + "C" > U+00C7
+
+"<" + "<" > U+00AB c Angled quotes
+">" + ">" > U+00BB
+```
+
+All we need to do now is to test the keyboard.
+
+- [Continue with Step 7: Testing the Keyboard](step-7)
+- [Back to Step 5: Rules with Context](step-5)
\ No newline at end of file
diff --git a/developer/docs/help/guides/develop/tutorial/step-7.md b/developer/docs/help/guides/develop/tutorial/step-7.md
new file mode 100644
index 00000000000..2432bb76f4a
--- /dev/null
+++ b/developer/docs/help/guides/develop/tutorial/step-7.md
@@ -0,0 +1,52 @@
+---
+title: Step 7: Testing the Keyboard
+---
+
+# Compiling the Keyboard
+
+Before we can test the keyboard, we must compile it. Choose
+Keyboard, Compile
+Keyboard or press F7 to compile the
+keyboard. The Message window will display the results of the
+compilation; if you have no typing errors, the keyboard should compile
+successfully.
+
+If there are any mistakes, an error message will be displayed in the
+Message window, which will tell you the line on which the error
+occurred.
+
+## Compiling the Keyboard
+
+After compiling the keyboard successfully, we can start testing it.
+Choose Debug,
+Start Debugging or press
+F5 to begin testing. The Keyboard debug window
+will appear.
+
+Now we test that all the rules operate as expected. To test the rules,
+we type the keystrokes that will give us the output; for example, we can
+type a quote ' followed by one of
+A, E,
+I, O,
+U, or Y to test the
+uppercase acute-accented vowels. Similarly we can test the other
+accents, and C-cedilla (`Ç`) and the angled quotes. If the rules are
+correct, all this should work as we wanted.
+
+Testing the rules in isolation like this will show if the rules are
+correct or not, but won't show other possible errors that might occur in
+everyday usage of the keyboard. For example, look at what happens if you
+type the following quote:
+
+> `'Alors Alice demande, "Où est mon chat ?"'`
+
+As you can see, it comes out incorrectly as:
+
+> `Álors Alice demande, Öù est mon chat ?"'`
+
+The problem occurs when we have a word in quotes that begins with a
+vowel: the keyboard will convert it to an accented vowel. We will need
+to come up with a solution to this problem.
+
+- [Continue with Step 8: Deadkeys](step-8)
+- [Back to Step 6: Stores, `any()`, and `index()`](step-6)
\ No newline at end of file
diff --git a/developer/docs/help/guides/develop/tutorial/step-8.md b/developer/docs/help/guides/develop/tutorial/step-8.md
new file mode 100644
index 00000000000..52f532ce76d
--- /dev/null
+++ b/developer/docs/help/guides/develop/tutorial/step-8.md
@@ -0,0 +1,78 @@
+---
+title: Step 8: Deadkeys
+---
+
+## Solving the Problem
+
+Probably the easiest solution to the problem which we encountered on the
+last page is to design the keyboard so that the user types a quote key
+twice when they want to produce a quote, but once when they want to use
+it as an accent. However, we cannot simply use the rule:
+
+```keyman
+"'" + "'" > "'"
+```
+
+This will certainly produce a single quote after it's typed twice;
+however it will still produce the same error, because the following
+keystroke will continue to swallow the quote character from the context.
+We need to distinguish between the output of this rule, when the user
+wants a quote, and that of a single quote press, when the user wants to
+place an accent on a vowel. To implement this behaviour, we use
+[deadkeys](/developer/language/reference/deadkey).
+
+## Deadkeys
+
+A deadkey is like a character that is used in the context or output but
+never appears on the screen. We use deadkeys like this:
+
+```keyman
++ "'" > deadkey(quote)
+
+c Handle acute accents
+deadkey(quote) + any( plainvowels ) > index( acutevowels, 2 )
+
+c Handle a single quote
+deadkey(quote) + "'" > "'"
+```
+
+Note that for the sake of convenience, a deadkey can also be written in
+a short form:
+
+```keyman
+dk(quote) c This is identical to deadkey(quote)
+```
+
+Type the three rules above in place of the existing rule for acute
+accents in the keyboard so far. If you test the keyboard now, you will
+find that the error no longer occurs. This is because accented vowels
+are only produced after the deadkey, and no deadkey is output if the
+user types a quote key twice.
+
+But we've introduced another difference to the keyboard now: the quote
+is no longer displayed before you type the vowel. This is because we are
+converting the quote to a deadkey. If we prefer, we can still
+distinguish between rules in the above manner and display the quote, if
+we just add a quote before the deadkey, like this:
+
+```keyman
++ "'" > "'" dk(quote)
+
+c Handle acute accents
+"'" dk(quote) + any( plainvowels ) > index( acutevowels, 3 )
+
+c Handle a single quote
+"'" dk(quote) + "'" > "'"
+```
+
+However, we will not use this technique for the Quick French keyboard.
+
+Now we can make changes so that all the other accents use deadkeys as
+well, for consistency, and add rules to output the accent character by
+typing the key twice.
+
+The Quick French keyboard is now complete. The full source is on the
+next page.
+
+- [Continue with Step 9: The Finished Keyboard](step-9)
+- [Back to Step 7: Testing the Keyboard](step-7)
\ No newline at end of file
diff --git a/developer/docs/help/guides/develop/tutorial/step-9.md b/developer/docs/help/guides/develop/tutorial/step-9.md
new file mode 100644
index 00000000000..03e00a27870
--- /dev/null
+++ b/developer/docs/help/guides/develop/tutorial/step-9.md
@@ -0,0 +1,66 @@
+---
+title: Step 9: The Finished Keyboard
+---
+
+## The Quick French Keyboard
+
+Here is the completed keyboard:
+
+```keyman
+c Simplified French Keyboard for Keyman 9.0
+c
+c This keyboard program uses a simplified set of keys
+c for typing French, especially for those who don't know the
+c standard French keyboard.
+c
+c NOTE: This keyboard was created from the Keyman keyboard
+c programming tutorial.
+
+store(&Version) "9.0" c This keyboard is for use with Keyman 9.0
+store(&Name) "Quick French"
+store(&Bitmap) "qfrench.ico"
+store(&MnemonicLayout) "1" c This keyboard uses a mnemonic layout.
+
+begin Unicode > use(Main)
+
+group( Main ) using keys
+
+c Store the upper and lowercase vowels with different accents
+store( plainvowels ) 'a' 'e' 'i' 'o' 'u' 'A' 'E' 'I' 'O' 'U'
+store( acutevowels ) U+00E1 U+00E9 U+00ED U+00F3 U+00FA U+00C1 U+00C9 U+00CD U+00D3 U+00DA
+store( gravevowels ) U+00E0 U+00E8 U+00EC U+00F2 U+00F9 U+00C0 U+00C8 U+00CC U+00D2 U+00D9
+store( circumvowels ) U+00E2 U+00EA U+00EE U+00F4 U+00FB U+00C2 U+00CA U+00CE U+00D4 U+00DB
+store( dresisvowels ) U+00E4 U+00EB U+00EF U+00F6 U+00FC U+00C4 U+00CB U+00CF U+00D6 U+00DC
+
+c Output deadkeys only for the accent keys pressed
++ "'" > dk(quote) c Quote for acute accent
++ "`" > dk(bkquote) c Backquote for grave accent
++ "^" > dk(caret) c Caret for circumflex
++ '"' > dk(dbquote) c Double-quote for dieresis
+
+c Rules for accented vowels
+dk(quote) + any( plainvowels ) > index( acutevowels, 2 )
+dk(bkquote) + any( plainvowels ) > index( gravevowels, 2 )
+dk(caret) + any( plainvowels ) > index( circumvowels, 2 )
+dk(dbquote) + any( plainvowels ) > index( dresisvowels, 2 )
+
+c Rules for other characters
+dk(quote) + "y" > U+00FD c Acute-accented Y
+dk(quote) + "Y" > U+00DD
+
+dk(quote) + "c" > U+00E7 c C-cedilla
+dk(quote) + "C" > U+00C7
+
+"<" + "<" > U+00AB c Angled quotes
+">" + ">" > U+00BB
+
+c Rules for the accent character itself (type it twice)
+dk(quote) + "'" > "'" c Quote
+dk(bkquote) + "`" > "`" c Backquote
+dk(caret) + "^" > "^" c Caret
+dk(dbquote) + '"' > '"' c Double-quote
+
+c End of keyboard
+```
+
+- [Back to Step 8: Deadkeys](step-8)
\ No newline at end of file
diff --git a/developer/docs/help/guides/distribute/index.md b/developer/docs/help/guides/distribute/index.md
new file mode 100644
index 00000000000..7b84bf472fd
--- /dev/null
+++ b/developer/docs/help/guides/distribute/index.md
@@ -0,0 +1,10 @@
+---
+title: Distributing keyboards
+---
+
+- [Package development tutorial](tutorial) (Keyman Desktop, Keyman for macOS, Keyman for mobile apps)
+- [Distribute keyboards to Keyman applications](packages)
+
+## See also
+
+- [Keyman Engine](/developer/engine/)
\ No newline at end of file
diff --git a/developer/docs/help/guides/distribute/install-kmp-android.md b/developer/docs/help/guides/distribute/install-kmp-android.md
new file mode 100644
index 00000000000..bdba196c908
--- /dev/null
+++ b/developer/docs/help/guides/distribute/install-kmp-android.md
@@ -0,0 +1,78 @@
+---
+title: Installing Custom Keyboards to your Android Device
+---
+
+## Overview
+
+In [Keyman for Android 10.0](/products/android/10.0/), we added the ability to
+easily download/share custom keyboards to your Android device.
+
+## Installing a custom keyboard
+
+To install a custom keyboard, you will need a link to download the
+keyboard package. If you have already copied the kmp file onto the
+Android device, skip to Step 2.
+
+If you want to build and dev your own keyboard, you will need some
+technical skills for this, and can read our full step-by-step
+instructional on how to do this [here](packages).
+
+Once you have a website or email with a link to the keyboard package,
+follow these steps on your Android device to download and install the
+keyboard package into the Keyman for Android application:
+
+## Step 1) Click the link to save your custom keyboard package file
+
+The link in this example is for Khmer Angkor keyboard.
+
+![](../../images/dist-url-screen-ap.png)
+
+## Step 2) Add Keyboard from Device
+
+Once the KMP file is on your device, you will need to browse to the KMP
+file and select it. From the Keyman menu, select "Settings". From the
+Keyman Settings menu, select "Add Keyboard from Device".
+
+![](../../images/settings-language-ap.png)
+
+The device will launch a file browser where you'll browse to the
+directory of your KMP file. A common places to look is the "Downloads"
+folder.
+
+![](../../images/dist-file-browser-ap.png)
+
+Selecting the KMP file should bring you to Step 3)
+
+## Step 3) Grant Keyman for Android access to storage (Android 6.0+ only)
+
+On Android 6.0 (Marshmallow) and higher, mobile apps need to request
+permission to access storage. Keyman for Android needs access to read
+storage for installing the KMP file. At the dialog, select "ALLOW". Once
+authorized, Keyman for Android won't need to ask for storage permission
+again, unless the user revokes or uninstalls the app.
+
+![](../../images/dist-storage-permission-ap.png)
+
+Older versions of Android grant Storage permissions at app installation
+time, so those users can skip this step.
+
+## Step 4) Keyboard Package welcome screen
+
+Keyman for Android will parse the metadata in the package. If the
+keyboard package includes a "welcome.htm" file, this will be displayed
+at the confirmation to install the keyboard package
+
+![](../../images/dist-welcome-ap.png)
+
+If "welcome.htm" is not included, a generic page with the package ID and
+package version will be shown.
+
+Click the left "Install" button to install the entire keyboard package
+
+## Step 5) The keyboard is successfully installed!
+
+All the keyboards in the package are installed as a group. In this
+example, the package only has the "Khmer Angkor" keyboard, so it becomes
+the active keyboard
+
+![](../../images/dist-install1-ap.png)
\ No newline at end of file
diff --git a/developer/docs/help/guides/distribute/install-kmp-ios.md b/developer/docs/help/guides/distribute/install-kmp-ios.md
new file mode 100644
index 00000000000..de4d3a64cb5
--- /dev/null
+++ b/developer/docs/help/guides/distribute/install-kmp-ios.md
@@ -0,0 +1,53 @@
+---
+title: Installing Custom Keyboards to your iPhone or iPad
+---
+
+## Overview
+
+In version 10 of Keyman for iPhone and iPad, we added the ability to easily download custom keyboards to your iOS device.
+
+## Installing a custom keyboard
+
+To install a custom keyboard, you will first need a link to the keyboard
+package on a website or in your email.
+
+If you want to build and dev your own keyboard, you will need some
+technical skills for this, and can read our full step-by-step
+instructional on how to do this [here](packages).
+
+Once you have a website or email with a link to the keyboard package,
+follow these steps on your iPhone or iPad to download and install the
+keyboard package into the Keyman for iPhone and iPad application:
+
+## Step 1) Click the link to your custom keyboard package file
+
+The link in this example is for GFF Amharic 7 keyboard.
+
+![](../../images/dist-url-screen-i.png)
+
+Safari will display an option to open the KMP file with Keyman
+
+![](../../images/dist-kmp-open-i.png)
+
+## Step 2) Click Open in "Keyman"
+
+Keyman for iPhone and iPad will parse the metadata in the package. If
+the keyboard package includes a "welcome.htm" file, this will be
+displayed at the confirmation to install the keyboard package
+
+![](../../images/dist-welcome-i.png)
+
+If "welcome.htm" is not included, a generic page with the package ID and
+package version will be shown.
+
+## Step 3) Click the top right "Install" button
+
+The keyboard from the keyboard package is successfully installed!
+
+![](../../images/dist-kmp-success-i.png)
+
+All the keyboards in the package are installed as a group. In this
+example, the package only has the "GFF Amharic 7" keyboard, so it
+becomes the active keyboard
+
+![](../../images/dist-install1-i.png)
diff --git a/developer/docs/help/guides/distribute/packages.md b/developer/docs/help/guides/distribute/packages.md
new file mode 100644
index 00000000000..e3382220a30
--- /dev/null
+++ b/developer/docs/help/guides/distribute/packages.md
@@ -0,0 +1,131 @@
+---
+title: Distribute keyboards to Keyman Applications
+---
+
+## Overview
+
+Keyboard package files contain one or more keyboards, along with readme
+files, fonts (if your keyboard requires a custom font), and any other
+files you wish to include. You should create a package file to bundle
+your keyboard with fonts and help into a simple, single file that is
+easy for an end-user to install.
+
+Internally, as well as your own files, the package file will contain
+metadata files "kmp.inf" and "kmp.json", which list the details Keyman
+needs to install the package. The package file is a ZIP compatible
+archive.
+
+> ### Tip
+You can distribute keyboards and lexical models in package files, but
+you can't include both in the same package file.
+
+#### Keyman Desktop
+
+Keyman Desktop can install package files, including installing fonts,
+creating Start Menu shortcuts, and adding appropriate registry entries
+for uninstallation.
+
+On Windows, the context menu for a package file has one additional
+entry: "Install".
+
+#### Keyman for macOS
+
+Keyman for macOS can install package files with fonts and keyboards, and
+shortcuts in the package will be available through the keyboard's entry
+in Keyman Configuration.
+
+#### Keyman for Android and Keyman for iPhone and iPad
+
+Keyman mobile applications can install the same package files as Keyman
+Desktop, as long as the package includes keyboards for touch layouts.
+
+## Package file contents
+
+A package can have a variety of different files contained within. The
+following files and file types are recognized by the package installer:
+
+\*.kmx (Desktop and macOS only)
+: Keyboard files. Each of these will be installed. Keyman
+ Configuration will not allow installation or uninstallation of a
+ single keyboard from a package. They will always be treated as a
+ group for installation and uninstallation.
+
+\*.kvk (Desktop and macOS only)
+: On Screen Keyboard files, associated with each keyboard file.
+
+\*.js (mobile only)
+: Touch layout Keyboard files. When Keyman mobile applications install
+ a keyboard package, all included keyboards will be installed as a
+ group. With Keyman Developer 10, the keyboard version information is
+ in kmp.json, and no longer within the JS file names.
+
+welcome.htm
+: Introductory help for the keyboard, HTML format. This will normally
+ be displayed when the package is installed by the user, and is also
+ the entry point for help when accessed via Keyman's help system or
+ Keyman Configuration.
+
+readme.htm
+: Displayed before a package is installed, together with brief
+ metadata about the package, to allow the user to determine if they
+ wish to continue installation of the package.
+
+kmp.inf (Legacy versions of Keyman)
+: A Windows .ini format file that lists each of the files in the
+ package, together with metadata.
+
+kmp.json
+: A JSON format file contains metadata for the keyboard package such
+ as package version, keyboard versions, and lists each of the files
+ in the package. For more explanation of the structure of the JSON
+ file, please read the
+ [metadata](../../reference/file-types/metadata) documentation.
+
+\*.ttf, \*.otf, \*.ttc
+: Truetype font files that will be installed with the package, and
+ uninstalled when the package is removed. On mobile, these fonts will
+ be available only within the Keyman app and the on-screen-keyboard,
+ not in other apps.
+
+## Share the keyboard package file
+
+> ### Tip
+Read [Step 2: Organizing the Keyboard Files](../../../keyboards/github/step-2)
+to know which files to include or exclude in the keybord folder.
+
+Once the keyboard package .kmp file is created, you can share them via
+external storage devices (USB drive, SD card, etc). You can also share
+the package file via a cloud storage system (Google Drive, Dropbox or
+similar service), then share the link to your device via text, note or
+email. Alternatively, you can upload the .kmp file to a public facing
+website. For this example, the keyboard package for Khmer Angkor is
+being uploaded:
+
+1. khmer_angkor.kmp (the keyboard package .kmp file)
+
+## Putting the keyboard package on a website
+
+Once all the files have been uploaded, you will need to provide a link
+to the keyboard package .kmp file for your device to download and
+install. This can either be a link on a web page, or a link in an email.
+In this tutorial, a very simple .html web page with a link to the
+khmer_angkor.kmp file is created:
+
+``` markup
+
+
+
+
+ Custom Keyboard URL
+
+
+```
+
+The link must be in the format `http://` or `https://`
+
+Upload the web page to your public facing website. Once done, you can
+install the keyboard package onto your mobile devices by following these
+steps:
+
+- [Installing custom keyboards to your iPhone or iPad](install-kmp-ios)
+- [Installing custom keyboards to your Android device](install-kmp-android)
diff --git a/developer/docs/help/guides/distribute/tutorial/index.md b/developer/docs/help/guides/distribute/tutorial/index.md
new file mode 100644
index 00000000000..ec57e57e100
--- /dev/null
+++ b/developer/docs/help/guides/distribute/tutorial/index.md
@@ -0,0 +1,36 @@
+---
+title: Package Tutorial
+---
+
+[Step 1: What do we include?](step-1)
+
+[Step 2: Creating Additional Files for the Package](step-2)
+
+[Step 3: Creating a package and adding files](step-3)
+
+[Step 4: Filling in package details](step-4)
+
+[Step 5: Shortcuts](step-5)
+
+[Step 6: Compiling, testing and distributing a Package](step-6)
+
+## Overview
+
+Welcome! In this tutorial, you will learn how to create a
+[package](../../../reference/file-types/kmp). A package is a collection
+of files, compressed into a single file, just like a ZIP file. It is
+designed to make installation of keyboards, fonts and related
+documentation straightforward for the end user. When done, the package
+can be installed into Keyman Desktop through a simple dialog. Keyman for
+mobile apps can also download and install keyboards through the keyboard
+package.
+
+We will be creating a Khmer Angkor package. You can make your own
+package based on the Quick French keyboard from the [keyboard tutorial](../../develop/tutorial).
+
+### Let's begin
+
+Let's get started! Move on to the next topic to begin the first step,
+choosing what to include in the package.
+
+[Step 1: What do we include?](step-1)
\ No newline at end of file
diff --git a/developer/docs/help/guides/distribute/tutorial/step-1.md b/developer/docs/help/guides/distribute/tutorial/step-1.md
new file mode 100644
index 00000000000..a18fce49ac9
--- /dev/null
+++ b/developer/docs/help/guides/distribute/tutorial/step-1.md
@@ -0,0 +1,35 @@
+---
+title: Step 1: What do we include?
+---
+
+What is the purpose of a package? To make installation of a keyboard,
+fonts, documentation, and on screen keyboard as straightforward as
+possible for the end user.
+
+We need to keep this goal in mind as we work on all the aspects of a
+package.
+
+A good package will:
+
+1. Collect the keyboard and font files together for simple installation
+2. Include Start Menu items for documentation
+
+A great package will also:
+
+1. Include a 'readme' visible before install.
+2. Include a 'welcome.htm' file which is displayed after install.
+3. Include an On Screen Keyboard if relevant.
+4. Include documentation on use of the keyboard.
+
+Packages uploaded to the Tavultesoft website will get basic icon ratings
+reflecting their ease of install/use:
+
+- ![](../../../images/tutorial_package_includesfonts.gif) includes fonts
+- ![](../../../images/tutorial_package_includesdocs.gif) includes documentation
+- ![](../../../images/tutorial_package_includesosk.gif) includes on screen keyboards
+- ![](../../../images/tutorial_package_includeswelcome.gif) includes welcome.htm
+
+In general, the more icons that a package earns, the easier that end
+users will find it to start using.
+
+[Step 2: Creating Additional Files for the Package](step-2)
\ No newline at end of file
diff --git a/developer/docs/help/guides/distribute/tutorial/step-2.md b/developer/docs/help/guides/distribute/tutorial/step-2.md
new file mode 100644
index 00000000000..17dc613ba8d
--- /dev/null
+++ b/developer/docs/help/guides/distribute/tutorial/step-2.md
@@ -0,0 +1,127 @@
+---
+title: Step 2: Creating Additional Files for the Package
+---
+
+You should create or select some additional files for a package. Below
+are a list of the typical files that you would create for or include in
+a package.
+
+readme.htm
+
+: A short description of the package, its use restrictions, and what
+ it includes. Try to keep the readme under 10 lines long. The readme
+ is displayed in the package install dialog and should be an html
+ file for optimal formatting.
+
+ > ### Tip
+ As the package has not been installed at this stage, and you are using fonts that may not yet be available on the target device, you should use CSS @font-face declarations to ensure that the text is readable on all devices.
+
+ Create the HTML file in any HTML editor.
+
+ > ### Tip
+ If using Microsoft Word, choose HTML (clean) when saving to create a smaller file.
+
+ Note, if your HTML editor puts images into a subfolder, you will need to edit the HTML source so that all files are in the same folder -- the package builder will not maintain subfolders. You can easily edit the HTML source in Keyman Developer.
+
+ Also, if your welcome or readme files use embedded external images, stylesheets, javascript or other files, you will need to add these files to your package as well.
+
+welcome.htm
+
+: When including an introductory help file in your package, you must
+ name the file welcome.htm. This file will be detected during install
+ and displayed (in a window roughly two thirds of the user's screen
+ width) after the package install completes successfully. Make sure
+ that you design your HTML file so that it can be resized to fit the
+ user's screen - avoid extra wide tables or wide fixed width
+ elements.
+
+ At this stage of the installation, fonts in the package have been installed, so you can include text that uses those fonts.
+
+ Welcome.htm will also be accessible after installation from Keyman Configuration under the Package Options menu, and from the Keyboard Help item and On Screen Keyboard toolbar button.
+
+ The intention of welcome.htm is to provide instructions on getting started with your keyboards.
+
+ > ### Tip
+ Useful information to include in welcome.htm is:
+ - key sequences - especially for characters that may not be immediately obvious.
+ - names of fonts in the package.
+ - names of keyboards in the package.
+ - links to additional help on your website or more extensive documentation files in the package.
+ - a link to an official distribution site for your package (even
+ if it is the Keyman website) - so that users know where to find
+ the latest version of your package.
+
+ You should avoid including instructions for the use of Keyman
+ itself - although a basic "click the Keyman icon and choose Quick
+ French" would be helpful.
+
+ > ### Tip
+ If you want links to your website to open in the user's preferred
+ browser, preface the href link with `link:`, e.g.
+ `website`
+ The `link:` sceheme will open the referred file in the default
+ application - that is, a web browser for URLs and links, Notepad for
+ .txt files, Adobe Reader for PDFs. You can use `link:` to open any
+ of the files in the package, e.g. `link:docs.pdf` will open the file
+ docs.pdf in Adobe Reader or the default PDF reader on the system.
+
+ To save you the effort of writing a welcome and readme file for the
+ Quick French example, we have placed some in the Samples/QFrench
+ folder.
+
+ If you create documents in other formats, for example PDF or
+ printable documentation, you should link to that in the welcome.htm.
+
+ > ### Tip
+ You can include multiple "welcome.htm" files for different languages
+ by appending a hyphen and the BCP 47 language code to the filename,
+ for example welcome-fr.htm for French.
+
+Fonts
+
+: A font is the single most important item to include with a keyboard
+ – if the characters of the language you are supporting are not in
+ fonts included with target operating systems. Installing fonts is
+ tedious, so make sure that your users don't have to locate and
+ install fonts themselves!
+
+ .TTF, .OTF, and .TTC fonts will be installed by the package
+ installer, and uninstalled when the package is uninstalled. A list
+ of the fonts installed is displayed in the Install Package dialog.
+
+Keyboards
+
+: Add the .kmx compiled Keyman keyboards to the package. You can add
+ multiple keyboards to the package, but be judicious.
+
+ > ### Tip
+ Don't forget to add the On Screen keyboard .KVK file associated with
+ your keyboard to the package. KVK files are not compiled into the
+ .KMX file -- although the .ICO/.BMP is.
+
+ Add the .js compiled Keyman touch layout keyboards to the package.
+
+Documentation
+
+: The two preferred documentation formats are HTML and PDF. You should
+ avoid .DOC, .RTF, and other formats -- .DOC files in particular are
+ not recommended due to the possibility of macro viruses.
+
+ Remember that HTML files can be displayed on any computer without
+ additional software. PDF files require Adobe Reader or a compatible
+ PDF viewer application. You may choose to include both and HTML
+ documentation - PDF documents often print better than HTML
+ documents, but HTML documentation is more accessible and translates
+ better to on-screen or web use.
+
+ > ### Tip
+ If you use HTML, don't forget to also add all the included files
+ such as images and stylesheets!
+
+Splash Image
+: The splash image is a 140x250 pixel image that is displayed when the
+ package is installed, at the left of the Package Install dialog.
+ Including a splash image makes your package look more professional
+ and polished, so a splash image is recommended!
+
+[Step 3: Creating a package and adding files](step-3)
\ No newline at end of file
diff --git a/developer/docs/help/guides/distribute/tutorial/step-3.md b/developer/docs/help/guides/distribute/tutorial/step-3.md
new file mode 100644
index 00000000000..657f2cd02b5
--- /dev/null
+++ b/developer/docs/help/guides/distribute/tutorial/step-3.md
@@ -0,0 +1,31 @@
+---
+title: Step 3: Creating a package and adding files
+---
+
+We are finally ready to open up the Package Editor and create the new
+package.
+
+- In the Project Window in Keyman Developer, click the **Packages** tab, and click **New Package...**. Enter the path and filename of the package you are creating.
+
+> ### Tip
+This tutorial makes a package named `khmer_angkor.kps`, but you could call yours `qfrench.kps`.
+
+A package source file will have the extension .kps, and will be compiled in a file with extension .kmp.
+
+After you click OK, the Package Editor will open with the Files tab visible.
+
+![](../../../images/tutorial_distribute_keyboard_3_files.png)
+
+- In the Files tab, click **Add...** to add
+ all the files we discussed in the previous step to the package. You
+ can add multiple files at once, and from multiple folders. When the
+ package is compiled, all the files will be placed in the same folder
+ within the package.
+- If your package includes .js touch layout keyboards, you can
+ associate fonts and languages in the Keyboards tab. A valid [BCP 47 language tag](../../../reference/bcp-47) must be set or the keyboard will not install on your mobile device.
+
+![](../../../images/tutorial_distribute_keyboard_3.png)
+
+You could stop here. This would be a completely valid package, but it would not be as good as it could be. So let's continue on to the next step, and fill in some descriptions of the package.
+
+[Step 4: Filling in package details](step-4)
\ No newline at end of file
diff --git a/developer/docs/help/guides/distribute/tutorial/step-4.md b/developer/docs/help/guides/distribute/tutorial/step-4.md
new file mode 100644
index 00000000000..67c55fe66b8
--- /dev/null
+++ b/developer/docs/help/guides/distribute/tutorial/step-4.md
@@ -0,0 +1,63 @@
+---
+title: Step 4: Filling in package details
+---
+
+Click on the Details tab in the Package Editor. You should fill in as
+many details as you can on this page.
+
+![](../../../images/tutorial_distribute_keyboard_details.png)
+
+Package Name
+
+: The Package Name will be displayed in the package install dialog,
+ and within Keyman Desktop Configuration, wherever the package is
+ referred to.
+
+Readme File
+
+: Select the readme file from the list of files you added in the
+ previous step. Remember that it should be a HTML file for optimum
+ clarity.
+
+Version
+
+: A version number for the package is important - it helps the your
+ users know that they are using the most recent update of your
+ package. The version format you should use is `1.0`. When making a
+ major change to your package or keyboards in your package, increment
+ the first part and set the second part to, e.g. `2.0`; when making a
+ bug fix or a minor update, increment the second part, e.g. `1.1`.
+
+Version numbers should be in the form `major.minor[.subversion]`.
+ Subversion is optional but is helpful for small bug fix releases.
+ Each of the sections of the version should be an integer. Keyman
+ Desktop does integer comparisons on the version numbers, so, for
+ example, version `2.01` is regarded as older than version `2.2`.
+ Alphabetic or date formats should be avoided as the installer for
+ the keyboard cannot determine which version is older reliably.
+
+Copyright
+
+: Enter copyright details for your package and keyboards. Keep this
+ reasonably short or it won't be clear for end users.
+
+Author
+
+: Enter your name or the name of your company.
+
+Email
+
+: Enter a contact email address where package users can contact you.
+ If you don't want to be contacted via email, leave this field empty
+
+Website
+
+: Enter the name of the website where you will have information about
+ this keyboard. If you want to host it on keyman.com, you could enter
+ `http://www.keyman.com/`
+
+Image file
+: Select the splash image file that you created in Step 2 from the
+ list of files in your package.
+
+[Step 5: Shortcuts](step-5)
diff --git a/developer/docs/help/guides/distribute/tutorial/step-5.md b/developer/docs/help/guides/distribute/tutorial/step-5.md
new file mode 100644
index 00000000000..eb24d70f133
--- /dev/null
+++ b/developer/docs/help/guides/distribute/tutorial/step-5.md
@@ -0,0 +1,49 @@
+---
+title: Step 5: Shortcuts
+---
+
+Click on the Shortcuts tab in the Package Editor.
+
+![](../../../images/tutorial_distribute_keyboard_shortcuts.png)
+
+While it may be a good idea to include Start Menu shortcuts for your
+package, on recent versions of Windows these are not very visible to end
+users. This means that you should ensure that all instructions are
+available through the **welcome.htm** file as well. The Start Menu
+shortcuts were designed originally for Windows-based keyboard packages
+and work slightly differently in macOS.
+
+You should consider adding the following items to the Start Menu:
+
+- Documentation shortcuts
+- Welcome.htm shortcut
+
+> ### Note
+While Keyman Developer 8.0 and earlier versions of the documentation
+advised including an uninstall shortcut to the package, this is no
+longer recommended. Users should uninstall your package from Keyman
+Configuration. Adding an uninstall shortcut to the package adds
+confusion to the Start Menu search on Windows, and is not compatible
+with macOS keyboards.
+
+Click **New** to add a new shortcut to the
+package. Enter a description for the shortcut, and select the program or
+file to start from the Program list. Four predefined program entries
+(Start Product), (Product Configuration), (Product Help) and (About
+Product) will be translated into the appropriate shortcuts to start
+Keyman Desktop tasks as described.
+
+> ### Note
+In Keyman 6 and earlier, the predefined targets $KEYMAN\keyman.exe and
+$KEYMAN\kmshell.exe were available. These are translated to (Start
+Product) and (Product Configuration), respectively, in Keyman Desktop
+7.0 and later versions.
+
+
+
+> ### Tip
+Packages are not listed in the Control Panel Add/Remove Programs applet
+with Keyman Desktop 7.0 and later versions. Packages can be uninstalled
+through the Start Menu shortcut or from Keyman Desktop Configuration.
+
+[Step 6: Compiling, testing and distributing a Package](step-6)
\ No newline at end of file
diff --git a/developer/docs/help/guides/distribute/tutorial/step-6.md b/developer/docs/help/guides/distribute/tutorial/step-6.md
new file mode 100644
index 00000000000..374584c3e1c
--- /dev/null
+++ b/developer/docs/help/guides/distribute/tutorial/step-6.md
@@ -0,0 +1,68 @@
+---
+title: Step 6: Compiling, testing and distributing a Package
+---
+
+In the Package Editor, click on the **Compile** tab.
+
+![](../../../images/tutorial_distribute_keyboard_compile.png)
+
+Click **Compile Package** to compile the
+package into a .kmp file. Compiling takes all the files listed for the
+package, compresses them (using a .ZIP-compatible format) and adds the
+package information, all into a single file. If any files are not
+available, an error will be listed in the Messages window.
+
+After compiling, you can test the package installation in Keyman
+Desktop, by clicking **Install Package**. You
+should test that all the keyboards and fonts install successfully, that
+the Readme and Welcome files are displayed during the install, and that
+the documentation is accessible to the end user. Make sure that the On
+Screen Keyboard installs with your keyboards, and that the shortcuts are
+correctly listed and working in the Start Menu.
+
+> ### Advice
+The distribution model for Keyman keyboards is changing. We now
+recommend distributing source for keyboards through the [Keyman Cloud Keyboard Repository](/developer/keyboards/). The instructions here
+remain, but the binary distribution model is deprecated.
+
+## Distributing a package on the Keyman Cloud Keyboard Repository
+
+Once you have tested the package to your satisfaction, it is time to
+distribute it. We recommend uploading your keyboard package to the
+[Keyman Cloud Keyboard Repository](/developer/keyboards/)
+
+## Distributing a package on your own website
+
+If you distribute a package on your own site, we have the following
+recommendations:
+
+1. Ensure the MIME type on the web server or folder for .KMX and .KMP
+ files is set up to application/octet-stream. Without this, .KMP
+ files may be recognised as .ZIP files -- this is not helpful to the
+ end user as it will be opened in the wrong application. If you can't
+ make this change, consider hosting the keyboard package on the
+ Tavultesoft website to make things easier for your end user.
+2. Avoid putting the .KMP file in an archive (e.g. .ZIP) or
+ self-expanding archive (.EXE) - this makes it harder for end users
+ to install. A .KMP file is already compressed (it is actually just a
+ ZIP archive file!) and you won't save much space by recompressing
+ it.
+3. Include a link to the Keyman download page:
+ `http://keyman.com/desktop/`
+
+## Distributing a package by email
+
+Attaching the KMP file directly to an email may be blocked for security
+reasons. As mentioned above, a KMP file is basically a ZIP file and
+mobile keyboard data is in JavaScript, this is a combination that looks
+suspicious to many email servers. You can upload it to a Google drive,
+and email a link for downloading the file
+
+You may need to do some exploring to get your mobile device to load the
+keyboard from the KMP file into Keyman. The device may not open the file
+directly, claiming it is an unrecognized type. If you look for other
+options to open the file, hopefully you can get it to Keyman. For
+example, in iOS, I had to tap on the three dots to the right, then
+choose "Open in" and then I could choose "Copy to Keyman." In Android, I
+had to download the file to my device, then open the KMP file in a file
+manager before I could install it in Keyman.
\ No newline at end of file
diff --git a/developer/docs/help/guides/index.md b/developer/docs/help/guides/index.md
new file mode 100644
index 00000000000..55604780ad2
--- /dev/null
+++ b/developer/docs/help/guides/index.md
@@ -0,0 +1,13 @@
+---
+title: Keyman Developer Guides
+---
+
+Need help using Keyman Developer? You'll find everything you need here, including product documentation, frequently asked questions and tutorials.
+
+- [Introduction to Keyman Developer](intro)
+- [Developing Keyman keyboards](develop/)
+- [Testing Keyman keyboards](test/)
+- [Distributing Keyman keyboards](distribute/)
+- [Developing Keyman lexical models (for predictive text and autocorrect)](lexical-models/)
+- [Distributing Keyman lexical models](lexical-models/distribute/)
+- [Keyman Developer command line tools guide](command-line)
\ No newline at end of file
diff --git a/developer/docs/help/guides/intro.md b/developer/docs/help/guides/intro.md
new file mode 100644
index 00000000000..fd72c898f91
--- /dev/null
+++ b/developer/docs/help/guides/intro.md
@@ -0,0 +1,43 @@
+---
+title: Introducing Keyman Developer
+---
+
+## Welcome to Keyman Developer
+
+Keyman Developer allows you to create your own keyboard layouts for use
+on the Keyman platform. Keyman is a multi-platform input method solution
+that runs on Windows, macOS, the web, mobile web, iOS and Android and
+Linux.
+
+There are two core applications included in Keyman Developer: the Keyman
+Developer IDE (formerly called TIKE), and the command line compiler kmc.
+
+[Watch a video](https://youtu.be/kwhgx_eX4Es) that highlights some of
+the Keyman Developer features.
+
+## Keyman Developer IDE
+
+The Keyman Developer IDE is a complete environment for designing,
+developing, testing, and packaging your keyboards for distribution. Some
+of the major features of Keyman Developer IDE are:
+
+- Keyboard Editor, for creating keyboards visually for all platforms
+- Touch Layout editor, for creating fluid touch layouts
+- Package Editor, for creating a package of keyboard, fonts and files for distribution (used on all platforms except web)
+- Lexical Model Editor, for creating predictive text models
+- Integrated compiler, to compile keyboards, packages and models
+- Fully interactive debugger for testing keyboards and models
+- Character Map, which supports viewing Unicode fonts under all Windows versions
+- On Screen Keyboard editor, for creating visual representations of keyboards
+- Projects, for managing keyboard and lexical model development projects large and small
+
+Source files (except images!) are normally written in UTF-8 format, so
+they can be opened with any text editor. However, the Keyman Developer
+IDE provides much tooling to simplify editing each of the types of
+files. The Keyman Developer IDE runs on Windows.
+
+## kmc, the command line compiler
+
+
+kmc, the command-line compiler, is a comprehensive tool that lets you
+compile keyboards, packages, models and projects from the command-line.
diff --git a/developer/docs/help/guides/lexical-models/advanced/index.md b/developer/docs/help/guides/lexical-models/advanced/index.md
new file mode 100644
index 00000000000..951df2fc48e
--- /dev/null
+++ b/developer/docs/help/guides/lexical-models/advanced/index.md
@@ -0,0 +1,19 @@
+---
+title: Advanced Lexical Models Topics
+---
+
+> For the following topics, **knowledge of TypeScript or JavaScript is
+required**.
+
+The Keyman lexical model engine is capable of much more sophisticated
+lexical models than the one described in the [tutorial](../tutorial). To
+make advanced models, you will need to modify the [model definition file](model-definition-file), also known as the [`.model.ts` file](../../../reference/file-types/model-ts).
+
+Once you understand how to change the model.ts file, you can attempt
+these customizations to an existing wordlist project:
+
+[Changing the default punctuation](punctuation)
+
+[Creating a custom search key function](search-term-to-key)
+
+[Customizing the word breaker](word-breaker)
\ No newline at end of file
diff --git a/developer/docs/help/guides/lexical-models/advanced/model-definition-file.md b/developer/docs/help/guides/lexical-models/advanced/model-definition-file.md
new file mode 100644
index 00000000000..97b207d8f32
--- /dev/null
+++ b/developer/docs/help/guides/lexical-models/advanced/model-definition-file.md
@@ -0,0 +1,102 @@
+---
+title: The model definition file
+---
+
+This is a small [TypeScript](https://www.typescriptlang.org/) source
+code file that tells us how to define our model.
+
+In the case of the **wordlist lexical models**, the model definition
+file indicates where to find the [TSV source files](../../../reference/file-types/tsv), as well as gives us the option to tell the compiler a little bit more about our language’s spelling system or *orthography*.
+
+## The model definition template
+
+**Keyman Developer** provides a default model definition similar to the
+following. If you want to create the file yourself, copy-paste the
+following template, and save it as `model.ts`. Place this file in the
+same folder as `wordlist.tsv`.
+
+```typescript
+/*
+ sencoten 1.0 generated from template.
+
+ This is a minimal lexical model source that uses a tab delimited wordlist.
+ See documentation online at https://help.keyman.com/developer/ for
+ additional parameters.
+*/
+
+const source: LexicalModelSource = {
+ format: 'trie-1.0',
+ wordBreaker: {
+ use: 'default',
+ },
+ sources: ['wordlist.tsv'],
+};
+export default source;
+```
+
+Let's step through this file, line-by-line.
+
+On the first line, we're declaring the source code of a new lexical
+model.
+
+```typescript
+const source: LexicalModelSource = {
+```
+
+On the second line, we're saying the lexical model will use the
+`trie-1.0` format. The `trie` format creates a lexical model from one or
+more word lists; the `trie` structures the lexical model such that it
+can predict through thousands of words very quickly.
+
+```typescript
+ format: 'trie-1.0',
+```
+
+On lines 3–5, we're specifying the word breaking algorithm that we want
+to use. Keyman supplies a default algorithm that conforms to the rules
+expected for many languages.
+
+```typescript
+ wordBreaker: {
+ use: 'default',
+},
+```
+
+On the sixth line, we're telling the `trie` where to find our wordlist.
+
+```typescript
+ sources: ['wordlist.tsv'],
+```
+
+The seventh line marks the termination of the lexical model source code.
+If we specify any customizations, they **must** be declared above this
+line:
+
+```typescript
+};
+```
+
+The eighth line is necessary to allow external applications to read the
+lexical model source code.
+
+```typescript
+export default source;
+```
+
+## Customizing our lexical model
+
+The template, as described in the previous section, is a good starting
+point, and may be all you need for your language. However, most language
+require a few customizations. The `trie-1.0` wordlist model supports the
+following customizations:
+
+[Punctuation](punctuation)
+: How to define certain punctuation in your language.
+
+[Word breaker](word-breaker)
+: How to determine when words start and end in the writing system.
+
+[Search term to key](search-term-to-key)
+: How and when to ignore accents and letter case.
+
+To see all of the things possible in a model definition file, see the [`LexicalModelSource` interface](https://github.com/keymanapp/keyman/blob/stable-15.0/developer/js/source/lexical-model-compiler/lexical-model.ts#L95-L146).
\ No newline at end of file
diff --git a/developer/docs/help/guides/lexical-models/advanced/punctuation.md b/developer/docs/help/guides/lexical-models/advanced/punctuation.md
new file mode 100644
index 00000000000..a665f08e4d8
--- /dev/null
+++ b/developer/docs/help/guides/lexical-models/advanced/punctuation.md
@@ -0,0 +1,73 @@
+---
+title: Punctuation
+---
+
+The lexical models use two different kinds of punctuation:
+
+- Quotation marks around the “keep” suggestion.
+- What kind of space to insert after words, if any.
+
+Both of these can be customized by adding the punctuation to [model definition file](./model-definition-file). Here is a full example of a model definition file using default punctuation:
+
+```typescript
+const source: LexicalModelSource = {
+ format: 'trie-1.0',
+ sources: ['wordlist.tsv'],
+ // CUSTOMIZE THIS:
+ punctuation: {
+ quotesForKeepSuggestion: {
+ open: "“", close: "”"
+ },
+ insertAfterWord: " ",
+ },
+ // other customizations go here:
+};
+
+export default source;
+```
+
+## Customizing `quotesForKeepSuggestion`
+
+These are the quotation marks that surrond the “keep” suggestion when
+it's displayed in the suggestion bar. By defaut, the quotations used are
+“smart” quotation marks used in English typography. Namely, the **open
+quote** is `“` U+201C LEFT DOUBLE QUOTATION MARK, and the **close
+quote** is `”` U+201D RIGHT DOUBLE QUOTATION MARK.
+
+Let's customize this to use `«` and `»` for the open and close quote, respectively. To do this, change the part labeled
+`quotesForKeepSuggestion`:
+
+```typescript
+punctuation: {
+ quotesForKeepSuggestion: {
+ open: "«", close: "»"
+ },
+},
+```
+
+## Customizing `insertAfterWord`
+
+Many languages insert a space after a word. Some languages, like Thai or Khmer, do not use spaces. To suppress the space, you may set `insertAfterWord` to the empty string:
+
+```typescript
+punctuation: {
+ insertAfterWord: "",
+},
+```
+
+You can even use an alternate spacing character, if required by your language:
+
+```typescript
+punctuation: {
+ insertAfterWord: "\u200B", // U+200B ZERO WIDTH SPACE
+},
+```
+
+## See also
+
+[The TypeScript definition of
+`LexicalModelPunctuation`](https://github.com/keymanapp/keyman/blob/4211b468949860b8fb4a4707710472ab9e33c581/common/lexical-model-types/index.d.ts#L328-L371)
+
+------------------------------------------------------------------------
+
+[Return to “Advanced Lexical Model Topics”](./)
\ No newline at end of file
diff --git a/developer/docs/help/guides/lexical-models/advanced/search-term-to-key.md b/developer/docs/help/guides/lexical-models/advanced/search-term-to-key.md
new file mode 100644
index 00000000000..086a9ca01cf
--- /dev/null
+++ b/developer/docs/help/guides/lexical-models/advanced/search-term-to-key.md
@@ -0,0 +1,93 @@
+---
+title: Search term to key
+---
+
+To look up words quickly, the `trie` model creates a _search key_
+that takes the latest word (as determined by the
+[word breaker](word-breaker)) and converts it into an internal form. The
+purpose of this internal form is to make searching for a word work, as
+expected, regardless of things such as **accents**, **diacritics**,
+**letter case**, and minor **spelling variations**. The internal form is
+called the _key_. Typically, the key is always in
+lowercase, and lacks all accents and diacritics. For example, the key
+for “naïve" is `naive` and the key for “Canada” is `canada`.
+
+The form of the word that is stored is “regularized” through the use of a _key function_, which you can define in TypeScript code.
+
+> ### Note:
+This function runs both **on every word when the wordlist is compiled** and **on the input, whenever a suggestion is requested**. This way, whatever a user types is *matched* to something stored in the lexical model, without the user having to type things in a specific way.
+
+The key function takes a string which is the raw search term, and
+returns a new string, being the “regularized” key. As an example,
+consider the **default key function**; that is, the key function that is
+used if you do not specify one:
+
+```typescript
+searchTermToKey: function (term: string): string {
+ // Use this pattern to remove common diacritical marks (accents).
+ // See: https://www.compart.com/en/unicode/block/U+0300
+ const COMBINING_DIACRITICAL_MARKS = /[\u0300-\u036f]/g;
+
+ // Lowercase each letter in the string INDIVIDUALLY.
+ // Why individually? Some languages have context-sensitive lowercasing
+ // rules (e.g., Greek), which we would like to avoid.
+ // So we convert the string into an array of code points (Array.from(term)),
+ // convert each individual code point to lowercase (.map(c => c.toLowerCase())),
+ // and join the pieces back together again (.join(''))
+ let lowercasedTerm = Array.from(term).map(c => c.toLowerCase()).join('');
+
+ // Once it's lowercased, we convert it to NFKD normalization form
+ // This does many things, such as:
+ //
+ // - separating characters from their accents/diacritics
+ // e.g., "ï" -> "i" + "◌̈"
+ // - converting lookalike characters to a canonical ("regular") form
+ // e.g., ";" -> ";" (yes, those are two completely different characters!)
+ // - converting "compatible" characters to their canonical ("regular") form
+ // e.g., "𝔥𝔢𝔩𝔩𝔬" -> "hello"
+ let normalizedTerm = lowercasedTerm.normalize('NFKD');
+
+ // Now, using the pattern defined above, replace each accent and diacritic with the
+ // empty string. This effectively removes all accents and diacritics!
+ //
+ // e.g., "i" + "◌̈" -> "i"
+ let termWithoutDiacritics = normalizedTerm.replace(COMBINING_DIACRITICAL_MARKS, '');
+
+ // The resultant key is lowercased, and has no accents or diacritics.
+ return termWithoutDiacritics;
+},
+```
+
+This should be sufficient for most Latin-based writing systems. However,
+there are cases, such as with SENĆOŦEN, where some characters do not
+decompose into a base letter and a diacritic. In this case, it is
+necessary to write your own key function.
+
+## Use in your model definition file
+
+To use this in your model definition file, provide a function as the `searchTermToKey` property of the lexical model source:
+
+```typescript
+const source: LexicalModelSource = {
+ format: 'trie-1.0',
+ sources: ['wordlist.tsv'],
+ searchTermToKey: function (wordform: string): string {
+ // Your searchTermToKey function goes here!
+ let key = wordform.toLowerCase();
+ return key;
+ },
+ // other customizations go here:
+};
+
+export default source;
+```
+
+## Suggested customizations
+
+- For all writing systems, **normalize into NFKD** or **NFKC** form using `wordform = wordform.normalize('NFKD')`.
+- For Latin-based scripts, **lowercase** the word, and **remove diacritics**.
+- For scripts that use the U+200C zero-width joiner (ZWJ) and/or the U+200D zero-width non-joiner (ZWNJ) (e.g., Brahmic scripts), **remove the ZWJ or ZWNJ** from the **end** of the input with `wordform = wordform.replace(/[\u200C\u200D]+$/`
+
+------------------------------------------------------------------------
+
+[Return to “Advanced Lexical Model Topics”](./)
\ No newline at end of file
diff --git a/developer/docs/help/guides/lexical-models/advanced/unicode-breaker-extension.md b/developer/docs/help/guides/lexical-models/advanced/unicode-breaker-extension.md
new file mode 100644
index 00000000000..a9e4d3776cc
--- /dev/null
+++ b/developer/docs/help/guides/lexical-models/advanced/unicode-breaker-extension.md
@@ -0,0 +1,539 @@
+---
+title: Customization and extension of the Unicode word breaker
+---
+
+> ### Note:
+While this offers a great amount of flexibility and customization that may assist predictions for your language, note that this approach may get quite technical!
+
+As mentioned on the [main word breaker page](./word-breaker),
+our currently-supported lexical models need to know what a word
+is in running text. After all, it is quite difficult to look up a word in a
+dictionary when unclear about what the start and end of that word even is.
+
+In languages using the Latin script—like, English, French,
+and SENĆOŦEN—finding words is easy. Words are separated by spaces or
+punctuation. The actual rules for where to find words can get quite tricky to
+describe, but Keyman implements the [Unicode Standard Annex #29 §4.1 Default Word Boundary Specification](https://unicode.org/reports/tr29/#Word_Boundaries) which works well for most languages.
+
+This guide is about techniques that may be used to customize and extend
+the behaviors of that specification to be better tailored to your language.
+This is done by using similar patterns and structures to the rules found in
+the specification itself.
+
+There are three ways - all of them optional - to extend and customize
+the word-breaking rules themselves:
+
+* If you need to prevent splits in very specific scenarios and/or add splits in other specific scenarios, you may specify [context-based rules](#rules) to obtain the desired behavior.
+* If certain characters are not handled appropriately for their role in
+ your language, you may [map characters](#map) to different
+ word-breaking character classes - including custom ones. This will
+ override the default property they are assigned by the default
+ implementation, with the new property applying for all word-breaking
+ rules.
+* If the default word-breaking classes from the specification are
+ too general for certain aspects of your language, it is possible to
+ [define custom character classes](#define) for use in custom
+ rules.
+
+## A first example
+
+This example was designed to address the needs of a minority language in the country
+of Cambodia. The majority language does not use spaces for wordbreaking, while the
+minority language in question does use them. In addition, hyphens sometimes occur
+within words.
+
+The word breaker function can be specified in the
+[model definition file](./model-definition-file) as follows:
+
+```typescript
+const source: LexicalModelSource = {
+ format: 'trie-1.0',
+ sources: ['wordlist.tsv'],
+ // CUSTOMIZE THIS:
+ wordBreaker: (text) => {
+ let customization = {
+
+ /*** Definition of extra word-breaking rules ***/
+ rules: [{
+ match: (context) => {
+ if(context.propertyMatch(null, ["ALetter"], ["Hyphen"], ["ALetter"])) {
+ return true;
+ } else if(context.propertyMatch(["ALetter"], ["Hyphen"], ["ALetter"], null)) {
+ return true;
+ } else if(context.propertyMatch(null, ["ALetter"], ["Hyphen"], ["eot"])) {
+ return true;
+ } else {
+ return false;
+ }
+ },
+ breakIfMatch: false
+ }],
+
+ /*** Character class overrides for specific characters ***/
+ propertyMapping: (char) => {
+ let hyphens = ['\u002d', '\u2010', '\u058a', '\u30a0'];
+ // treats Khmer consonants & independent vowels in the same manner
+ // as the basic latin-script based alphabet
+ if(char >= '\u1780' && char <= '\u17b3') {
+ return "ALetter";
+ } else if(hyphens.includes(char)) {
+ return "Hyphen";
+ } else {
+ // The other Khmer characters already have useful word-breaking
+ // property assignments.
+ return null;
+ }
+ },
+
+ /*** Declares any new, custom character classes to be recognized by the word-breaker ***/
+ customProperties: ["Hyphen"]
+ };
+
+ /*** Connects all the pieces together for actual use ***/
+ return wordBreakers['default'](text, customization);
+ },
+ // ...
+};
+
+export default source;
+```
+
+This example's customization is designed to accomplish two goals:
+
+1. Unicode's wordbreaker does not map base Khmer consonants to any of the
+ [relevant wordbreaking character properties](https://unicode.org/reports/tr29/#Table_Word_Break_Property_Values), causing it to be treated as `"Other"`.
+ The minority language in question instead wishes for Latin-script-like word-breaking,
+ so mapping the consonants to the same property as Latin-script consonants allows
+ them to be treated similarly - in the manner they expect.
+
+2. By default, the wordbreaker will automatically insert word boundaries before
+ and after a hyphen. To allow mid-word hyphens, we need to remap them to
+ a different character property.
+
+ While the specification itself mentions that we could
+ just map hyphens to `"MidLetter"`, this example opts to define
+ a custom-tailored property that ensures only hyphens are affected in order to add
+ a special, end-of-context rule that may be useful when typing - the rule above
+ referencing `"eot"`.
+
+More on the wordbreaking character properties will be covered later.
+
+## Custom word-breaking rules
+
+When defining additional rules for use in word-breaking, it is advisable to reference
+the [rules of the Unicode Standard Annex #29 §4.1 Default Word Boundary Specification](https://unicode.org/reports/tr29/#Word_Boundary_Rules).
+
+Rules WB1 through WB4 of the specification will always apply first, before any
+custom rules. Custom rules will then be applied in order of their definition within
+the model, with them all being applied before the specification's rules labeled WB5
+and onward.
+
+Each rule should be of the following form:
+
+```typescript
+{
+ // A function that returns 'true' whenever the rule should apply
+ match: (context) => {
+ // ...
+ },
+ // Whether to prevent (false) or to enforce (true) a boundary when the rule applies.
+ breakIfMatch: false
+}
+```
+
+Toward this end, the `context` object received by match provides a function called
+`propertyMatch` in order to define rules like those of the wordbreaker specification.
+Let's take WB6 - "do not break letters across certain punctuation" - as an example.
+
+As written in the spec, WB6 reads **AHLetter x (MidLetter | MidNumLetQ) AHLetter**.
+
+This is simply a series of characters, up to two characters before and after a potential break point. To break that down:
+- (implicit) **Any** -- accept any character in this position
+- **AHLetter**: `ALetter` or `Hebrew_Letter`
+- **x** - "do not break"
+- (either) **MidLetter** or **MidNumLetQ**: one of `MidLetter`, `MidNumLet`, or `Single_Quote`.
+- **AHLetter**: `ALetter` or `Hebrew_Letter`
+
+The expansions `MidNumLetQ` and `AHLetter` are defined at https://unicode.org/reports/tr29/#WB_Rule_Macros.
+
+If written as a custom rule, rule WB6 takes the following form. Note the use of the function `context.propertyMatch`,
+which takes 4 parameters - two to match characters before and two to match characters after a potential boundary:
+
+```typescript
+// Rule WB6 from the Unicode spec, as a custom rule:
+{
+ match: (context) => {
+ return context.propertyMatch(null, // no requirements set, so "Any" character may match
+ ["ALetter", "Hebrew_Letter"],
+ // x
+ ["MidLetter", "MidNumLet", "Single_Quote"],
+ ["ALetter", "Hebrew_Letter"]);
+ },
+ breakIfMatch: false // do not break
+}
+```
+
+Note that an empty array `[]` in any slot is not treated the same as `null` -
+use of an empty `[]` will prevent the rule from matching.
+
+Almost all of the specification's rules are of this form. Again, up to two characters
+before a potential boundary may be considered alongside up to two characters after
+the potential boundary.
+
+### Word-breaking property names
+The names used in each array must be defined in one of the following places:
+* https://unicode.org/reports/tr29/#Table_Word_Break_Property_Values
+* `customProperties` - your [declaration of any custom property types](#define)
+* One of the special property types `"Other"`, `"sot"`, or `"eot"`:
+ * `Other`: a character without an associated word-breaking property value
+ * `sot`: "start of text" - a marker indicating the beginning of the string being word-broken
+ * `eot`: "end of text" - a marker indicating the end of the string being word-broken
+
+All rules use case-insensitive matching, so capitalization differences will not affect operation.
+
+### Rule-matching examples
+
+#### A successful rule application
+As an example, when determining whether or not to break the English word `don't` when
+applying WB6 as written above, this is what happens near the apostrophe:
+
+```typescript
+{
+ match: (context) => {
+ return context.propertyMatch(null, /* match any character */ // "o" - ALetter
+ ["ALetter", "Hebrew_Letter"], // "n" - ALetter
+ // x
+ ["MidLetter", "MidNumLet", "Single_Quote"], // "'" - Single_Quote
+ ["ALetter", "Hebrew_Letter"]); // "t" - ALetter
+ },
+ breakIfMatch: false // do not wordbreak at any position where the rule matches.
+}
+```
+
+The rule applies at the position between the `n` and the `'` of `don't`, telling the
+word-breaker not to word-break if the rule matches at that location.
+
+#### An unsuccessful rule match
+
+Given the input `n't|` (where `|` is the caret), we can see that the rule will not match:
+```typescript
+{
+ match: (context) => {
+ return context.propertyMatch(null, /* automatic match */ // "n" - ALetter
+ ["ALetter", "Hebrew_Letter"], // "'" - Single_Quote
+ // x
+ ["MidLetter", "MidNumLet", "Single_Quote"], // "t" - Single_Quote
+ ["ALetter", "Hebrew_Letter"]); // - eot
+ },
+ breakIfMatch: false // do not break
+}
+```
+
+The same rule does not apply between the `'` and the `t`, so it does not apply at this position in
+the text. This is why the wordbreaking spec includes rule WB7 in addition to WB6 - some scenarios
+require multiple context property-matching attempts.
+
+You may define any number of these rule objects in any order for use within the wordbreaker within
+the `rules` array.
+
+> **Important note**: When no other rule successfully matches a potential word-boundary position, the spec's rule WB999 applies as a catch-all and will enforce a split. Preventing a word-break always requires a successfully-matching rule, though this is covered for most cases by the Unicode specification's ruleset.
+
+Defining custom rules is a powerful tool, but it is detailed work and may be somewhat tedious
+to get right. Feel free to [ask for help at our Community Site](https://community.software.sil.org/c/keyman/19) for assistance.
+
+## Character property remapping
+
+Many writing systems in the world are shared by multiple languages, using most of the same
+characters in common. However, sometimes there may be notable differences in how specific
+languages wish to treat certain characters. In order to address these cases, we allow
+overriding the "standard" word-breaking property that the Unicode specification gives the
+character with one set by the lexical model.
+
+### Default character properties
+
+For reference, [this text file](https://www.unicode.org/Public/UCD/latest/ucd/auxiliary/WordBreakProperty.txt)
+provides the standard word-breaking properties for all characters. This is one of many
+files Unicode provides publicly here: https://www.unicode.org/reports/tr41/#Props0.
+
+That text file contains many lines of the following form:
+
+```
+0041..005A ; ALetter # L& [26] LATIN CAPITAL LETTER A..LATIN CAPITAL LETTER Z
+```
+
+This says that `0041` through `005A` - or rather, `\u0041` (the code for 'A') through
+`\u005A` (the code for 'Z') - are assigned word-breaking property `ALetter`. That
+range covers 26 (`[26]`) characters. (For our purposes here, the `L&` part is
+irrelevant.)
+
+As noted at the top of the file:
+
+```
+# All code points not explicitly listed for Word_Break
+# have the value Other (XX).
+
+# @missing: 0000..10FFFF; Other
+```
+
+### Redefining character properties
+
+Of note from [our first example](#example):
+
+```typescript
+/*** Character class overrides for specific characters ***/
+propertyMapping: (char) => {
+ // ...
+ // treats Khmer consonants & independent vowels in the same manner
+ // as the basic latin-script based alphabet
+ if(char >= '\u1780' && char <= '\u17b3') {
+ return "ALetter";
+ } // ...
+},
+```
+
+If you search the [property definition text file](https://www.unicode.org/Public/UCD/latest/ucd/auxiliary/WordBreakProperty.txt)
+for `1780` or `17b3`, you will find neither. These correspond to many letters
+from the Khmer character set - notably, the 'base' characters used in Khmer's
+grapheme clusters. The other Khmer characters tend to attach at various positions
+around these base characters. The majority language for the script - Khmer -
+does not follow conventional word-breaking rules; most notably, they do not add
+whitespace between each word. (There are other strategies that get utilized for
+such scripts.)
+
+As breaks sometimes occur between the base characters while other times do not,
+properties for these base characters were not explicitly defined and are thus
+treated as class `Other`.
+- As other Khmer characters tend to attach around the base characters, they do
+ have specified word-breaking properties - they `Extend` the grapheme cluster.
+- Searching `17b4` in [the text file mentioned above](https://www.unicode.org/Public/UCD/latest/ucd/auxiliary/WordBreakProperty.txt) will show the closest results to the characters under discussion.
+
+However, there are minority languages that prefer to use whitespaces between words,
+meaning that there should never be wordbreaks applied directly between neighboring
+characters for their words. In such cases, we can map them to a pre-existing property
+with the desired behavior - `ALetter`.
+
+```typescript
+if(char >= '\u1780' && char <= '\u17b3') {
+ return "ALetter"; // Maps Khmer-script base characters to ALetter
+ // to allow Latin-script-like word-breaking.
+}
+```
+
+For another example of character property remapping, consider the use of hyphens with
+words (and/or names) in some languages. Default word-breaking behavior will split
+hyphenated words and names apart, but by changing the property of hyphens, it is
+possible to disable this behavior.
+
+Noting [rule WB6](#WB6) and WB7, the `MidLetter` class is designed to prevent
+word-breaks from occurring when its characters lie directly between letters -
+hence the property name. Assigning hyphens to this class can provide the
+desired behavior.
+
+```typescript
+let hyphens = ['\u002d', '\u2010', '\u058a', '\u30a0'];
+// ...
+if(hyphens.includes(char)) {
+ return "MidLetter";
+} // ...
+```
+
+## Defining and using new word-breaking properties
+
+There may be some cases in which none of the default character word-breaking
+properties provide the exact behavior that you're wanting, or perhaps you
+want only specific characters from that class to match custom rules. For such
+cases, wordbreaker customization also allows definition of new word-breaking
+properties.
+
+For one example, note how word-breaking operations affect predictions when
+typing new words:
+
+- `can'`, with the intent to type `can't`
+- `full-`, with the intent to type `full-scale`
+
+For the first example above, while `'` (property `Single_Quote`) is included within
+WB6 and WB7, those rules only apply _between letters_. If there is no letter
+on the right-hand side, `can'` will be interpreted as `can` + `'` by the
+word-breaking algorithm. Similarly, even when remapping `-` to the `MidLetter`
+property, `full-` will be remapped to `full` + `-` before additional text
+is typed.
+
+Of course, this problem does alleviate itself once another `ALetter`-property
+letter is typed, but suppose we wanted a rule to prevent word-breaking for
+the second example above. (After all, `can'` could be the end of a quoted
+phrase in English - `'sure you can'` - in which case we might want the split
+to occur.)
+
+Revisiting [an earlier example](#example) and simplifying a little bit:
+
+```typescript
+/*** Definition of extra word-breaking rules ***/
+{
+ rules: [{
+ match: (context) => {
+ if(context.propertyMatch(null, ["ALetter"], ["Hyphen"], ["eot"])) {
+ return true;
+ } else {
+ /* ... */
+ } else {
+ return false;
+ }
+ },
+ breakIfMatch: false
+ }],
+
+ /*** Character class overrides for specific characters ***/
+ propertyMapping: (char) => {
+ let hyphens = ['\u002d', '\u2010', '\u058a', '\u30a0'];
+ if(hyphens.includes(char)) {
+ return "Hyphen";
+ } else {
+ // Use the default properties for anything else.
+ return null;
+ }
+ },
+
+ /*** Declares any new, custom character classes to be recognized by the word-breaker ***/
+ customProperties: ["Hyphen"]
+}
+```
+
+Let's walk through what this simplification is trying to achieve:
+
+1. Hyphens are mapped to their own distinct word-breaking property.
+2. The custom rule prevents wordbreaking between a letter and a hyphen at the end of text.
+ - It does not include any of the `MidLetter` characters.
+
+Matching the rule against the end of text suggests that what follows the `Hyphen` character
+is the point of text insertion. For this example, assuming that a user has just typed
+`full-`, there will be no word-break on the hyphen until either more input is received or
+the user changes the site of text entry.
+
+**Important note**: You must declare any custom properties within the `customProperties` array.
+If any are missing, the missing custom properties will fail to match against any word-breaking rule.
+Make sure you don't misspell it anywhere in your customization code!
+
+### Default rules + custom properties
+
+There is one notable issue with this example, though - whenever you remap a character to
+a new property, it is no longer considered to have its old property, and so it will no
+longer match rules based on its default property. This is why the original example
+included a couple of extra rules:
+
+```typescript
+{
+ match: (context) => {
+ // Extend WB6 - allow "Hyphen" in the same place as "MidLetter"
+ if(context.propertyMatch(null, ["ALetter"], ["Hyphen"], ["ALetter"])) {
+ return true;
+ // Extend WB7 - allow "Hyphen" in the same place as "MidLetter"
+ } else if(context.propertyMatch(["ALetter"], ["Hyphen"], ["ALetter"], null)) {
+ return true;
+ // The same rule from above.
+ } else if(context.propertyMatch(null, ["ALetter"], ["Hyphen"], ["eot"])) {
+ return true;
+ } else {
+ return false;
+ }
+ },
+ breakIfMatch: false
+}
+```
+
+By replicating [WB6](#WB6) and WB7's structure and allowing `Hyphen` to match in the same
+position as `MidLetter` in the original rules, we can prevent word-breaking splits
+after additional text has been typed after a `Hyphen`-property character. This does not
+_replace_ the behavior of WB6 and WB7 - it merely _extends_ it to include the new property.
+
+### A more complex case
+
+A meatier example may be found as [the specification's hypothetical rule WB5a](https://unicode.org/reports/tr29/#WB999):
+
+> "Break between apostrophe and vowels (French, Italian)"
+>
+> WB5a: **Apostrophe ÷ Vowels**.
+
+The idea of this rule is to allow words such as the French `l'objectif` to be split
+into the article - `l'` and its following word - `objectif` while preserving other
+cases that should still be treated as single words, such as `aujourd'hui`.
+
+To simplify the code needed for customization here somewhat, we will use `Single_Quote`
+in place of `Apostrophe`, as well as a few extra simplifications. The new `Vowels`
+property offers enough complexity as it is.
+
+```typescript
+let customization = {
+ rules: [
+ // WB5 extension - ensure `AVowel` is handled like `ALetter`.
+ {
+ match: (context) => {
+ if(context.propertyMatch(null, ["ALetter", "AVowel"], ["ALetter", "AVowel"], null)) {
+ return true;
+ } else {
+ return false;
+ }
+ },
+ breakIfMatch: false
+ },
+ // Our main goal WB5a
+ {
+ match: (context) => {
+ if(context.propertyMatch(null, ["Single_Quote"], ["AVowel"], null)) {
+ return true;
+ } else {
+ return false;
+ }
+ },
+ breakIfMatch: true
+ },
+ // WB6, 7 extension - ensure `AVowel` is handled like `ALetter`
+ {
+ match: (context) => {
+ if(context.propertyMatch(null,
+ ["ALetter", "AVowel"],
+ ["MidLetter", "MidNumLet", "Single_Quote"],
+ ["ALetter", "AVowel"])) {
+ return true;
+ } else if(context.propertyMatch(["ALetter", "AVowel"],
+ ["MidLetter", "MidNumLet", "Single_Quote"],
+ ["ALetter", "AVowel"],
+ null)) {
+ return true;
+ } else {
+ return false;
+ }
+ },
+ breakIfMatch: false
+ }
+ // Similar extensions to WB9, 10, 13a, and 13b would also be needed for robustness.
+ // Note: we have left "Hebrew_Letter" out of the WB5, 6, and 7 rewrites to help
+ // simplify this example.
+ ],
+ propertyMapping: (char) => {
+ const vowels = ['a', 'e', 'i', 'o', 'u'];
+ // French and Italian allow accented vowels; this will strip off the accent and
+ // leave us with the base vowel.
+ const baseChar = char.normalize('NFD').charAt(0);
+ if(vowels.includes(baseChar)) {
+ return "AVowel";
+ }
+
+ return null;
+ },
+ customProperties: ["AVowel"]
+}
+```
+
+Note that we have left out some of the rule extensions that would help cover certain
+less-frequently encountered cases. These may matter to some parts of a
+language community, especially for models targeting a majority language.
+Computer programmers in particular tend to care about the un-extended
+rules (WB9, 10, 13a, and 13b) more than most.
+
+Remember, remapping characters to a new word-breaking property prevents any default
+rule from handling them unless you add custom rules to re-include them as their
+new property.
+
+------
+
+[Return to “Advanced Lexical Model Topics”](./)
diff --git a/developer/docs/help/guides/lexical-models/advanced/word-breaker.md b/developer/docs/help/guides/lexical-models/advanced/word-breaker.md
new file mode 100644
index 00000000000..f8f321327ac
--- /dev/null
+++ b/developer/docs/help/guides/lexical-models/advanced/word-breaker.md
@@ -0,0 +1,218 @@
+---
+title: Word breaker
+---
+
+The `trie` family of lexical models needs to know what a word is in
+running text. In languages using the Latin script—like, English, French,
+and SENĆOŦEN—finding words is easy. Words are separated by spaces or
+punctuation. The actual rules for where to find words can get quite
+tricky to describe, but Keyman implements the [Unicode Standard Annex #29 §4.1 Default Word Boundary Specification](https://unicode.org/reports/tr29/#Word_Boundaries)
+which works well for most languages.
+
+However, in languages written in other scripts — especially East Asian
+scripts like Chinese, Japanese, Khmer, Lao, and Thai — there are no obvious break in between words. For these languages, there must be special rules for determining when words start and stop. This is what a _word breaker function_ is responsible for. It is a little bit of code that looks at some text to determine where the words are.
+
+You can customize the word breaker in three ways:
+- If your language uses its writing system in an unconventional way (e.g., use spaces to separate words in Thai, Lao, Burmese, or Khmer), you can [override the script's default behaviour](#overrides)
+- If the default word breaker creates **too many splits**, you can [choose which strings join words together](#join).
+- If the default word breaker creates **not enough splits**, you must [create your own word breaker function](#custom).
+- Alternatively, you may choose to [customize and extend the wordbreaker's behavior](./unicode-breaker-extension) by adding extra rules and changing how it treats specific characters.
+
+## Overriding script defaults
+
+The default word breaker makes assumptions about how each
+_script_ (alphabet, syllabary, or writing system)
+works. You can override the defaults by specifying the
+`overrideScriptDefaults` option.
+
+There is currently only one override:
+
+`'break-words-at-spaces'`
+: Only breaks words at spaces for scripts that otherwise do not use spaces in between words.
+
+### Break words at spaces
+
+This applies only to languages that borrow the **Burmese**, **Khmer**,
+**Lao**, or **Thai** scripts. The majority languages for these scripts
+do *not* use spaces in between words; hence, the default word breaker
+will produce undesired results when breaking words in these scripts.
+However, if your language is written in one of these scripts and *does*
+use spaces in between words, then you can set
+`overrideScriptDefaults: 'break-words-at-spaces',` to ensure word breaks
+do not occur in the middle of words, but instead, at spaces.
+
+Your model definition file should look like this:
+
+```typescript
+const source: LexicalModelSource = {
+ format: 'trie-1.0',
+ sources: ['wordlist.tsv'],
+ wordBreaker: {
+ use: 'default', // we want to use the default word breaker, BUT!
+ // Override the default for Burmese, Khmer, Lao, or Thai:
+ overrideScriptDefaults: 'break-words-at-spaces',
+ }
+};
+
+export default source;
+```
+
+## Customize joining rules
+
+The default word breaker is very liberal in what it considers is a word.
+
+For instance, the default word breaker will split words at hyphens.
+Consider the following Plains Cree example; this is a single word:
+
+: amiskwaciy-wâskahikan
+
+However, the default word breaker will produce three words: `amiskwaciy`, `-`, and `wâskahikan`.
+
+To **join words at hyphens and any other punctuation**, provide the
+`joinWordsAt` option in the [model definition file](./model-definition-file):
+
+```typescript
+const source: LexicalModelSource = {
+ format: 'trie-1.0',
+ sources: ['wordlist.tsv'],
+ wordBreaker: {
+ use: 'default', // we want to use the default word breaker, BUT!
+ // CUSTOMIZE THIS:
+ joinWordsAt: ['-'], // join words that contain hyphens
+ }
+};
+
+export default source;
+```
+
+You can specify one or more strings to join words at:
+
+```typescript
+const source: LexicalModelSource = {
+ format: 'trie-1.0',
+ sources: ['wordlist.tsv'],
+ wordBreaker: {
+ use: 'default',
+ // CUSTOMIZE THIS:
+ joinWordsAt: ['-', ':', '@'], // join words at hyphens, colons, at-signs
+ }
+};
+
+export default source;
+```
+
+## Writing a custom word breaker function
+
+> **Note**:
+If your language uses spaces to denote word breaks, the
+default word breaker is probably sufficient. Only customize this if you
+know the default word breaker really does not work for your language!
+
+The word breaker function can be specified in the [model definition file](./model-definition-file) as follows:
+
+```typescript
+const source: LexicalModelSource = {
+ format: 'trie-1.0',
+ sources: ['wordlist.tsv'],
+ // CUSTOMIZE THIS:
+ wordBreaker: {
+ use: function(text: string): Span[] {
+ // Return zero or more **spans** of text:
+ return [];
+ },
+ },
+ // other customizations go here:
+};
+
+export default source;
+```
+
+The function must return zero or more `Span` objects. The spans,
+representing an indivisible span of text, must be in ascending order of
+their start point, and they must be non-overlapping.
+
+### A `Span` object
+
+A _span_ is an indivisible piece of a sentence.
+This is typically a word, but it can also be a series of spaces, an
+emoji, or a series of punctuation characters. **A span that looks like a word is treated like a word in the `trie-1.0` model**.
+
+A `span` has the following properties:
+
+```typescript
+{
+ start: number;
+ end: number;
+ length: number;
+ text: string;
+}
+```
+
+The `start` and `end` properties are indices into the original string at
+which the span begins, and the index at which the *next* span begins.
+
+`length` is `end - start`.
+
+`text` is the actual text of the string contained within the span.
+
+## Example for English
+
+Here is a full example of word breaker function that returns an array of
+spans in an ASCII (English) string.
+
+> **Note**
+This is just an example—please
+use the default word breaker for English text!
+
+```typescript
+const source: LexicalModelSource = {
+ format: 'trie-1.0',
+ sources: ['wordlist.tsv'],
+ // EXAMPLE BEGINS HERE:
+ wordBreaker: function(text: string): Span[] {
+ // A span derived from a JavaScript RegExp match array:
+ class RegExpDerivedSpan implements Span {
+ readonly text: string;
+ readonly start: number;
+
+ constructor(text: string, start: number) {
+ this.text = text;
+ this.start = start;
+ }
+
+ get length(): number {
+ return this.text.length;
+ }
+
+ get end(): number {
+ return this.start + this.text.length;
+ }
+ }
+
+ let matchWord = /[A-Za-z0-9']+/g;
+ let words: Span[] = [];
+ let match: RegExpExecArray;
+ while ((match = matchWord.exec(phrase)) !== null) {
+ words.push(new RegExpDerivedSpan(match[0], match.index));
+ }
+
+ return words;
+ },
+ // other customizations go here:
+};
+
+export default source;
+```
+
+## See also
+
+- [The TypeScript definition of `WordBreakingFunction` and
+`Span`](https://github.com/keymanapp/keyman/blob/4211b468949860b8fb4a4707710472ab9e33c581/common/lexical-model-types/index.d.ts#L286-L323)
+
+- [Extension and customization of the Unicode word-breaker](./unicode-breaker-extension)
+
+- [The Unicode Standard Annex \#29 §4.1 Default Word Boundary Specification](https://unicode.org/reports/tr29/#Word_Boundaries)
+
+------------------------------------------------------------------------
+
+[Return to “Advanced Lexical Model Topics”](./)
\ No newline at end of file
diff --git a/developer/docs/help/guides/lexical-models/distribute/index.md b/developer/docs/help/guides/lexical-models/distribute/index.md
new file mode 100644
index 00000000000..42f3741af17
--- /dev/null
+++ b/developer/docs/help/guides/lexical-models/distribute/index.md
@@ -0,0 +1,6 @@
+---
+title: Distributing lexical models
+---
+
+- [Lexical model package development tutorial](tutorial/) (Keyman for mobile apps)
+- [Distribute lexical models to Keyman applications](packages)
\ No newline at end of file
diff --git a/developer/docs/help/guides/lexical-models/distribute/packages.md b/developer/docs/help/guides/lexical-models/distribute/packages.md
new file mode 100644
index 00000000000..92e61650308
--- /dev/null
+++ b/developer/docs/help/guides/lexical-models/distribute/packages.md
@@ -0,0 +1,70 @@
+---
+title: Distribute lexical models to Keyman Applications
+---
+
+## Overview
+
+Lexical model package files contain one lexical model, along with readme
+files, and any other files you wish to include. You must create a
+package file to bundle your lexical model and help documentation into a
+simple, single file that is easy for an end-user to install.
+
+The package file is a ZIP compatible archive.
+
+> ### Tip
+You can distribute keyboards and lexical models in package files, but
+you can't include both in the same package file.
+
+#### Keyman for Android and Keyman for iPhone and iPad
+
+Keyman mobile applications can install the same lexical model package
+files.
+
+## Package file contents
+
+A package can have a variety of different files contained within. The
+following files and file types are recognized by the package installer:
+
+\*.model.js
+: Lexical model file. When Keyman mobile applications install a
+ lexical model package, the included model will be installed and
+ associated with the specified languages.
+
+welcome.htm
+: Introductory help for the lexical model, HTML format. This will
+ normally be displayed when the package is installed by the user, and
+ is also the entry point for help when accessed via Keyman's help
+ system or Keyman Configuration.
+
+## Step 1) Share the lexical model package file
+
+Once the lexical model package .kmp file is created, you can share them
+via external storage devices (USB drive, SD card, etc). If that is not
+an option, you can upload the .kmp file to a public facing website. For
+this example, the lexical model package for SENĆOŦEN is being uploaded:
+
+1. nrc.str.sencoten.model.kmp (the lexical model package .kmp file)
+
+## Step 2) Create a link to the KMP file
+
+Once all the files have been uploaded, you will need to provide a link
+to the lexical model package .kmp file for your device to download and
+install. This can either be a link on a web page, or a link in an email.
+In this tutorial, a very simple .html web page with a link to the
+nrc.str.sencoten.kmp file is created:
+
+```html
+
+
+
+
+ Sencoten Lexical Model Package
+
+
+```
+
+The link must be in the format `http://` or `https://`
+
+Upload the web page to your public facing website. Once done, you can
+install the lexical model package onto your mobile devices by
+downloading the .kmp from your device's internet browser.
\ No newline at end of file
diff --git a/developer/docs/help/guides/lexical-models/distribute/tutorial/index.md b/developer/docs/help/guides/lexical-models/distribute/tutorial/index.md
new file mode 100644
index 00000000000..fe4e0d039ec
--- /dev/null
+++ b/developer/docs/help/guides/lexical-models/distribute/tutorial/index.md
@@ -0,0 +1,32 @@
+---
+title: Lexical Model Package Tutorial
+---
+
+[Step 1: What do we include?](step-1)
+
+[Step 2: Editing .htm files for the Package](step-2)
+
+[Step 3: Checking a package and adding files](step-3)
+
+[Step 4: Filling in package details](step-4)
+
+[Step 5: Compiling, testing and distributing a Package](step-5)
+
+## Overview
+
+Welcome! In this tutorial, you will learn how to create a lexical model
+[package](../../../../reference/file-types/kmp). A package is a
+collection of files, compressed into a single file, just like a ZIP
+file. It is designed to make installation of a lexical model
+straightforward for the end user. When done, Keyman for mobile apps can
+download and install lexical models through the package.
+
+We will be creating a package for the SENĆOŦEN lexical model that was
+made in [Developing a lexical model from a word list](../../tutorial/).
+
+### Let's begin
+
+Let's get started! Move on to the next topic to begin the first step,
+choosing what to include in the package.
+
+[Step 1: What do we include?](step-1)
\ No newline at end of file
diff --git a/developer/docs/help/guides/lexical-models/distribute/tutorial/step-1.md b/developer/docs/help/guides/lexical-models/distribute/tutorial/step-1.md
new file mode 100644
index 00000000000..d27970b9e43
--- /dev/null
+++ b/developer/docs/help/guides/lexical-models/distribute/tutorial/step-1.md
@@ -0,0 +1,15 @@
+---
+title: Step 1: What do we include?
+---
+
+What is the purpose of a lexical model package? To make installation of
+a lexical model as straightforward as possible for the end user. We need
+to keep this goal in mind as we work on all the aspects of a package.
+
+A great package will:
+
+1. Include one lexical model to install.
+2. Include a 'readme.htm' file which describes why the user would install this package.
+3. Include a 'welcome.htm' file which is displayed before/after install.
+
+[Step 2: Editing .htm files for the package](step-2)
\ No newline at end of file
diff --git a/developer/docs/help/guides/lexical-models/distribute/tutorial/step-2.md b/developer/docs/help/guides/lexical-models/distribute/tutorial/step-2.md
new file mode 100644
index 00000000000..10a560fd194
--- /dev/null
+++ b/developer/docs/help/guides/lexical-models/distribute/tutorial/step-2.md
@@ -0,0 +1,72 @@
+---
+title: Step 2: Editing .htm files for the package
+---
+
+When Keyman Developer created your lexical model project, it will have
+created some of these files to go in the package. You will still need to
+edit some of these templated files for the package.
+
+readme.htm
+
+: A short description of the package, its use restrictions, and what
+ it includes. Try to keep the readme under 10 lines long. The readme
+ should be an html file for optimal formatting.
+
+ The intention of readme.htm is to describe why a user would want to
+ install the lexical model.
+
+ Create the HTML file in any HTML editor.
+
+ > ### Tip
+ If using Microsoft Word, choose HTML (clean) when saving to create a
+ smaller file.
+
+ Note, if your HTML editor puts images into a subfolder, you will
+ need to edit the HTML source so that all files are in the same
+ folder -- the package builder will not maintain subfolders. You can
+ easily edit the HTML source in Keyman Developer.
+
+ Also, if your welcome or readme files use embedded external images,
+ stylesheets, javascript or other files, you will need to add these
+ files to your package as well.
+
+welcome.htm
+
+: When including an introductory help file in your package, you must
+ keep the name of the file "welcome.htm". This file will be displayed
+ before the lexical model is installed. Make sure that you design
+ your HTML file so that it can be viewed on a mobile device - avoid
+ extra wide tables or wide fixed width elements.
+
+ After package installation, the welcome.htm will also be accessible
+ from the lexical model info pages.
+
+ The intention of welcome.htm is to provide instructions on getting
+ started with your lexical model.
+
+ > ### Tip
+ Useful information to include in welcome.htm is:
+ - names of languages associated with the lexical model.
+ - name of the lexical model in the package.
+ - links to additional help on your website or more extensive
+ documentation files in the package.
+ - a link to an official distribution site for your package (even
+ if it is the Keyman website) - so that users know where to find
+ the latest version of your package.
+
+
+
+ > ### Tip
+ If you want links to your website to open in the user's preferred
+ browser, preface the href link with `link:`, e.g.
+ `website`
+ The `link:` sceheme will open the referred file in the default
+ application - that is, a web browser for URLs and links, Notepad for
+ .txt files, Adobe Reader for PDFs. You can use `link:` to open any
+ of the files in the package, e.g. `link:docs.pdf` will open the file
+ docs.pdf in Adobe Reader or the default PDF reader on the system.
+
+ If you create documents in other formats, for example PDF or
+ printable documentation, you should link to that in the welcome.htm.
+
+[Step 3: Checking a package and adding files](step-3)
\ No newline at end of file
diff --git a/developer/docs/help/guides/lexical-models/distribute/tutorial/step-3.md b/developer/docs/help/guides/lexical-models/distribute/tutorial/step-3.md
new file mode 100644
index 00000000000..9d1ea260002
--- /dev/null
+++ b/developer/docs/help/guides/lexical-models/distribute/tutorial/step-3.md
@@ -0,0 +1,51 @@
+---
+title: Step 3: Checking a package and adding files
+---
+
+We are finally ready to open up the Package Editor and check the package
+contents. In the Project Window in Keyman Developer, click the
+**Packaging** tab.
+
+> ### Tip
+This tutorial makes a package named `nrc.str.sencoten.model.kps`, but
+you should substitute the name with your own model.
+
+A lexical model package source file will have the extension .model.kps,
+and will be compiled in a file with extension .kmp.
+
+## Files
+
+In the Package Editor, click on the **Files**
+tab.
+
+![Lexical Model Package Files](../../../../images/lm/tutorial_distribute_model_3_files.png)
+
+Keyman Developer already included your lexical model, welcome.htm, and
+readme.htm files in your package. In the Files tab, click
+**Add** to add all the additional files we
+discussed in the previous step to the package. For example, if your
+welcome or readme files use embedded files, include them in your
+package. You can add multiple files at once, and from multiple folders.
+When the package is compiled, all the files will be placed in the same
+folder within the package.
+
+While keyboards can also be distributed in packages, do not include them
+in a lexical model package.
+
+## Lexical Models
+
+In the Package Editor, click on the **Lexical Models** tab.
+
+![Lexical Model Info](../../../../images/lm/tutorial_distribute_model_3.png)
+
+Language Tag
+
+: A valid BCP 47 language tag must be set or the lexical model will
+ not install on your mobile device. Update the list of languages you
+ want to associate with your lexical model.
+
+You could stop here. This would be a completely valid package, but it
+would not be as good as it could be. So let's continue on to the next
+step, and fill in some descriptions of the package.
+
+[Step 4: Filling in package details](step-4)
\ No newline at end of file
diff --git a/developer/docs/help/guides/lexical-models/distribute/tutorial/step-4.md b/developer/docs/help/guides/lexical-models/distribute/tutorial/step-4.md
new file mode 100644
index 00000000000..bae0be8c676
--- /dev/null
+++ b/developer/docs/help/guides/lexical-models/distribute/tutorial/step-4.md
@@ -0,0 +1,55 @@
+---
+title: Step 4: Filling in package details
+---
+
+In the Package Editor, click on the **Details** tab. You should fill in as many details as you can on this page.
+
+![](../../../../images/lm/tutorial_distribute_model_details.png)
+
+Package Name
+
+: The Package Name will be displayed in the package install dialog and
+ wherever the package is referred to.
+
+Model Version
+
+: Update the version (intial version can default to 1.0). A version
+ number for the model and package is important - it helps your users
+ know that they are using the most recent update of your package. The
+ version format you should use is `1.0`.
+
+ When making a major change to your lexical model package, increment
+ the first part and set the second part to `0`, e.g. from `1.0` to
+ `2.0`.
+
+ When making a bug fix or a minor update, increment the second part,
+ e.g. from `1.0` to `1.1`.
+
+ Version numbers should be in the form `major.minor[.subversion]`.
+ Subversion is optional but is helpful for small bug fix releases.
+ Each of the sections of the version should be an integer. Keyman
+ does integer comparisons on the version numbers, so, for example,
+ version `2.01` is regarded as older than version `2.2`. Alphabetic
+ or date formats should be avoided as the installer for the model
+ cannot determine which version is older reliably.
+
+Copyright
+
+: Enter copyright details for your lexical model package. Keep this
+ reasonably short or it won't be clear for end users.
+
+Author
+
+: Enter your name or the name of your company.
+
+Email
+
+: Enter a contact email address where package users can contact you.
+ If you don't want to be contacted via email, leave this field empty
+
+Website
+
+: Enter the name of the website where you will have information about
+ this lexical model.
+
+[Step 5: Compiling, testing and distributing a Package](step-5)
\ No newline at end of file
diff --git a/developer/docs/help/guides/lexical-models/distribute/tutorial/step-5.md b/developer/docs/help/guides/lexical-models/distribute/tutorial/step-5.md
new file mode 100644
index 00000000000..ed873c69cab
--- /dev/null
+++ b/developer/docs/help/guides/lexical-models/distribute/tutorial/step-5.md
@@ -0,0 +1,55 @@
+---
+title: Step 5: Compiling, testing and distributing a Package
+---
+
+In the Package Editor, click on the **Compile** tab.
+
+![](../../../../images/lm/tutorial_distribute_model_compile.png)
+
+Click **Compile Package** to compile the
+package into a .kmp file. Compiling takes all the files listed for the
+package, compresses them (using a .ZIP-compatible format) and adds the
+package information, all into a single file. If any files are not
+available, an error will be listed in the Messages window.
+
+After compiling, you can test the package installation in the mobile
+Keyman apps. You should test that all the model installs successfully,
+that the Welcome file is displayed during the install, and that the
+documentation is accessible to the end user.
+
+## Distributing a package on the Keyman Cloud Lexical Model Repository
+
+Once you have tested the package to your satisfaction, it is time to
+distribute it. We recommend submitting your lexical model package to the
+[Keyman Lexical Models Repository](https://github.com/keymanapp/lexical-models)
+
+## Distributing a package on your own website
+
+If you distribute a package on your own site, we have the following
+recommendations:
+
+1. Ensure the MIME type on the web server or folder for .KMP files is
+ set up to application/octet-stream. Without this, .KMP files may be
+ recognised as .ZIP files -- this is not helpful to the end user as
+ it will be opened in the wrong application.
+2. Avoid putting the .KMP file in an archive (e.g. .ZIP) or
+ self-expanding archive (.EXE) - this makes it harder for end users
+ to install. A .KMP file is already compressed (it is actually just a
+ ZIP archive file!) and you won't save much space by recompressing
+ it.
+3. Include a link to the Keyman download page:
+ `http://keyman.com/downloads/`
+
+## Distributing a package by email
+
+- Attaching the KMP file directly to an email may be blocked for security
+reasons. As mentioned above, a KMP file is basically a ZIP file and
+lexical model data is in JavaScript, this is a combination that looks
+suspicious to many email servers. You can upload it to a Google drive,
+and email a link for downloading the file
+
+## Installing the lexical model package
+
+- The same method for installing custom keyboard packages to mobile
+devices can be used for installing lexical model packages. Refer to
+these pages for [Keyman for iPhone and iPad](../../../distribute/install-kmp-ios) and [Keyman for Android](../../../distribute/install-kmp-android).
\ No newline at end of file
diff --git a/developer/docs/help/guides/lexical-models/index.md b/developer/docs/help/guides/lexical-models/index.md
new file mode 100644
index 00000000000..e913ce7007c
--- /dev/null
+++ b/developer/docs/help/guides/lexical-models/index.md
@@ -0,0 +1,22 @@
+---
+title: Developing lexical models (dictionaries)
+---
+
+A **lexical model** (Keyman apps use the term
+**dictionary**) is what powers **predictive text**
+and **autocorrect** for a language. If you want your keyboard to predict
+and correct words in your language, you must create a lexical model that
+generates suggestions for your language.
+
+[What is a lexical model? Or: how do I add prediction and autocorrection
+to my keyboard?](intro)
+
+[Tutorial: Developing a lexical model from a word list](tutorial)
+
+[Advanced lexical models topics](advanced)
+
+[Distribute a lexical model](distribute)
+
+## Reference
+
+[Wordlist TSV format](../../reference/file-types/tsv)
\ No newline at end of file
diff --git a/developer/docs/help/guides/lexical-models/intro/index.md b/developer/docs/help/guides/lexical-models/intro/index.md
new file mode 100644
index 00000000000..314a571ea52
--- /dev/null
+++ b/developer/docs/help/guides/lexical-models/intro/index.md
@@ -0,0 +1,162 @@
+---
+title: What is a lexical model?
+---
+
+
+
+![Smartphone keyboard displaying suggestions for the English phrase ”on my w-”](../../../images/lm/whatis-on-my-w.png)
+
+##### Predictive text for English.
+
+
+
+Many mobile phone keyboards enjoy **predictive text** for their
+languages.
+
+Predictive text is the feature on your keyboard that displays a series
+of _predictions_, typically above your keyboard,
+that try to guess the word or words that you are typing next. For
+example, if I start typing the English phrase “On my w”, your keyboard’s
+predictive text feature will infer, using its knowledge of the English
+language, that the word you are typing is most likely “way”, followed by
+other, less likely suggestions, such as “whole”, or “website”.
+
+The same feature that provides predictive text can also suggest
+_corrections_ to what you are typing. For
+example, if I start typing “thr” on my English keyboard, my keyboard
+will suggest that I meant to type “the” instead. This **autocorrect**
+feature is powered by your keyboard’s knowledge of the current language.
+
+The way your keyboard knows how to suggest **predictions** and
+**corrections** for your language is through its **lexical model**.
+
+
+
+![diagram of the context ”on my w” used as input to the lexical model; the results of the lexical model are a list of suggestions.](../../../images/lm/overview.svg)
+
+##### How the lexical model is involved in generating suggestions.
+
+
+
+## Why should I create a lexical model for my language?
+
+### Words are difficult to type
+
+
+
+![Typing “n-a-i-v” on a smartphone. The keyboard suggests “naïve” for this input.](../../../images/lm/whatis-naiv.png)
+
+##### Typing “naiv” on a smartphone with predictive text.
+
+
+
+Some words have many accents, diacritics, or similar-looking forms. This
+is not very common in the English language, however, this is quite
+common in other languages. Predictive text can recognize forms without
+the correct diacritics, or recognize forms that are simpler to type than
+the orthographically correct version. This allows the typist to type
+words quickly, with minimum effort.
+
+For example, in English, I want to type “naïve”, however, my keyboard
+does not have the ï key present on its main
+layout. Sure, if I use a keyboard that I can press-and-hold the
+i key, it may pop-up additional characters,
+and I can probably find ï nestled there
+among the other options. However, I have to go out of my way to type the
+correct variant, whereas the incorrect variant will be perfectly
+understood. In most cases, I choose the option that is more economical
+to type–“naive”—rather than the “correct” option.
+
+However, a **lexical model** that understands the English language will
+see the word “naive” does not exist, but a similarly typed variant
+“naïve” does exist. Therefore, when I type “naive”, the lexical model
+will suggest “naïve”, and I can select it and have the correctly spelled
+version without having to long-press and select the correct “ï”. Even
+better than that, I can choose the suggestion right after typing “naiv”,
+as there are relatively view English words that start with the prefix of
+“naiv-”
+
+### Words are long
+
+
+
+![The top suggestion shows “incomprehensible”](../../../images/lm/whatis-incompr.png)
+
+Typing “incompr” on an English keyboard.
+
+
+
+Sometimes, the words are very long, but can be typed in far fewer
+keystrokes if predictive text is used. For example, if in English, I
+want to write the word “incomprehensible” (a 16-letter word), a lexical
+model for English can predict it from the prefix “incompr” and save 8
+keystrokes!
+
+English is not prone to extraordinarily long words; the average word
+length in English is about 9 letters. However, languages that are fond
+of compounding, such as German (e.g.,
+_scheinwerferreinigungsanlage_ is a 28-letter word
+which means “headlight washers”), or _polysynthetic
+languages_ in which one word can have the meaning of a complete
+sentence in English, such as Mohawk (e.g.,
+_sahonwanhotónkwahse_ is a 20-letter word which
+means “she
+opened the door for him again”). For these languages, predictive
+text can save even more keystrokes than in English.
+
+### People make mistakes
+
+
+
+![The first suggestion is to write the word “the” instead.](../../../images/lm/whatis-correct-thr.png)
+
+##### Typing “thr” on an English keyboard.
+
+
+
+When typing quickly on a small phone screen, mistakes are inevitable.
+Say I try to write the word “the” on my phone. I press
+t, then h, but
+as I try to type e, I press a few
+millimeters to the right of the intended key and press
+r instead. With a lexical model, the
+predictive text feature understands that in English, “thr” is not a
+complete word in-and-of-itself; however, a word that is typed quite
+similarly, “the”, is a very common word. Therefore, the lexical model
+provides enough information to assume that the user intended to type
+“the” instead of “thr”. Thus, one of its **suggestions** is the
+**correction** of “the” in place of “thr”.
+
+However, the predictive text feature is not overly presumptuous; what if
+the typist really did want to type “thr”? As a result, “thr” is
+suggested as a _keep suggestion_. When the typist
+selects the “keep” suggestion, whatever they had originally typed is
+kept, even if the lexical model suggests what it thinks is a far more
+likely correction.
+
+## What do I need to make my own lexical model?
+
+To make a lexical model, you need some information about your language.
+At bare minimum, you need a list of words in your language. Keyman
+Developer supports importing a word list as a spreadsheet of words in
+your language that you wish to use for predictions and corrections. This
+will create a _word list lexical model_.
+
+If you have such a list of words, you can continue to the
+[tutorial](../tutorial) to create a word list lexical model for your
+language!
+
+However, to make a more accurate lexical model, you will need an idea of
+how to rank suggestions relative to each other. For this, you may extend
+the simple word list with **counts**. Each word has a count of how often
+it has been seen in a representative collection of texts in your
+language. For example, I could download articles from the English
+language Wikipedia, and count how often Wikipedia contributors have
+typed “rule” versus how many times they have typed “rupturewort”. This
+will help us understand how to rank these two suggestions given the
+context of “ru”.
+
+You can extend your spreadsheet with counts (placed in the second
+column) to make a more accurate—and thus more useful—lexical model.
+
+[Next: Developing a lexical model from a word list](../tutorial)
\ No newline at end of file
diff --git a/developer/docs/help/guides/lexical-models/tutorial/index.md b/developer/docs/help/guides/lexical-models/tutorial/index.md
new file mode 100644
index 00000000000..d6887656b3f
--- /dev/null
+++ b/developer/docs/help/guides/lexical-models/tutorial/index.md
@@ -0,0 +1,29 @@
+---
+title: Tutorial: Developing a lexical model from a word list
+---
+
+[Step 1: Prerequisites for building a lexical model](step-1)
+
+[Step 2: Creating a lexical model project](step-2)
+
+[Step 3: Get some language data](step-3)
+
+[Step 4: Compile the lexical model](step-4)
+
+## Overview
+
+In this tutorial, will learn how to create a **word list lexical
+model**. If you are not sure what a lexical model is, or why you would
+make one, read [“What is a lexical model?”](../intro) first, then come
+back here to follow this tutorial.
+
+## Let's begin
+
+Let's get started! Move on to the next topic to begin the first step.
+
+At the bottom of each page in the tutorial, will be a link to the next
+step. You can use these links to work your way through the tutorial. You
+may also find links to reference information, which you can select to
+learn more about a particular aspect of creating Keyman keyboards.
+
+[Step 1: Prerequisites for building a lexical model](step-1)
\ No newline at end of file
diff --git a/developer/docs/help/guides/lexical-models/tutorial/step-1.md b/developer/docs/help/guides/lexical-models/tutorial/step-1.md
new file mode 100644
index 00000000000..9bd7f84c86f
--- /dev/null
+++ b/developer/docs/help/guides/lexical-models/tutorial/step-1.md
@@ -0,0 +1,22 @@
+---
+title: Step 1: Prerequisites for building a lexical model
+---
+
+To prepare a lexical model based on a word list, we will need a few
+things:
+
+- Install [Keyman Developer](https://keyman.com/developer) 12.0 or greater (Windows only)
+- A list of words in your language, stored in an spreadsheet, in dictionary-making software or, other means.
+
+> ### Note
+Currently, lexical models work better with languages that use regular
+spaces as word boundaries. For complex scripts such as Khmer, Thai, Lao,
+etc, a pre-processing is required before getting it to work, i.e. word
+segmentation has to be determined and/or configured. See the advanced
+topic on making a custom [word breaker](../advanced/word-breaker).
+This will be addressed in a future
+[feature](https://github.com/keymanapp/keyman/issues/5025).
+
+Now to create a lexical model project!
+
+[Step 2: Creating a lexical model project](step-2)
\ No newline at end of file
diff --git a/developer/docs/help/guides/lexical-models/tutorial/step-2.md b/developer/docs/help/guides/lexical-models/tutorial/step-2.md
new file mode 100644
index 00000000000..bdfb063ae46
--- /dev/null
+++ b/developer/docs/help/guides/lexical-models/tutorial/step-2.md
@@ -0,0 +1,115 @@
+---
+title: Step 2: Creating a lexical model project
+---
+
+## Create the new project
+
+Start Keyman Developer. On the “Welcome” screen, click on
+**New Project...**. The “New Project” dialog
+will appear. Select “Wordlist Lexical Model” and press
+**OK**.
+
+
+
+![“New Project” dialog](../../../images/ui/frmNewLMProject.png)
+
+###### The “New Project” dialog, with “Wordlist Lexical Model” selected.
+
+
+
+## Provide required information
+
+
+
+![New LM Project Parameters](../../../images/ui/frmNewLMProjectParameters.png)
+
+###### The New Lexical Model dialog box.
+
+
+
+![Select BCP 47 Tag dialog](../../../images/ui/frmNewLMProjectSelectLanguage.png)
+
+###### The “Select BCP 47 Tag” dialog box for SENĆOŦEN.
+
+
+
+Enter the BCP 47 language tag that you have selected. [Learn more about BCP 47 language tags](../../../reference/bcp-47)
+
+Once you are finished adding the primary language, click
+**OK** to return to the **New Lexical Model
+Project dialog**.
+
+## The Model ID
+
+Keyman will create a **model ID** which is how
+Keyman sorts and organizes different lexical models. If you choose to
+share your model publicly, the model ID is vital for both people and
+Keyman to identify and use your lexical model!
+
+Keyman automatically generates a model ID for you, given all the
+information already filled out. If you're satisfied with the generated
+model ID, you can [skip to the next step](#toc-double-check-the-information).
+
+In this example, my generated model ID is
+`national_research_council_canada.str.sencoten`, derived from my
+organization name, the name of the primary language, and my model name.
+However, I find the “author ID” part of the generated model ID
+excessively long. I changed the author ID to `nrc`, and the model ID
+automatically changes to the much more manageable `nrc.str.sencoten`.
+
+## Double-check the information
+
+Verify that all of the information is correct. Once all of the required
+information has been filled in and verified, click
+**OK** to create the project.
+
+Once we have created the project, we can begin to prepare the data!
+
+[Step 3: Get some language data](step-3)
diff --git a/developer/docs/help/guides/lexical-models/tutorial/step-3.md b/developer/docs/help/guides/lexical-models/tutorial/step-3.md
new file mode 100644
index 00000000000..88d9af90aec
--- /dev/null
+++ b/developer/docs/help/guides/lexical-models/tutorial/step-3.md
@@ -0,0 +1,143 @@
+---
+title: Step 3: Get some language data
+---
+
+To predict words in your language, a lexical model needs to know the
+words in your language!
+
+Keyman Developer understands how to read words in a [TSV file](../../../reference/file-types/tsv). This kind of file can be saved
+from a **spreadsheet** application like [Google Sheets](https://sheets.google.com/) or [Microsoft Excel](https://products.office.com/en/excel). Other users may also use
+**language data management software** like [SIL FieldWorks Language
+Explorer (FLEx)](https://software.sil.org/fieldworks/) to export an
+appropriate **TSV** file.
+
+One simple way to create your TSV file is to use the **PrimerPrep**
+tool:
+
+1. Install PrimerPrep (info at
+ )
+2. Run PrimerPrep (note that on the first run it often takes a couple
+ of minutes; subsequent starts are faster)
+3. Click on the Add Text(s) button; select one or more plain text
+ (UTF-8) files in the language to analyze
+4. The word list with frequency counts appears in the pane to the right
+5. From the File menu, select Save Word List… and specify the file name
+ and location (a .tsv extension is recommended)
+
+> **For advanced users**: Ultimately, what Keyman Developer requires is a
+tab-separated values (TSV) file in a specfic format described in the
+[file types reference](../../../reference/file-types/tsv). Refer to this
+reference file if you are coding your own exporter.
+
+## Example Wordlist
+
+I have words in my language of choice, SENĆOŦEN. Here is my list of
+words, with the count of how many times I’ve seen the word:
+
+List of ten SENĆOŦEN words, with counts
+
+
+| Word | Count |
+|-------|-------|
+| TŦE | 13644 |
+| E | 9134 |
+| SEN | 4816 |
+| Ȼ | 3479 |
+| SW̱ | 2621 |
+| NIȽ | 2314 |
+| U¸ | 2298 |
+| I¸ | 1988 |
+| ȻSE | 1925 |
+| I | 1884 |
+
+
+## Editing the .TSV in Keyman Developer
+
+If you plan to only edit a few entries for your wordlist, you can use
+the TSV editor in Keyman Developer. The project template created a
+wordlist named **wordlist.tsv** that we will now edit.
+
+In Keyman Developer project view, select the Models tab and click on
+wordlist.tsv.
+
+
+
+![Open TSV file in Keyman Developer](../../../images/lm/developer-open-tsv.png)
+
+##### Open a .tsv file in Keyman Developer
+
+
+
+Keyman Developer already generated a few example words when it created
+the template wordlist.tsv file.
+
+
+
+We will replace these entries with SENĆOŦEN words from our wordlist. For
+each row, edit the "Word Form" and "Count". Counts are optional for each
+word: that is, some words may specify counts in the second column, while
+other words may leave the second column blank. To create a new entry at
+the bottom, click "Add word...". When you are finished, you'll have a
+wordlist that looks like this:
+
+
+
+![Editing wordlist.tsv in Keyman Developer](../../../images/lm/edited-tsv.png)
+
+##### Edited wordlist.tsv for SENĆOŦEN
+
+
+
+After saving your wordlist file, you can move on to Step 4.
+
+## Editing the .TSV in Google Sheets
+
+Alternatively, you may want to use a different spreadsheet tool for
+editing large wordlists. I’ve entered this information into my
+spreadsheet of choice, [Google Sheets](https://sheets.google.com/). I’ve
+shared this spreadsheet publicly
+[here](https://docs.google.com/spreadsheets/d/10zhIc439BCSSooL_-HeJ6TUHd-ovkiXYcIGe-pHDTSg/edit?usp=sharing).
+The order of the columns matters:
+
+The first column (column A) **must** be the “words”. If provided, the
+second column (column B) **must** be the “counts”. Counts are optional
+for each word: that is, some words may specify counts in the second
+column, while other words may leave the second column blank. The third
+column (column C) is always ignored. You may use this column as a
+comment. The spreadsheet can be as simple as a single column of all of
+the words in the language, with each word being separated by a line
+break.
+
+This is what my word list looks like in Google Sheets:
+
+
+
+![screenshot of the word list in Google Sheets](../../../images/lm/sencoten-sheets-full.png)
+
+##### The word list, as it appears in Google Sheets
+
+
+
+Now, we download the spreadsheet in the [required format](../../../reference/file-types/tsv). To do this, in Google
+Sheets, select “File” » “Download as” » “Tab-separated values (.tsv,
+current sheet)”.
+
+
+
+![screenshot of “Save as…” menu in Google Sheets, selecting ”TSV”](../../../images/lm/sencoten-sheets-save-as.png)
+
+##### Exporting the TSV file from Google Sheets
+
+
Analyzes a single source file for Unicode character usage. Can parse .kmn, .kvks, .keyman-touch-layout file formats. Can be called multiple times to collect results from more than one file. Use [AnalyzeOskCharacterUse.getStrings()](./kmc-analyze.analyzeoskcharacteruse.getstrings.md) to retrieve results.
Note: analyze() collects key cap data, so calling this for a .kmn file will retrieve the key caps from the .kvks and .keyman-touch-layout files that it references, rather than key cap data from the .kmn file itself.
|
+| [clear()](./kmc-analyze.analyzeoskcharacteruse.clear.md) | | Clears analysis data collected from previous calls to [AnalyzeOskCharacterUse.analyze()](./kmc-analyze.analyzeoskcharacteruse.analyze.md) |
+| [getStrings(format)](./kmc-analyze.analyzeoskcharacteruse.getstrings.md) | |
Returns the collected results from earlier calls to [AnalyzeOskCharacterUse.analyze()](./kmc-analyze.analyzeoskcharacteruse.analyze.md). This generates a mapping from a key cap (one or more characters) to a PUA code, for use with &displayMap.
Three output formats are supported:
- .txt: tab-separated string format, with three columns: PUA, Key Cap, and plain string. The PUA and Key Cap columns are formatted as Unicode Scalar Values, e.g. U+0061, and the plain string is the original key cap string.
- .md: formatted for documentation purposes. Generates a Markdown table (GFM) with PUA, Key Cap, and plain string. The PUA and Key Cap columns are formatted as Unicode Scalar Values, e.g. U+0061, and the plain string is the original key cap string.
- .json: returns the final aggregated data as an array of strings, which can be joined to form a JSON blob of an object with a single member, map, which is an array of objects.
|
+
diff --git a/developer/docs/help/reference/api/kmc-analyze.analyzeoskcharacteruseoptions.includecounts.md b/developer/docs/help/reference/api/kmc-analyze.analyzeoskcharacteruseoptions.includecounts.md
new file mode 100644
index 00000000000..e105b24b29d
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-analyze.analyzeoskcharacteruseoptions.includecounts.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-analyze](./kmc-analyze.md) > [AnalyzeOskCharacterUseOptions](./kmc-analyze.analyzeoskcharacteruseoptions.md) > [includeCounts](./kmc-analyze.analyzeoskcharacteruseoptions.includecounts.md)
+
+## AnalyzeOskCharacterUseOptions.includeCounts property
+
+If true, reports number of references to each character found in each source file
+
+**Signature:**
+
+```typescript
+includeCounts?: boolean;
+```
diff --git a/developer/docs/help/reference/api/kmc-analyze.analyzeoskcharacteruseoptions.md b/developer/docs/help/reference/api/kmc-analyze.analyzeoskcharacteruseoptions.md
new file mode 100644
index 00000000000..cfaa60ffb34
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-analyze.analyzeoskcharacteruseoptions.md
@@ -0,0 +1,22 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-analyze](./kmc-analyze.md) > [AnalyzeOskCharacterUseOptions](./kmc-analyze.analyzeoskcharacteruseoptions.md)
+
+## AnalyzeOskCharacterUseOptions interface
+
+Options for character analysis
+
+**Signature:**
+
+```typescript
+export interface AnalyzeOskCharacterUseOptions
+```
+
+## Properties
+
+| Property | Modifiers | Type | Description |
+| --- | --- | --- | --- |
+| [includeCounts?](./kmc-analyze.analyzeoskcharacteruseoptions.includecounts.md) | | boolean | _(Optional)_ If true, reports number of references to each character found in each source file |
+| [puaBase?](./kmc-analyze.analyzeoskcharacteruseoptions.puabase.md) | | number | _(Optional)_ First character to use in PUA for remapping with &displayMap, defaults to U+F100 |
+| [stripDottedCircle?](./kmc-analyze.analyzeoskcharacteruseoptions.stripdottedcircle.md) | | boolean | _(Optional)_ If true, strips U+25CC from the key cap before further analysis |
+
diff --git a/developer/docs/help/reference/api/kmc-analyze.analyzeoskcharacteruseoptions.puabase.md b/developer/docs/help/reference/api/kmc-analyze.analyzeoskcharacteruseoptions.puabase.md
new file mode 100644
index 00000000000..e1ff19b2184
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-analyze.analyzeoskcharacteruseoptions.puabase.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-analyze](./kmc-analyze.md) > [AnalyzeOskCharacterUseOptions](./kmc-analyze.analyzeoskcharacteruseoptions.md) > [puaBase](./kmc-analyze.analyzeoskcharacteruseoptions.puabase.md)
+
+## AnalyzeOskCharacterUseOptions.puaBase property
+
+First character to use in PUA for remapping with &displayMap, defaults to U+F100
+
+**Signature:**
+
+```typescript
+puaBase?: number;
+```
diff --git a/developer/docs/help/reference/api/kmc-analyze.analyzeoskcharacteruseoptions.stripdottedcircle.md b/developer/docs/help/reference/api/kmc-analyze.analyzeoskcharacteruseoptions.stripdottedcircle.md
new file mode 100644
index 00000000000..db975bf710d
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-analyze.analyzeoskcharacteruseoptions.stripdottedcircle.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-analyze](./kmc-analyze.md) > [AnalyzeOskCharacterUseOptions](./kmc-analyze.analyzeoskcharacteruseoptions.md) > [stripDottedCircle](./kmc-analyze.analyzeoskcharacteruseoptions.stripdottedcircle.md)
+
+## AnalyzeOskCharacterUseOptions.stripDottedCircle property
+
+If true, strips U+25CC from the key cap before further analysis
+
+**Signature:**
+
+```typescript
+stripDottedCircle?: boolean;
+```
diff --git a/developer/docs/help/reference/api/kmc-analyze.analyzeoskrewritepua._constructor_.md b/developer/docs/help/reference/api/kmc-analyze.analyzeoskrewritepua._constructor_.md
new file mode 100644
index 00000000000..a0d5f23e35c
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-analyze.analyzeoskrewritepua._constructor_.md
@@ -0,0 +1,20 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-analyze](./kmc-analyze.md) > [AnalyzeOskRewritePua](./kmc-analyze.analyzeoskrewritepua.md) > [(constructor)](./kmc-analyze.analyzeoskrewritepua._constructor_.md)
+
+## AnalyzeOskRewritePua.(constructor)
+
+Constructs a new instance of the `AnalyzeOskRewritePua` class
+
+**Signature:**
+
+```typescript
+constructor(callbacks: CompilerCallbacks);
+```
+
+## Parameters
+
+| Parameter | Type | Description |
+| --- | --- | --- |
+| callbacks | CompilerCallbacks | |
+
diff --git a/developer/docs/help/reference/api/kmc-analyze.analyzeoskrewritepua.analyze.md b/developer/docs/help/reference/api/kmc-analyze.analyzeoskrewritepua.analyze.md
new file mode 100644
index 00000000000..7cdaa00da60
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-analyze.analyzeoskrewritepua.analyze.md
@@ -0,0 +1,27 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-analyze](./kmc-analyze.md) > [AnalyzeOskRewritePua](./kmc-analyze.analyzeoskrewritepua.md) > [analyze](./kmc-analyze.analyzeoskrewritepua.analyze.md)
+
+## AnalyzeOskRewritePua.analyze() method
+
+Analyze a keyboard file or files, and provide a remapped output. Accepts a .kmn, .kvks, .keyman-touch-layout file formats. For .kmn, will rewrite associated On Screen Keyboard file formats. Can be called multiple times to rewrite multiple files. Use the [AnalyzeOskRewritePua.data](./kmc-analyze.analyzeoskrewritepua.data.md) property to retrieve the output file content for writing. This does not modify the source file.
+
+**Signature:**
+
+```typescript
+analyze(file: string, mapping: Osk.StringResult[]): Promise;
+```
+
+## Parameters
+
+| Parameter | Type | Description |
+| --- | --- | --- |
+| file | string | relative or absolute path to a Keyman source file |
+| mapping | Osk.StringResult\[\] | OSK keycap map provided by [AnalyzeOskCharacterUse](./kmc-analyze.analyzeoskcharacteruse.md) |
+
+**Returns:**
+
+Promise<boolean>
+
+true if the file is successfully loaded and rewritten
+
diff --git a/developer/docs/help/reference/api/kmc-analyze.analyzeoskrewritepua.clear.md b/developer/docs/help/reference/api/kmc-analyze.analyzeoskrewritepua.clear.md
new file mode 100644
index 00000000000..001d0c494b2
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-analyze.analyzeoskrewritepua.clear.md
@@ -0,0 +1,17 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-analyze](./kmc-analyze.md) > [AnalyzeOskRewritePua](./kmc-analyze.analyzeoskrewritepua.md) > [clear](./kmc-analyze.analyzeoskrewritepua.clear.md)
+
+## AnalyzeOskRewritePua.clear() method
+
+Clears data collected from previous calls to [AnalyzeOskRewritePua.analyze()](./kmc-analyze.analyzeoskrewritepua.analyze.md)
+
+**Signature:**
+
+```typescript
+clear(): void;
+```
+**Returns:**
+
+void
+
diff --git a/developer/docs/help/reference/api/kmc-analyze.analyzeoskrewritepua.data.md b/developer/docs/help/reference/api/kmc-analyze.analyzeoskrewritepua.data.md
new file mode 100644
index 00000000000..9e3091ca7cb
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-analyze.analyzeoskrewritepua.data.md
@@ -0,0 +1,15 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-analyze](./kmc-analyze.md) > [AnalyzeOskRewritePua](./kmc-analyze.analyzeoskrewritepua.md) > [data](./kmc-analyze.analyzeoskrewritepua.data.md)
+
+## AnalyzeOskRewritePua.data property
+
+Returns the file data for OSK files rewritten with PUA characters, for use with `&displayMap`.
+
+**Signature:**
+
+```typescript
+get data(): {
+ [index: string]: Uint8Array;
+ };
+```
diff --git a/developer/docs/help/reference/api/kmc-analyze.analyzeoskrewritepua.md b/developer/docs/help/reference/api/kmc-analyze.analyzeoskrewritepua.md
new file mode 100644
index 00000000000..731b90bfb64
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-analyze.analyzeoskrewritepua.md
@@ -0,0 +1,33 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-analyze](./kmc-analyze.md) > [AnalyzeOskRewritePua](./kmc-analyze.analyzeoskrewritepua.md)
+
+## AnalyzeOskRewritePua class
+
+Rewrite On Screen Keyboard files (.kvks, .keyman-touch-layout) with PUA codepoints, based on analysis provided by [AnalyzeOskCharacterUse](./kmc-analyze.analyzeoskcharacteruse.md) class.
+
+**Signature:**
+
+```typescript
+export declare class AnalyzeOskRewritePua
+```
+
+## Constructors
+
+| Constructor | Modifiers | Description |
+| --- | --- | --- |
+| [(constructor)(callbacks)](./kmc-analyze.analyzeoskrewritepua._constructor_.md) | | Constructs a new instance of the AnalyzeOskRewritePua class |
+
+## Properties
+
+| Property | Modifiers | Type | Description |
+| --- | --- | --- | --- |
+| [data](./kmc-analyze.analyzeoskrewritepua.data.md) | readonly | { \[index: string\]: Uint8Array; } | Returns the file data for OSK files rewritten with PUA characters, for use with &displayMap. |
+
+## Methods
+
+| Method | Modifiers | Description |
+| --- | --- | --- |
+| [analyze(file, mapping)](./kmc-analyze.analyzeoskrewritepua.analyze.md) | | Analyze a keyboard file or files, and provide a remapped output. Accepts a .kmn, .kvks, .keyman-touch-layout file formats. For .kmn, will rewrite associated On Screen Keyboard file formats. Can be called multiple times to rewrite multiple files. Use the [AnalyzeOskRewritePua.data](./kmc-analyze.analyzeoskrewritepua.data.md) property to retrieve the output file content for writing. This does not modify the source file. |
+| [clear()](./kmc-analyze.analyzeoskrewritepua.clear.md) | | Clears data collected from previous calls to [AnalyzeOskRewritePua.analyze()](./kmc-analyze.analyzeoskrewritepua.analyze.md) |
+
diff --git a/developer/docs/help/reference/api/kmc-analyze.md b/developer/docs/help/reference/api/kmc-analyze.md
new file mode 100644
index 00000000000..3b514bcf962
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-analyze.md
@@ -0,0 +1,21 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-analyze](./kmc-analyze.md)
+
+## kmc-analyze package
+
+kmc-analyze - keyboard analysis classes, including tools for `&displayMap`.
+
+## Classes
+
+| Class | Description |
+| --- | --- |
+| [AnalyzeOskCharacterUse](./kmc-analyze.analyzeoskcharacteruse.md) | Analyze the characters used in On Screen Keyboard files (.kvks, .keyman-touch-layout) for use with &displayMap. |
+| [AnalyzeOskRewritePua](./kmc-analyze.analyzeoskrewritepua.md) | Rewrite On Screen Keyboard files (.kvks, .keyman-touch-layout) with PUA codepoints, based on analysis provided by [AnalyzeOskCharacterUse](./kmc-analyze.analyzeoskcharacteruse.md) class. |
+
+## Interfaces
+
+| Interface | Description |
+| --- | --- |
+| [AnalyzeOskCharacterUseOptions](./kmc-analyze.analyzeoskcharacteruseoptions.md) | Options for character analysis |
+
diff --git a/developer/docs/help/reference/api/kmc-keyboard-info.keyboardinfocompiler._constructor_.md b/developer/docs/help/reference/api/kmc-keyboard-info.keyboardinfocompiler._constructor_.md
new file mode 100644
index 00000000000..0196e3ca32f
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-keyboard-info.keyboardinfocompiler._constructor_.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-keyboard-info](./kmc-keyboard-info.md) > [KeyboardInfoCompiler](./kmc-keyboard-info.keyboardinfocompiler.md) > [(constructor)](./kmc-keyboard-info.keyboardinfocompiler._constructor_.md)
+
+## KeyboardInfoCompiler.(constructor)
+
+Constructs a new instance of the `KeyboardInfoCompiler` class
+
+**Signature:**
+
+```typescript
+constructor();
+```
diff --git a/developer/docs/help/reference/api/kmc-keyboard-info.keyboardinfocompiler.init.md b/developer/docs/help/reference/api/kmc-keyboard-info.keyboardinfocompiler.init.md
new file mode 100644
index 00000000000..fb9b1460444
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-keyboard-info.keyboardinfocompiler.init.md
@@ -0,0 +1,27 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-keyboard-info](./kmc-keyboard-info.md) > [KeyboardInfoCompiler](./kmc-keyboard-info.keyboardinfocompiler.md) > [init](./kmc-keyboard-info.keyboardinfocompiler.init.md)
+
+## KeyboardInfoCompiler.init() method
+
+Initialize the compiler. Copies options.
+
+**Signature:**
+
+```typescript
+init(callbacks: CompilerCallbacks, options: KeyboardInfoCompilerOptions): Promise;
+```
+
+## Parameters
+
+| Parameter | Type | Description |
+| --- | --- | --- |
+| callbacks | CompilerCallbacks | Callbacks for external interfaces, including message reporting and file io |
+| options | [KeyboardInfoCompilerOptions](./kmc-keyboard-info.keyboardinfocompileroptions.md) | Compiler options |
+
+**Returns:**
+
+Promise<boolean>
+
+false if initialization fails
+
diff --git a/developer/docs/help/reference/api/kmc-keyboard-info.keyboardinfocompiler.md b/developer/docs/help/reference/api/kmc-keyboard-info.keyboardinfocompiler.md
new file mode 100644
index 00000000000..8bca9bd0e32
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-keyboard-info.keyboardinfocompiler.md
@@ -0,0 +1,29 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-keyboard-info](./kmc-keyboard-info.md) > [KeyboardInfoCompiler](./kmc-keyboard-info.keyboardinfocompiler.md)
+
+## KeyboardInfoCompiler class
+
+Compiles source data from a keyboard project to a .keyboard\_info. The compiler does not read or write from filesystem or network directly, but relies on callbacks for all external IO.
+
+**Signature:**
+
+```typescript
+export declare class KeyboardInfoCompiler implements KeymanCompiler
+```
+**Implements:** KeymanCompiler
+
+## Constructors
+
+| Constructor | Modifiers | Description |
+| --- | --- | --- |
+| [(constructor)()](./kmc-keyboard-info.keyboardinfocompiler._constructor_.md) | | Constructs a new instance of the KeyboardInfoCompiler class |
+
+## Methods
+
+| Method | Modifiers | Description |
+| --- | --- | --- |
+| [init(callbacks, options)](./kmc-keyboard-info.keyboardinfocompiler.init.md) | | Initialize the compiler. Copies options. |
+| [run(inputFilename, outputFilename)](./kmc-keyboard-info.keyboardinfocompiler.run.md) | |
Builds a .keyboard\_info file with metadata from the keyboard and package source file. Returns an object containing binary artifacts on success. The files are passed in by name, and the compiler will use callbacks as passed to the [KeyboardInfoCompiler.init()](./kmc-keyboard-info.keyboardinfocompiler.init.md) function to read any input files by disk.
This function is intended for use within the keyboards repository. While many of the parameters could be deduced from each other, they are specified here to reduce the number of places the filenames are constructed. For full documentation, see: https://help.keyman.com/developer/cloud/keyboard\_info/
Write artifacts from a successful compile to disk, via callbacks methods. The artifacts written may include:
- .keyboard\_info file - metadata file used by keyman.com
|
+
diff --git a/developer/docs/help/reference/api/kmc-keyboard-info.keyboardinfocompiler.run.md b/developer/docs/help/reference/api/kmc-keyboard-info.keyboardinfocompiler.run.md
new file mode 100644
index 00000000000..711d8948648
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-keyboard-info.keyboardinfocompiler.run.md
@@ -0,0 +1,29 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-keyboard-info](./kmc-keyboard-info.md) > [KeyboardInfoCompiler](./kmc-keyboard-info.keyboardinfocompiler.md) > [run](./kmc-keyboard-info.keyboardinfocompiler.run.md)
+
+## KeyboardInfoCompiler.run() method
+
+Builds a .keyboard\_info file with metadata from the keyboard and package source file. Returns an object containing binary artifacts on success. The files are passed in by name, and the compiler will use callbacks as passed to the [KeyboardInfoCompiler.init()](./kmc-keyboard-info.keyboardinfocompiler.init.md) function to read any input files by disk.
+
+This function is intended for use within the keyboards repository. While many of the parameters could be deduced from each other, they are specified here to reduce the number of places the filenames are constructed. For full documentation, see: https://help.keyman.com/developer/cloud/keyboard\_info/
+
+**Signature:**
+
+```typescript
+run(inputFilename: string, outputFilename?: string): Promise;
+```
+
+## Parameters
+
+| Parameter | Type | Description |
+| --- | --- | --- |
+| inputFilename | string | |
+| outputFilename | string | _(Optional)_ |
+
+**Returns:**
+
+Promise<[KeyboardInfoCompilerResult](./kmc-keyboard-info.keyboardinfocompilerresult.md)>
+
+Binary artifacts on success, null on failure.
+
diff --git a/developer/docs/help/reference/api/kmc-keyboard-info.keyboardinfocompiler.write.md b/developer/docs/help/reference/api/kmc-keyboard-info.keyboardinfocompiler.write.md
new file mode 100644
index 00000000000..9ac5bf1b336
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-keyboard-info.keyboardinfocompiler.write.md
@@ -0,0 +1,28 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-keyboard-info](./kmc-keyboard-info.md) > [KeyboardInfoCompiler](./kmc-keyboard-info.keyboardinfocompiler.md) > [write](./kmc-keyboard-info.keyboardinfocompiler.write.md)
+
+## KeyboardInfoCompiler.write() method
+
+Write artifacts from a successful compile to disk, via callbacks methods. The artifacts written may include:
+
+- .keyboard\_info file - metadata file used by keyman.com
+
+**Signature:**
+
+```typescript
+write(artifacts: KeyboardInfoCompilerArtifacts): Promise;
+```
+
+## Parameters
+
+| Parameter | Type | Description |
+| --- | --- | --- |
+| artifacts | [KeyboardInfoCompilerArtifacts](./kmc-keyboard-info.keyboardinfocompilerartifacts.md) | object containing artifact binary data to write out |
+
+**Returns:**
+
+Promise<boolean>
+
+true on success
+
diff --git a/developer/docs/help/reference/api/kmc-keyboard-info.keyboardinfocompilerartifacts.keyboard_info.md b/developer/docs/help/reference/api/kmc-keyboard-info.keyboardinfocompilerartifacts.keyboard_info.md
new file mode 100644
index 00000000000..e3b46d10e55
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-keyboard-info.keyboardinfocompilerartifacts.keyboard_info.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-keyboard-info](./kmc-keyboard-info.md) > [KeyboardInfoCompilerArtifacts](./kmc-keyboard-info.keyboardinfocompilerartifacts.md) > [keyboard\_info](./kmc-keyboard-info.keyboardinfocompilerartifacts.keyboard_info.md)
+
+## KeyboardInfoCompilerArtifacts.keyboard\_info property
+
+Binary keyboard info filedata and filename - used by keyman.com
+
+**Signature:**
+
+```typescript
+keyboard_info: KeymanCompilerArtifact;
+```
diff --git a/developer/docs/help/reference/api/kmc-keyboard-info.keyboardinfocompilerartifacts.md b/developer/docs/help/reference/api/kmc-keyboard-info.keyboardinfocompilerartifacts.md
new file mode 100644
index 00000000000..fcfa258df4b
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-keyboard-info.keyboardinfocompilerartifacts.md
@@ -0,0 +1,21 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-keyboard-info](./kmc-keyboard-info.md) > [KeyboardInfoCompilerArtifacts](./kmc-keyboard-info.keyboardinfocompilerartifacts.md)
+
+## KeyboardInfoCompilerArtifacts interface
+
+Internal in-memory build artifacts from a successful compilation
+
+**Signature:**
+
+```typescript
+export interface KeyboardInfoCompilerArtifacts extends KeymanCompilerArtifacts
+```
+**Extends:** KeymanCompilerArtifacts
+
+## Properties
+
+| Property | Modifiers | Type | Description |
+| --- | --- | --- | --- |
+| [keyboard\_info](./kmc-keyboard-info.keyboardinfocompilerartifacts.keyboard_info.md) | | KeymanCompilerArtifact | Binary keyboard info filedata and filename - used by keyman.com |
+
diff --git a/developer/docs/help/reference/api/kmc-keyboard-info.keyboardinfocompileroptions.md b/developer/docs/help/reference/api/kmc-keyboard-info.keyboardinfocompileroptions.md
new file mode 100644
index 00000000000..63f1279300e
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-keyboard-info.keyboardinfocompileroptions.md
@@ -0,0 +1,21 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-keyboard-info](./kmc-keyboard-info.md) > [KeyboardInfoCompilerOptions](./kmc-keyboard-info.keyboardinfocompileroptions.md)
+
+## KeyboardInfoCompilerOptions interface
+
+Options for the .keyboard\_info compiler
+
+**Signature:**
+
+```typescript
+export interface KeyboardInfoCompilerOptions extends CompilerOptions
+```
+**Extends:** CompilerOptions
+
+## Properties
+
+| Property | Modifiers | Type | Description |
+| --- | --- | --- | --- |
+| [sources](./kmc-keyboard-info.keyboardinfocompileroptions.sources.md) | | [KeyboardInfoSources](./kmc-keyboard-info.keyboardinfosources.md) | Description of sources and metadata required to build a .keyboard\_info file |
+
diff --git a/developer/docs/help/reference/api/kmc-keyboard-info.keyboardinfocompileroptions.sources.md b/developer/docs/help/reference/api/kmc-keyboard-info.keyboardinfocompileroptions.sources.md
new file mode 100644
index 00000000000..371f1734776
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-keyboard-info.keyboardinfocompileroptions.sources.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-keyboard-info](./kmc-keyboard-info.md) > [KeyboardInfoCompilerOptions](./kmc-keyboard-info.keyboardinfocompileroptions.md) > [sources](./kmc-keyboard-info.keyboardinfocompileroptions.sources.md)
+
+## KeyboardInfoCompilerOptions.sources property
+
+Description of sources and metadata required to build a .keyboard\_info file
+
+**Signature:**
+
+```typescript
+sources: KeyboardInfoSources;
+```
diff --git a/developer/docs/help/reference/api/kmc-keyboard-info.keyboardinfocompilerresult.artifacts.md b/developer/docs/help/reference/api/kmc-keyboard-info.keyboardinfocompilerresult.artifacts.md
new file mode 100644
index 00000000000..0554d415c53
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-keyboard-info.keyboardinfocompilerresult.artifacts.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-keyboard-info](./kmc-keyboard-info.md) > [KeyboardInfoCompilerResult](./kmc-keyboard-info.keyboardinfocompilerresult.md) > [artifacts](./kmc-keyboard-info.keyboardinfocompilerresult.artifacts.md)
+
+## KeyboardInfoCompilerResult.artifacts property
+
+Internal in-memory build artifacts from a successful compilation. Caller can write these to disk with [KeyboardInfoCompiler.write()](./kmc-keyboard-info.keyboardinfocompiler.write.md)
+
+**Signature:**
+
+```typescript
+artifacts: KeyboardInfoCompilerArtifacts;
+```
diff --git a/developer/docs/help/reference/api/kmc-keyboard-info.keyboardinfocompilerresult.md b/developer/docs/help/reference/api/kmc-keyboard-info.keyboardinfocompilerresult.md
new file mode 100644
index 00000000000..96de0472e3b
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-keyboard-info.keyboardinfocompilerresult.md
@@ -0,0 +1,21 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-keyboard-info](./kmc-keyboard-info.md) > [KeyboardInfoCompilerResult](./kmc-keyboard-info.keyboardinfocompilerresult.md)
+
+## KeyboardInfoCompilerResult interface
+
+Build artifacts from the .keyboard\_info compiler
+
+**Signature:**
+
+```typescript
+export interface KeyboardInfoCompilerResult extends KeymanCompilerResult
+```
+**Extends:** KeymanCompilerResult
+
+## Properties
+
+| Property | Modifiers | Type | Description |
+| --- | --- | --- | --- |
+| [artifacts](./kmc-keyboard-info.keyboardinfocompilerresult.artifacts.md) | | [KeyboardInfoCompilerArtifacts](./kmc-keyboard-info.keyboardinfocompilerartifacts.md) | Internal in-memory build artifacts from a successful compilation. Caller can write these to disk with [KeyboardInfoCompiler.write()](./kmc-keyboard-info.keyboardinfocompiler.write.md) |
+
diff --git a/developer/docs/help/reference/api/kmc-keyboard-info.keyboardinfosources.forpublishing.md b/developer/docs/help/reference/api/kmc-keyboard-info.keyboardinfosources.forpublishing.md
new file mode 100644
index 00000000000..5eb9fe6d6f9
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-keyboard-info.keyboardinfosources.forpublishing.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-keyboard-info](./kmc-keyboard-info.md) > [KeyboardInfoSources](./kmc-keyboard-info.keyboardinfosources.md) > [forPublishing](./kmc-keyboard-info.keyboardinfosources.forpublishing.md)
+
+## KeyboardInfoSources.forPublishing property
+
+Return an error if project does not meet requirements of keyboards repository
+
+**Signature:**
+
+```typescript
+forPublishing: boolean;
+```
diff --git a/developer/docs/help/reference/api/kmc-keyboard-info.keyboardinfosources.jsfilename.md b/developer/docs/help/reference/api/kmc-keyboard-info.keyboardinfosources.jsfilename.md
new file mode 100644
index 00000000000..15e0557724b
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-keyboard-info.keyboardinfosources.jsfilename.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-keyboard-info](./kmc-keyboard-info.md) > [KeyboardInfoSources](./kmc-keyboard-info.keyboardinfosources.md) > [jsFilename](./kmc-keyboard-info.keyboardinfosources.jsfilename.md)
+
+## KeyboardInfoSources.jsFilename property
+
+The compiled keyboard filename and relative path (.js only)
+
+**Signature:**
+
+```typescript
+jsFilename?: string;
+```
diff --git a/developer/docs/help/reference/api/kmc-keyboard-info.keyboardinfosources.kmpfilename.md b/developer/docs/help/reference/api/kmc-keyboard-info.keyboardinfosources.kmpfilename.md
new file mode 100644
index 00000000000..53689423218
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-keyboard-info.keyboardinfosources.kmpfilename.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-keyboard-info](./kmc-keyboard-info.md) > [KeyboardInfoSources](./kmc-keyboard-info.keyboardinfosources.md) > [kmpFilename](./kmc-keyboard-info.keyboardinfosources.kmpfilename.md)
+
+## KeyboardInfoSources.kmpFilename property
+
+The compiled package filename and relative path (.kmp)
+
+**Signature:**
+
+```typescript
+kmpFilename: string;
+```
diff --git a/developer/docs/help/reference/api/kmc-keyboard-info.keyboardinfosources.kpsfilename.md b/developer/docs/help/reference/api/kmc-keyboard-info.keyboardinfosources.kpsfilename.md
new file mode 100644
index 00000000000..577820f1454
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-keyboard-info.keyboardinfosources.kpsfilename.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-keyboard-info](./kmc-keyboard-info.md) > [KeyboardInfoSources](./kmc-keyboard-info.keyboardinfosources.md) > [kpsFilename](./kmc-keyboard-info.keyboardinfosources.kpsfilename.md)
+
+## KeyboardInfoSources.kpsFilename property
+
+The source package filename and relative path (.kps)
+
+**Signature:**
+
+```typescript
+kpsFilename: string;
+```
diff --git a/developer/docs/help/reference/api/kmc-keyboard-info.keyboardinfosources.lastcommitdate.md b/developer/docs/help/reference/api/kmc-keyboard-info.keyboardinfosources.lastcommitdate.md
new file mode 100644
index 00000000000..398a0ab9552
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-keyboard-info.keyboardinfosources.lastcommitdate.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-keyboard-info](./kmc-keyboard-info.md) > [KeyboardInfoSources](./kmc-keyboard-info.keyboardinfosources.md) > [lastCommitDate](./kmc-keyboard-info.keyboardinfosources.lastcommitdate.md)
+
+## KeyboardInfoSources.lastCommitDate property
+
+Last modification date for files in the project folder 'YYYY-MM-DDThh:mm:ssZ'
+
+**Signature:**
+
+```typescript
+lastCommitDate?: string;
+```
diff --git a/developer/docs/help/reference/api/kmc-keyboard-info.keyboardinfosources.md b/developer/docs/help/reference/api/kmc-keyboard-info.keyboardinfosources.md
new file mode 100644
index 00000000000..5a6caf26d68
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-keyboard-info.keyboardinfosources.md
@@ -0,0 +1,25 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-keyboard-info](./kmc-keyboard-info.md) > [KeyboardInfoSources](./kmc-keyboard-info.keyboardinfosources.md)
+
+## KeyboardInfoSources interface
+
+Description of sources and metadata required to build a .keyboard\_info file
+
+**Signature:**
+
+```typescript
+export interface KeyboardInfoSources
+```
+
+## Properties
+
+| Property | Modifiers | Type | Description |
+| --- | --- | --- | --- |
+| [forPublishing](./kmc-keyboard-info.keyboardinfosources.forpublishing.md) | | boolean | Return an error if project does not meet requirements of keyboards repository |
+| [jsFilename?](./kmc-keyboard-info.keyboardinfosources.jsfilename.md) | | string | _(Optional)_ The compiled keyboard filename and relative path (.js only) |
+| [kmpFilename](./kmc-keyboard-info.keyboardinfosources.kmpfilename.md) | | string | The compiled package filename and relative path (.kmp) |
+| [kpsFilename](./kmc-keyboard-info.keyboardinfosources.kpsfilename.md) | | string | The source package filename and relative path (.kps) |
+| [lastCommitDate?](./kmc-keyboard-info.keyboardinfosources.lastcommitdate.md) | | string | _(Optional)_ Last modification date for files in the project folder 'YYYY-MM-DDThh:mm:ssZ' |
+| [sourcePath](./kmc-keyboard-info.keyboardinfosources.sourcepath.md) | | string | The path in the keymanapp/keyboards repo where this keyboard may be found |
+
diff --git a/developer/docs/help/reference/api/kmc-keyboard-info.keyboardinfosources.sourcepath.md b/developer/docs/help/reference/api/kmc-keyboard-info.keyboardinfosources.sourcepath.md
new file mode 100644
index 00000000000..197a5d123be
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-keyboard-info.keyboardinfosources.sourcepath.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-keyboard-info](./kmc-keyboard-info.md) > [KeyboardInfoSources](./kmc-keyboard-info.keyboardinfosources.md) > [sourcePath](./kmc-keyboard-info.keyboardinfosources.sourcepath.md)
+
+## KeyboardInfoSources.sourcePath property
+
+The path in the keymanapp/keyboards repo where this keyboard may be found
+
+**Signature:**
+
+```typescript
+sourcePath: string;
+```
diff --git a/developer/docs/help/reference/api/kmc-keyboard-info.md b/developer/docs/help/reference/api/kmc-keyboard-info.md
new file mode 100644
index 00000000000..403add9e832
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-keyboard-info.md
@@ -0,0 +1,21 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-keyboard-info](./kmc-keyboard-info.md)
+
+## kmc-keyboard-info package
+
+## Classes
+
+| Class | Description |
+| --- | --- |
+| [KeyboardInfoCompiler](./kmc-keyboard-info.keyboardinfocompiler.md) | Compiles source data from a keyboard project to a .keyboard\_info. The compiler does not read or write from filesystem or network directly, but relies on callbacks for all external IO. |
+
+## Interfaces
+
+| Interface | Description |
+| --- | --- |
+| [KeyboardInfoCompilerArtifacts](./kmc-keyboard-info.keyboardinfocompilerartifacts.md) | Internal in-memory build artifacts from a successful compilation |
+| [KeyboardInfoCompilerOptions](./kmc-keyboard-info.keyboardinfocompileroptions.md) | Options for the .keyboard\_info compiler |
+| [KeyboardInfoCompilerResult](./kmc-keyboard-info.keyboardinfocompilerresult.md) | Build artifacts from the .keyboard\_info compiler |
+| [KeyboardInfoSources](./kmc-keyboard-info.keyboardinfosources.md) | Description of sources and metadata required to build a .keyboard\_info file |
+
diff --git a/developer/docs/help/reference/api/kmc-kmn.kmncompiler._constructor_.md b/developer/docs/help/reference/api/kmc-kmn.kmncompiler._constructor_.md
new file mode 100644
index 00000000000..42a1cd666cb
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-kmn.kmncompiler._constructor_.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-kmn](./kmc-kmn.md) > [KmnCompiler](./kmc-kmn.kmncompiler.md) > [(constructor)](./kmc-kmn.kmncompiler._constructor_.md)
+
+## KmnCompiler.(constructor)
+
+Constructs a new instance of the `KmnCompiler` class
+
+**Signature:**
+
+```typescript
+constructor();
+```
diff --git a/developer/docs/help/reference/api/kmc-kmn.kmncompiler.init.md b/developer/docs/help/reference/api/kmc-kmn.kmncompiler.init.md
new file mode 100644
index 00000000000..5d2698b5443
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-kmn.kmncompiler.init.md
@@ -0,0 +1,27 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-kmn](./kmc-kmn.md) > [KmnCompiler](./kmc-kmn.kmncompiler.md) > [init](./kmc-kmn.kmncompiler.init.md)
+
+## KmnCompiler.init() method
+
+Initialize the compiler, including loading the WASM host for kmcmplib. Copies options.
+
+**Signature:**
+
+```typescript
+init(callbacks: CompilerCallbacks, options: KmnCompilerOptions): Promise;
+```
+
+## Parameters
+
+| Parameter | Type | Description |
+| --- | --- | --- |
+| callbacks | CompilerCallbacks | Callbacks for external interfaces, including message reporting and file io |
+| options | [KmnCompilerOptions](./kmc-kmn.kmncompileroptions.md) | Compiler options |
+
+**Returns:**
+
+Promise<boolean>
+
+false if initialization fails
+
diff --git a/developer/docs/help/reference/api/kmc-kmn.kmncompiler.md b/developer/docs/help/reference/api/kmc-kmn.kmncompiler.md
new file mode 100644
index 00000000000..d41c05fc2e3
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-kmn.kmncompiler.md
@@ -0,0 +1,30 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-kmn](./kmc-kmn.md) > [KmnCompiler](./kmc-kmn.kmncompiler.md)
+
+## KmnCompiler class
+
+Compiles a .kmn file to a .kmx, .kvk, and/or .js. The compiler does not read or write from filesystem or network directly, but relies on callbacks for all external IO.
+
+**Signature:**
+
+```typescript
+export declare class KmnCompiler implements KeymanCompiler, UnicodeSetParser
+```
+**Implements:** KeymanCompiler, UnicodeSetParser
+
+## Constructors
+
+| Constructor | Modifiers | Description |
+| --- | --- | --- |
+| [(constructor)()](./kmc-kmn.kmncompiler._constructor_.md) | | Constructs a new instance of the KmnCompiler class |
+
+## Methods
+
+| Method | Modifiers | Description |
+| --- | --- | --- |
+| [init(callbacks, options)](./kmc-kmn.kmncompiler.init.md) | | Initialize the compiler, including loading the WASM host for kmcmplib. Copies options. |
+| [run(infile, outfile)](./kmc-kmn.kmncompiler.run.md) | | Compiles a .kmn file to .kmx, .kvk, and/or .js files. Returns an object containing binary artifacts on success. The files are passed in by name, and the compiler will use callbacks as passed to the [KmnCompiler.init()](./kmc-kmn.kmncompiler.init.md) function to read any input files by disk. |
+| [verifyInitialized()](./kmc-kmn.kmncompiler.verifyinitialized.md) | | Verify that wasm is spun up OK. |
+| [write(artifacts)](./kmc-kmn.kmncompiler.write.md) | |
Write artifacts from a successful compile to disk, via callbacks methods. The artifacts written may include:
- .kmx file - binary keyboard used by Keyman on desktop platforms - .kvk file - binary on screen keyboard used by Keyman on desktop platforms - .js file - Javascript keyboard for web and touch platforms
|
+
diff --git a/developer/docs/help/reference/api/kmc-kmn.kmncompiler.run.md b/developer/docs/help/reference/api/kmc-kmn.kmncompiler.run.md
new file mode 100644
index 00000000000..a97f40ab962
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-kmn.kmncompiler.run.md
@@ -0,0 +1,27 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-kmn](./kmc-kmn.md) > [KmnCompiler](./kmc-kmn.kmncompiler.md) > [run](./kmc-kmn.kmncompiler.run.md)
+
+## KmnCompiler.run() method
+
+Compiles a .kmn file to .kmx, .kvk, and/or .js files. Returns an object containing binary artifacts on success. The files are passed in by name, and the compiler will use callbacks as passed to the [KmnCompiler.init()](./kmc-kmn.kmncompiler.init.md) function to read any input files by disk.
+
+**Signature:**
+
+```typescript
+run(infile: string, outfile: string): Promise;
+```
+
+## Parameters
+
+| Parameter | Type | Description |
+| --- | --- | --- |
+| infile | string | Path to source file. Path will be parsed to find relative references in the .kmn file, such as icon or On Screen Keyboard file |
+| outfile | string | Path to output file. The file will not be written to, but will be included in the result for use by [KmnCompiler.write()](./kmc-kmn.kmncompiler.write.md). |
+
+**Returns:**
+
+Promise<[KmnCompilerResult](./kmc-kmn.kmncompilerresult.md)>
+
+Binary artifacts on success, null on failure.
+
diff --git a/developer/docs/help/reference/api/kmc-kmn.kmncompiler.verifyinitialized.md b/developer/docs/help/reference/api/kmc-kmn.kmncompiler.verifyinitialized.md
new file mode 100644
index 00000000000..f2d38795172
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-kmn.kmncompiler.verifyinitialized.md
@@ -0,0 +1,19 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-kmn](./kmc-kmn.md) > [KmnCompiler](./kmc-kmn.kmncompiler.md) > [verifyInitialized](./kmc-kmn.kmncompiler.verifyinitialized.md)
+
+## KmnCompiler.verifyInitialized() method
+
+Verify that wasm is spun up OK.
+
+**Signature:**
+
+```typescript
+verifyInitialized(): boolean;
+```
+**Returns:**
+
+boolean
+
+true if OK
+
diff --git a/developer/docs/help/reference/api/kmc-kmn.kmncompiler.write.md b/developer/docs/help/reference/api/kmc-kmn.kmncompiler.write.md
new file mode 100644
index 00000000000..ebc1b8d8437
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-kmn.kmncompiler.write.md
@@ -0,0 +1,28 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-kmn](./kmc-kmn.md) > [KmnCompiler](./kmc-kmn.kmncompiler.md) > [write](./kmc-kmn.kmncompiler.write.md)
+
+## KmnCompiler.write() method
+
+Write artifacts from a successful compile to disk, via callbacks methods. The artifacts written may include:
+
+- .kmx file - binary keyboard used by Keyman on desktop platforms - .kvk file - binary on screen keyboard used by Keyman on desktop platforms - .js file - Javascript keyboard for web and touch platforms
+
+**Signature:**
+
+```typescript
+write(artifacts: KmnCompilerArtifacts): Promise;
+```
+
+## Parameters
+
+| Parameter | Type | Description |
+| --- | --- | --- |
+| artifacts | [KmnCompilerArtifacts](./kmc-kmn.kmncompilerartifacts.md) | object containing artifact binary data to write out |
+
+**Returns:**
+
+Promise<boolean>
+
+true on success
+
diff --git a/developer/docs/help/reference/api/kmc-kmn.kmncompilerartifacts.js.md b/developer/docs/help/reference/api/kmc-kmn.kmncompilerartifacts.js.md
new file mode 100644
index 00000000000..12706601f1e
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-kmn.kmncompilerartifacts.js.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-kmn](./kmc-kmn.md) > [KmnCompilerArtifacts](./kmc-kmn.kmncompilerartifacts.md) > [js](./kmc-kmn.kmncompilerartifacts.js.md)
+
+## KmnCompilerArtifacts.js property
+
+Javascript keyboard filedata and filename - installable into KeymanWeb, Keyman mobile products
+
+**Signature:**
+
+```typescript
+js?: KeymanCompilerArtifactOptional;
+```
diff --git a/developer/docs/help/reference/api/kmc-kmn.kmncompilerartifacts.kmx.md b/developer/docs/help/reference/api/kmc-kmn.kmncompilerartifacts.kmx.md
new file mode 100644
index 00000000000..c850b6d036b
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-kmn.kmncompilerartifacts.kmx.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-kmn](./kmc-kmn.md) > [KmnCompilerArtifacts](./kmc-kmn.kmncompilerartifacts.md) > [kmx](./kmc-kmn.kmncompilerartifacts.kmx.md)
+
+## KmnCompilerArtifacts.kmx property
+
+Binary keyboard filedata and filename - installable into Keyman desktop projects
+
+**Signature:**
+
+```typescript
+kmx?: KeymanCompilerArtifactOptional;
+```
diff --git a/developer/docs/help/reference/api/kmc-kmn.kmncompilerartifacts.kvk.md b/developer/docs/help/reference/api/kmc-kmn.kmncompilerartifacts.kvk.md
new file mode 100644
index 00000000000..4a6bb87d65a
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-kmn.kmncompilerartifacts.kvk.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-kmn](./kmc-kmn.md) > [KmnCompilerArtifacts](./kmc-kmn.kmncompilerartifacts.md) > [kvk](./kmc-kmn.kmncompilerartifacts.kvk.md)
+
+## KmnCompilerArtifacts.kvk property
+
+Binary on screen keyboard filedata and filename - installable into Keyman desktop projects alongside .kmx
+
+**Signature:**
+
+```typescript
+kvk?: KeymanCompilerArtifactOptional;
+```
diff --git a/developer/docs/help/reference/api/kmc-kmn.kmncompilerartifacts.md b/developer/docs/help/reference/api/kmc-kmn.kmncompilerartifacts.md
new file mode 100644
index 00000000000..37561d34a02
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-kmn.kmncompilerartifacts.md
@@ -0,0 +1,23 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-kmn](./kmc-kmn.md) > [KmnCompilerArtifacts](./kmc-kmn.kmncompilerartifacts.md)
+
+## KmnCompilerArtifacts interface
+
+Internal in-memory build artifacts from a successful compilation
+
+**Signature:**
+
+```typescript
+export interface KmnCompilerArtifacts extends KeymanCompilerArtifacts
+```
+**Extends:** KeymanCompilerArtifacts
+
+## Properties
+
+| Property | Modifiers | Type | Description |
+| --- | --- | --- | --- |
+| [js?](./kmc-kmn.kmncompilerartifacts.js.md) | | KeymanCompilerArtifactOptional | _(Optional)_ Javascript keyboard filedata and filename - installable into KeymanWeb, Keyman mobile products |
+| [kmx?](./kmc-kmn.kmncompilerartifacts.kmx.md) | | KeymanCompilerArtifactOptional | _(Optional)_ Binary keyboard filedata and filename - installable into Keyman desktop projects |
+| [kvk?](./kmc-kmn.kmncompilerartifacts.kvk.md) | | KeymanCompilerArtifactOptional | _(Optional)_ Binary on screen keyboard filedata and filename - installable into Keyman desktop projects alongside .kmx |
+
diff --git a/developer/docs/help/reference/api/kmc-kmn.kmncompileroptions.md b/developer/docs/help/reference/api/kmc-kmn.kmncompileroptions.md
new file mode 100644
index 00000000000..1591a9313b6
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-kmn.kmncompileroptions.md
@@ -0,0 +1,15 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-kmn](./kmc-kmn.md) > [KmnCompilerOptions](./kmc-kmn.kmncompileroptions.md)
+
+## KmnCompilerOptions interface
+
+Options for the .kmn compiler
+
+**Signature:**
+
+```typescript
+export interface KmnCompilerOptions extends CompilerOptions
+```
+**Extends:** CompilerOptions
+
diff --git a/developer/docs/help/reference/api/kmc-kmn.kmncompilerresult.artifacts.md b/developer/docs/help/reference/api/kmc-kmn.kmncompilerresult.artifacts.md
new file mode 100644
index 00000000000..12a4dac8596
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-kmn.kmncompilerresult.artifacts.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-kmn](./kmc-kmn.md) > [KmnCompilerResult](./kmc-kmn.kmncompilerresult.md) > [artifacts](./kmc-kmn.kmncompilerresult.artifacts.md)
+
+## KmnCompilerResult.artifacts property
+
+Internal in-memory build artifacts from a successful compilation. Caller can write these to disk with [KmnCompiler.write()](./kmc-kmn.kmncompiler.write.md)
+
+**Signature:**
+
+```typescript
+artifacts: KmnCompilerArtifacts;
+```
diff --git a/developer/docs/help/reference/api/kmc-kmn.kmncompilerresult.displaymap.md b/developer/docs/help/reference/api/kmc-kmn.kmncompilerresult.displaymap.md
new file mode 100644
index 00000000000..cdf6cbe9a4f
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-kmn.kmncompilerresult.displaymap.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-kmn](./kmc-kmn.md) > [KmnCompilerResult](./kmc-kmn.kmncompilerresult.md) > [displayMap](./kmc-kmn.kmncompilerresult.displaymap.md)
+
+## KmnCompilerResult.displayMap property
+
+Mapping data for `&displayMap`, intended for use by kmc-analyze
+
+**Signature:**
+
+```typescript
+displayMap?: Osk.PuaMap;
+```
diff --git a/developer/docs/help/reference/api/kmc-kmn.kmncompilerresult.extra.md b/developer/docs/help/reference/api/kmc-kmn.kmncompilerresult.extra.md
new file mode 100644
index 00000000000..9263bb78037
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-kmn.kmncompilerresult.extra.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-kmn](./kmc-kmn.md) > [KmnCompilerResult](./kmc-kmn.kmncompilerresult.md) > [extra](./kmc-kmn.kmncompilerresult.extra.md)
+
+## KmnCompilerResult.extra property
+
+Internal additional metadata used by secondary compile phases such as KmwCompiler, not intended for external use
+
+**Signature:**
+
+```typescript
+extra: KmnCompilerResultExtra;
+```
diff --git a/developer/docs/help/reference/api/kmc-kmn.kmncompilerresult.md b/developer/docs/help/reference/api/kmc-kmn.kmncompilerresult.md
new file mode 100644
index 00000000000..b50a6025b41
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-kmn.kmncompilerresult.md
@@ -0,0 +1,23 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-kmn](./kmc-kmn.md) > [KmnCompilerResult](./kmc-kmn.kmncompilerresult.md)
+
+## KmnCompilerResult interface
+
+Build artifacts from the .kmn compiler
+
+**Signature:**
+
+```typescript
+export interface KmnCompilerResult extends KeymanCompilerResult
+```
+**Extends:** KeymanCompilerResult
+
+## Properties
+
+| Property | Modifiers | Type | Description |
+| --- | --- | --- | --- |
+| [artifacts](./kmc-kmn.kmncompilerresult.artifacts.md) | | [KmnCompilerArtifacts](./kmc-kmn.kmncompilerartifacts.md) | Internal in-memory build artifacts from a successful compilation. Caller can write these to disk with [KmnCompiler.write()](./kmc-kmn.kmncompiler.write.md) |
+| [displayMap?](./kmc-kmn.kmncompilerresult.displaymap.md) | | [Osk.PuaMap](./kmc-kmn.osk.puamap.md) | _(Optional)_ Mapping data for &displayMap, intended for use by kmc-analyze |
+| [extra](./kmc-kmn.kmncompilerresult.extra.md) | | KmnCompilerResultExtra | Internal additional metadata used by secondary compile phases such as KmwCompiler, not intended for external use |
+
diff --git a/developer/docs/help/reference/api/kmc-kmn.md b/developer/docs/help/reference/api/kmc-kmn.md
new file mode 100644
index 00000000000..253dbbac62b
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-kmn.md
@@ -0,0 +1,28 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-kmn](./kmc-kmn.md)
+
+## kmc-kmn package
+
+kmc-kmn - Keyman keyboard compiler
+
+## Classes
+
+| Class | Description |
+| --- | --- |
+| [KmnCompiler](./kmc-kmn.kmncompiler.md) | Compiles a .kmn file to a .kmx, .kvk, and/or .js. The compiler does not read or write from filesystem or network directly, but relies on callbacks for all external IO. |
+
+## Interfaces
+
+| Interface | Description |
+| --- | --- |
+| [KmnCompilerArtifacts](./kmc-kmn.kmncompilerartifacts.md) | Internal in-memory build artifacts from a successful compilation |
+| [KmnCompilerOptions](./kmc-kmn.kmncompileroptions.md) | Options for the .kmn compiler |
+| [KmnCompilerResult](./kmc-kmn.kmncompilerresult.md) | Build artifacts from the .kmn compiler |
+
+## Namespaces
+
+| Namespace | Description |
+| --- | --- |
+| [Osk](./kmc-kmn.osk.md) | |
+
diff --git a/developer/docs/help/reference/api/kmc-kmn.osk.md b/developer/docs/help/reference/api/kmc-kmn.osk.md
new file mode 100644
index 00000000000..7257d523fec
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-kmn.osk.md
@@ -0,0 +1,28 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-kmn](./kmc-kmn.md) > [Osk](./kmc-kmn.osk.md)
+
+## Osk namespace
+
+## Functions
+
+| Function | Description |
+| --- | --- |
+| [parseMapping(mapping)](./kmc-kmn.osk.parsemapping.md) | |
+| [remapTouchLayout(source, map)](./kmc-kmn.osk.remaptouchlayout.md) | |
+| [remapVisualKeyboard(vk, map)](./kmc-kmn.osk.remapvisualkeyboard.md) | |
+
+## Interfaces
+
+| Interface | Description |
+| --- | --- |
+| [StringRef](./kmc-kmn.osk.stringref.md) | |
+| [StringRefUsage](./kmc-kmn.osk.stringrefusage.md) | |
+| [StringResult](./kmc-kmn.osk.stringresult.md) | Represents a single key cap found by AnalyzeOskCharacterUse |
+
+## Type Aliases
+
+| Type Alias | Description |
+| --- | --- |
+| [PuaMap](./kmc-kmn.osk.puamap.md) | |
+
diff --git a/developer/docs/help/reference/api/kmc-kmn.osk.parsemapping.md b/developer/docs/help/reference/api/kmc-kmn.osk.parsemapping.md
new file mode 100644
index 00000000000..d2dbbdffe50
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-kmn.osk.parsemapping.md
@@ -0,0 +1,22 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-kmn](./kmc-kmn.md) > [Osk](./kmc-kmn.osk.md) > [parseMapping](./kmc-kmn.osk.parsemapping.md)
+
+## Osk.parseMapping() function
+
+**Signature:**
+
+```typescript
+export declare function parseMapping(mapping: any): PuaMap;
+```
+
+## Parameters
+
+| Parameter | Type | Description |
+| --- | --- | --- |
+| mapping | any | |
+
+**Returns:**
+
+[PuaMap](./kmc-kmn.osk.puamap.md)
+
diff --git a/developer/docs/help/reference/api/kmc-kmn.osk.puamap.md b/developer/docs/help/reference/api/kmc-kmn.osk.puamap.md
new file mode 100644
index 00000000000..4a56d392123
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-kmn.osk.puamap.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-kmn](./kmc-kmn.md) > [Osk](./kmc-kmn.osk.md) > [PuaMap](./kmc-kmn.osk.puamap.md)
+
+## Osk.PuaMap type
+
+**Signature:**
+
+```typescript
+export type PuaMap = {
+ [index: string]: string;
+};
+```
diff --git a/developer/docs/help/reference/api/kmc-kmn.osk.remaptouchlayout.md b/developer/docs/help/reference/api/kmc-kmn.osk.remaptouchlayout.md
new file mode 100644
index 00000000000..b6c8c76239f
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-kmn.osk.remaptouchlayout.md
@@ -0,0 +1,23 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-kmn](./kmc-kmn.md) > [Osk](./kmc-kmn.osk.md) > [remapTouchLayout](./kmc-kmn.osk.remaptouchlayout.md)
+
+## Osk.remapTouchLayout() function
+
+**Signature:**
+
+```typescript
+export declare function remapTouchLayout(source: TouchLayout.TouchLayoutFile, map: PuaMap): boolean;
+```
+
+## Parameters
+
+| Parameter | Type | Description |
+| --- | --- | --- |
+| source | TouchLayout.TouchLayoutFile | |
+| map | [PuaMap](./kmc-kmn.osk.puamap.md) | |
+
+**Returns:**
+
+boolean
+
diff --git a/developer/docs/help/reference/api/kmc-kmn.osk.remapvisualkeyboard.md b/developer/docs/help/reference/api/kmc-kmn.osk.remapvisualkeyboard.md
new file mode 100644
index 00000000000..b6c41d28032
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-kmn.osk.remapvisualkeyboard.md
@@ -0,0 +1,23 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-kmn](./kmc-kmn.md) > [Osk](./kmc-kmn.osk.md) > [remapVisualKeyboard](./kmc-kmn.osk.remapvisualkeyboard.md)
+
+## Osk.remapVisualKeyboard() function
+
+**Signature:**
+
+```typescript
+export declare function remapVisualKeyboard(vk: VisualKeyboard.VisualKeyboard, map: PuaMap): boolean;
+```
+
+## Parameters
+
+| Parameter | Type | Description |
+| --- | --- | --- |
+| vk | VisualKeyboard.VisualKeyboard | |
+| map | [PuaMap](./kmc-kmn.osk.puamap.md) | |
+
+**Returns:**
+
+boolean
+
diff --git a/developer/docs/help/reference/api/kmc-kmn.osk.stringref.md b/developer/docs/help/reference/api/kmc-kmn.osk.stringref.md
new file mode 100644
index 00000000000..24e66ec891a
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-kmn.osk.stringref.md
@@ -0,0 +1,19 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-kmn](./kmc-kmn.md) > [Osk](./kmc-kmn.osk.md) > [StringRef](./kmc-kmn.osk.stringref.md)
+
+## Osk.StringRef interface
+
+**Signature:**
+
+```typescript
+export interface StringRef
+```
+
+## Properties
+
+| Property | Modifiers | Type | Description |
+| --- | --- | --- | --- |
+| [str](./kmc-kmn.osk.stringref.str.md) | | string | |
+| [usages](./kmc-kmn.osk.stringref.usages.md) | | [StringRefUsage](./kmc-kmn.osk.stringrefusage.md)\[\] | |
+
diff --git a/developer/docs/help/reference/api/kmc-kmn.osk.stringref.str.md b/developer/docs/help/reference/api/kmc-kmn.osk.stringref.str.md
new file mode 100644
index 00000000000..85e45efa043
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-kmn.osk.stringref.str.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-kmn](./kmc-kmn.md) > [Osk](./kmc-kmn.osk.md) > [StringRef](./kmc-kmn.osk.stringref.md) > [str](./kmc-kmn.osk.stringref.str.md)
+
+## Osk.StringRef.str property
+
+**Signature:**
+
+```typescript
+str: string;
+```
diff --git a/developer/docs/help/reference/api/kmc-kmn.osk.stringref.usages.md b/developer/docs/help/reference/api/kmc-kmn.osk.stringref.usages.md
new file mode 100644
index 00000000000..8e6c209879a
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-kmn.osk.stringref.usages.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-kmn](./kmc-kmn.md) > [Osk](./kmc-kmn.osk.md) > [StringRef](./kmc-kmn.osk.stringref.md) > [usages](./kmc-kmn.osk.stringref.usages.md)
+
+## Osk.StringRef.usages property
+
+**Signature:**
+
+```typescript
+usages: StringRefUsage[];
+```
diff --git a/developer/docs/help/reference/api/kmc-kmn.osk.stringrefusage.count.md b/developer/docs/help/reference/api/kmc-kmn.osk.stringrefusage.count.md
new file mode 100644
index 00000000000..73cbde62ebd
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-kmn.osk.stringrefusage.count.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-kmn](./kmc-kmn.md) > [Osk](./kmc-kmn.osk.md) > [StringRefUsage](./kmc-kmn.osk.stringrefusage.md) > [count](./kmc-kmn.osk.stringrefusage.count.md)
+
+## Osk.StringRefUsage.count property
+
+**Signature:**
+
+```typescript
+count: number;
+```
diff --git a/developer/docs/help/reference/api/kmc-kmn.osk.stringrefusage.filename.md b/developer/docs/help/reference/api/kmc-kmn.osk.stringrefusage.filename.md
new file mode 100644
index 00000000000..f199327033d
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-kmn.osk.stringrefusage.filename.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-kmn](./kmc-kmn.md) > [Osk](./kmc-kmn.osk.md) > [StringRefUsage](./kmc-kmn.osk.stringrefusage.md) > [filename](./kmc-kmn.osk.stringrefusage.filename.md)
+
+## Osk.StringRefUsage.filename property
+
+**Signature:**
+
+```typescript
+filename: string;
+```
diff --git a/developer/docs/help/reference/api/kmc-kmn.osk.stringrefusage.md b/developer/docs/help/reference/api/kmc-kmn.osk.stringrefusage.md
new file mode 100644
index 00000000000..403b0af7fe0
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-kmn.osk.stringrefusage.md
@@ -0,0 +1,19 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-kmn](./kmc-kmn.md) > [Osk](./kmc-kmn.osk.md) > [StringRefUsage](./kmc-kmn.osk.stringrefusage.md)
+
+## Osk.StringRefUsage interface
+
+**Signature:**
+
+```typescript
+export interface StringRefUsage
+```
+
+## Properties
+
+| Property | Modifiers | Type | Description |
+| --- | --- | --- | --- |
+| [count](./kmc-kmn.osk.stringrefusage.count.md) | | number | |
+| [filename](./kmc-kmn.osk.stringrefusage.filename.md) | | string | |
+
diff --git a/developer/docs/help/reference/api/kmc-kmn.osk.stringresult.md b/developer/docs/help/reference/api/kmc-kmn.osk.stringresult.md
new file mode 100644
index 00000000000..1e5a6d10194
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-kmn.osk.stringresult.md
@@ -0,0 +1,23 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-kmn](./kmc-kmn.md) > [Osk](./kmc-kmn.osk.md) > [StringResult](./kmc-kmn.osk.stringresult.md)
+
+## Osk.StringResult interface
+
+Represents a single key cap found by `AnalyzeOskCharacterUse`
+
+**Signature:**
+
+```typescript
+export interface StringResult
+```
+
+## Properties
+
+| Property | Modifiers | Type | Description |
+| --- | --- | --- | --- |
+| [pua](./kmc-kmn.osk.stringresult.pua.md) | | string | hexadecimal single character in PUA range, without 'U+' prefix, e.g. 'F100' |
+| [str](./kmc-kmn.osk.stringresult.str.md) | | string | the key cap string |
+| [unicode](./kmc-kmn.osk.stringresult.unicode.md) | | string | unicode code points in for reference, without 'U+' prefix, e.g. '0061 0301' |
+| [usages](./kmc-kmn.osk.stringresult.usages.md) | | [StringRefUsage](./kmc-kmn.osk.stringrefusage.md)\[\] \| string\[\] | files in which the string is referenced; will be an array of if includeCounts is true, otherwise will be an array of strings listing files in which the key cap may be found |
+
diff --git a/developer/docs/help/reference/api/kmc-kmn.osk.stringresult.pua.md b/developer/docs/help/reference/api/kmc-kmn.osk.stringresult.pua.md
new file mode 100644
index 00000000000..84b2880dc0e
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-kmn.osk.stringresult.pua.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-kmn](./kmc-kmn.md) > [Osk](./kmc-kmn.osk.md) > [StringResult](./kmc-kmn.osk.stringresult.md) > [pua](./kmc-kmn.osk.stringresult.pua.md)
+
+## Osk.StringResult.pua property
+
+hexadecimal single character in PUA range, without 'U+' prefix, e.g. 'F100'
+
+**Signature:**
+
+```typescript
+pua: string;
+```
diff --git a/developer/docs/help/reference/api/kmc-kmn.osk.stringresult.str.md b/developer/docs/help/reference/api/kmc-kmn.osk.stringresult.str.md
new file mode 100644
index 00000000000..e24371b1d89
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-kmn.osk.stringresult.str.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-kmn](./kmc-kmn.md) > [Osk](./kmc-kmn.osk.md) > [StringResult](./kmc-kmn.osk.stringresult.md) > [str](./kmc-kmn.osk.stringresult.str.md)
+
+## Osk.StringResult.str property
+
+the key cap string
+
+**Signature:**
+
+```typescript
+str: string;
+```
diff --git a/developer/docs/help/reference/api/kmc-kmn.osk.stringresult.unicode.md b/developer/docs/help/reference/api/kmc-kmn.osk.stringresult.unicode.md
new file mode 100644
index 00000000000..92c8a789d50
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-kmn.osk.stringresult.unicode.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-kmn](./kmc-kmn.md) > [Osk](./kmc-kmn.osk.md) > [StringResult](./kmc-kmn.osk.stringresult.md) > [unicode](./kmc-kmn.osk.stringresult.unicode.md)
+
+## Osk.StringResult.unicode property
+
+unicode code points in for reference, without 'U+' prefix, e.g. '0061 0301'
+
+**Signature:**
+
+```typescript
+unicode: string;
+```
diff --git a/developer/docs/help/reference/api/kmc-kmn.osk.stringresult.usages.md b/developer/docs/help/reference/api/kmc-kmn.osk.stringresult.usages.md
new file mode 100644
index 00000000000..0010cc8cb89
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-kmn.osk.stringresult.usages.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-kmn](./kmc-kmn.md) > [Osk](./kmc-kmn.osk.md) > [StringResult](./kmc-kmn.osk.stringresult.md) > [usages](./kmc-kmn.osk.stringresult.usages.md)
+
+## Osk.StringResult.usages property
+
+files in which the string is referenced; will be an array of if includeCounts is true, otherwise will be an array of strings listing files in which the key cap may be found
+
+**Signature:**
+
+```typescript
+usages: StringRefUsage[] | string[];
+```
diff --git a/developer/docs/help/reference/api/kmc-ldml.ldmlcompileroptions.md b/developer/docs/help/reference/api/kmc-ldml.ldmlcompileroptions.md
new file mode 100644
index 00000000000..3ee68592870
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-ldml.ldmlcompileroptions.md
@@ -0,0 +1,21 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-ldml](./kmc-ldml.md) > [LdmlCompilerOptions](./kmc-ldml.ldmlcompileroptions.md)
+
+## LdmlCompilerOptions interface
+
+Options for the .xml LDML keyboard compiler
+
+**Signature:**
+
+```typescript
+export interface LdmlCompilerOptions extends CompilerOptions
+```
+**Extends:** CompilerOptions
+
+## Properties
+
+| Property | Modifiers | Type | Description |
+| --- | --- | --- | --- |
+| [readerOptions](./kmc-ldml.ldmlcompileroptions.readeroptions.md) | | LDMLKeyboardXMLSourceFileReaderOptions | Paths and other options required for reading .xml files |
+
diff --git a/developer/docs/help/reference/api/kmc-ldml.ldmlcompileroptions.readeroptions.md b/developer/docs/help/reference/api/kmc-ldml.ldmlcompileroptions.readeroptions.md
new file mode 100644
index 00000000000..59e3f53b736
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-ldml.ldmlcompileroptions.readeroptions.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-ldml](./kmc-ldml.md) > [LdmlCompilerOptions](./kmc-ldml.ldmlcompileroptions.md) > [readerOptions](./kmc-ldml.ldmlcompileroptions.readeroptions.md)
+
+## LdmlCompilerOptions.readerOptions property
+
+Paths and other options required for reading .xml files
+
+**Signature:**
+
+```typescript
+readerOptions: LDMLKeyboardXMLSourceFileReaderOptions;
+```
diff --git a/developer/docs/help/reference/api/kmc-ldml.ldmlkeyboardcompiler.init.md b/developer/docs/help/reference/api/kmc-ldml.ldmlkeyboardcompiler.init.md
new file mode 100644
index 00000000000..6fd83e9d463
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-ldml.ldmlkeyboardcompiler.init.md
@@ -0,0 +1,27 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-ldml](./kmc-ldml.md) > [LdmlKeyboardCompiler](./kmc-ldml.ldmlkeyboardcompiler.md) > [init](./kmc-ldml.ldmlkeyboardcompiler.init.md)
+
+## LdmlKeyboardCompiler.init() method
+
+Initialize the compiler, including loading the WASM host for uset parsing. Copies options.
+
+**Signature:**
+
+```typescript
+init(callbacks: CompilerCallbacks, options: LdmlCompilerOptions): Promise;
+```
+
+## Parameters
+
+| Parameter | Type | Description |
+| --- | --- | --- |
+| callbacks | CompilerCallbacks | Callbacks for external interfaces, including message reporting and file io |
+| options | [LdmlCompilerOptions](./kmc-ldml.ldmlcompileroptions.md) | Compiler options |
+
+**Returns:**
+
+Promise<boolean>
+
+false if initialization fails
+
diff --git a/developer/docs/help/reference/api/kmc-ldml.ldmlkeyboardcompiler.md b/developer/docs/help/reference/api/kmc-ldml.ldmlkeyboardcompiler.md
new file mode 100644
index 00000000000..fdd5d32c16b
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-ldml.ldmlkeyboardcompiler.md
@@ -0,0 +1,23 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-ldml](./kmc-ldml.md) > [LdmlKeyboardCompiler](./kmc-ldml.ldmlkeyboardcompiler.md)
+
+## LdmlKeyboardCompiler class
+
+Compiles a LDML keyboard .xml file to a .kmx (KMXPlus), .kvk, and/or .js. The compiler does not read or write from filesystem or network directly, but relies on callbacks for all external IO.
+
+**Signature:**
+
+```typescript
+export declare class LdmlKeyboardCompiler implements KeymanCompiler
+```
+**Implements:** KeymanCompiler
+
+## Methods
+
+| Method | Modifiers | Description |
+| --- | --- | --- |
+| [init(callbacks, options)](./kmc-ldml.ldmlkeyboardcompiler.init.md) | | Initialize the compiler, including loading the WASM host for uset parsing. Copies options. |
+| [run(inputFilename, outputFilename)](./kmc-ldml.ldmlkeyboardcompiler.run.md) | | Compiles a LDML keyboard .xml file to .kmx, .kvk, and/or .js files. Returns an object containing binary artifacts on success. The files are passed in by name, and the compiler will use callbacks as passed to the [LdmlKeyboardCompiler.init()](./kmc-ldml.ldmlkeyboardcompiler.init.md) function to read any input files by disk. |
+| [write(artifacts)](./kmc-ldml.ldmlkeyboardcompiler.write.md) | |
Write artifacts from a successful compile to disk, via callbacks methods. The artifacts written may include:
- .kmx file - binary keyboard used by Keyman on desktop platforms - .kvk file - binary on screen keyboard used by Keyman on desktop platforms - .js file - Javascript keyboard for web and touch platforms
|
+
diff --git a/developer/docs/help/reference/api/kmc-ldml.ldmlkeyboardcompiler.run.md b/developer/docs/help/reference/api/kmc-ldml.ldmlkeyboardcompiler.run.md
new file mode 100644
index 00000000000..cb0e9eaa7f4
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-ldml.ldmlkeyboardcompiler.run.md
@@ -0,0 +1,27 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-ldml](./kmc-ldml.md) > [LdmlKeyboardCompiler](./kmc-ldml.ldmlkeyboardcompiler.md) > [run](./kmc-ldml.ldmlkeyboardcompiler.run.md)
+
+## LdmlKeyboardCompiler.run() method
+
+Compiles a LDML keyboard .xml file to .kmx, .kvk, and/or .js files. Returns an object containing binary artifacts on success. The files are passed in by name, and the compiler will use callbacks as passed to the [LdmlKeyboardCompiler.init()](./kmc-ldml.ldmlkeyboardcompiler.init.md) function to read any input files by disk.
+
+**Signature:**
+
+```typescript
+run(inputFilename: string, outputFilename?: string): Promise;
+```
+
+## Parameters
+
+| Parameter | Type | Description |
+| --- | --- | --- |
+| inputFilename | string | |
+| outputFilename | string | _(Optional)_ |
+
+**Returns:**
+
+Promise<LdmlKeyboardCompilerResult>
+
+Binary artifacts on success, null on failure.
+
diff --git a/developer/docs/help/reference/api/kmc-ldml.ldmlkeyboardcompiler.write.md b/developer/docs/help/reference/api/kmc-ldml.ldmlkeyboardcompiler.write.md
new file mode 100644
index 00000000000..434b463adb4
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-ldml.ldmlkeyboardcompiler.write.md
@@ -0,0 +1,28 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-ldml](./kmc-ldml.md) > [LdmlKeyboardCompiler](./kmc-ldml.ldmlkeyboardcompiler.md) > [write](./kmc-ldml.ldmlkeyboardcompiler.write.md)
+
+## LdmlKeyboardCompiler.write() method
+
+Write artifacts from a successful compile to disk, via callbacks methods. The artifacts written may include:
+
+- .kmx file - binary keyboard used by Keyman on desktop platforms - .kvk file - binary on screen keyboard used by Keyman on desktop platforms - .js file - Javascript keyboard for web and touch platforms
+
+**Signature:**
+
+```typescript
+write(artifacts: LdmlKeyboardCompilerArtifacts): Promise;
+```
+
+## Parameters
+
+| Parameter | Type | Description |
+| --- | --- | --- |
+| artifacts | LdmlKeyboardCompilerArtifacts | object containing artifact binary data to write out |
+
+**Returns:**
+
+Promise<boolean>
+
+true on success
+
diff --git a/developer/docs/help/reference/api/kmc-ldml.md b/developer/docs/help/reference/api/kmc-ldml.md
new file mode 100644
index 00000000000..ca921083a22
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-ldml.md
@@ -0,0 +1,18 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-ldml](./kmc-ldml.md)
+
+## kmc-ldml package
+
+## Classes
+
+| Class | Description |
+| --- | --- |
+| [LdmlKeyboardCompiler](./kmc-ldml.ldmlkeyboardcompiler.md) | Compiles a LDML keyboard .xml file to a .kmx (KMXPlus), .kvk, and/or .js. The compiler does not read or write from filesystem or network directly, but relies on callbacks for all external IO. |
+
+## Interfaces
+
+| Interface | Description |
+| --- | --- |
+| [LdmlCompilerOptions](./kmc-ldml.ldmlcompileroptions.md) | Options for the .xml LDML keyboard compiler |
+
diff --git a/developer/docs/help/reference/api/kmc-model-info.md b/developer/docs/help/reference/api/kmc-model-info.md
new file mode 100644
index 00000000000..f7cbd6d4631
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-model-info.md
@@ -0,0 +1,21 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-model-info](./kmc-model-info.md)
+
+## kmc-model-info package
+
+## Classes
+
+| Class | Description |
+| --- | --- |
+| [ModelInfoCompiler](./kmc-model-info.modelinfocompiler.md) | Compiles source data from a lexical model project to a .model\_info. The compiler does not read or write from filesystem or network directly, but relies on callbacks for all external IO. |
+| [ModelInfoSources](./kmc-model-info.modelinfosources.md) | Description of sources and metadata required to build a .model\_info file |
+
+## Interfaces
+
+| Interface | Description |
+| --- | --- |
+| [ModelInfoCompilerArtifacts](./kmc-model-info.modelinfocompilerartifacts.md) | Internal in-memory build artifacts from a successful compilation |
+| [ModelInfoCompilerOptions](./kmc-model-info.modelinfocompileroptions.md) | Options for the .model\_info compiler |
+| [ModelInfoCompilerResult](./kmc-model-info.modelinfocompilerresult.md) | Build artifacts from the .model\_info compiler |
+
diff --git a/developer/docs/help/reference/api/kmc-model-info.modelinfocompiler._constructor_.md b/developer/docs/help/reference/api/kmc-model-info.modelinfocompiler._constructor_.md
new file mode 100644
index 00000000000..9aadd791a08
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-model-info.modelinfocompiler._constructor_.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-model-info](./kmc-model-info.md) > [ModelInfoCompiler](./kmc-model-info.modelinfocompiler.md) > [(constructor)](./kmc-model-info.modelinfocompiler._constructor_.md)
+
+## ModelInfoCompiler.(constructor)
+
+Constructs a new instance of the `ModelInfoCompiler` class
+
+**Signature:**
+
+```typescript
+constructor();
+```
diff --git a/developer/docs/help/reference/api/kmc-model-info.modelinfocompiler.init.md b/developer/docs/help/reference/api/kmc-model-info.modelinfocompiler.init.md
new file mode 100644
index 00000000000..ff8383bb27e
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-model-info.modelinfocompiler.init.md
@@ -0,0 +1,27 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-model-info](./kmc-model-info.md) > [ModelInfoCompiler](./kmc-model-info.modelinfocompiler.md) > [init](./kmc-model-info.modelinfocompiler.init.md)
+
+## ModelInfoCompiler.init() method
+
+Initialize the compiler. Copies options.
+
+**Signature:**
+
+```typescript
+init(callbacks: CompilerCallbacks, options: ModelInfoCompilerOptions): Promise;
+```
+
+## Parameters
+
+| Parameter | Type | Description |
+| --- | --- | --- |
+| callbacks | CompilerCallbacks | Callbacks for external interfaces, including message reporting and file io |
+| options | [ModelInfoCompilerOptions](./kmc-model-info.modelinfocompileroptions.md) | Compiler options |
+
+**Returns:**
+
+Promise<boolean>
+
+false if initialization fails
+
diff --git a/developer/docs/help/reference/api/kmc-model-info.modelinfocompiler.md b/developer/docs/help/reference/api/kmc-model-info.modelinfocompiler.md
new file mode 100644
index 00000000000..05b0c58ba68
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-model-info.modelinfocompiler.md
@@ -0,0 +1,29 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-model-info](./kmc-model-info.md) > [ModelInfoCompiler](./kmc-model-info.modelinfocompiler.md)
+
+## ModelInfoCompiler class
+
+Compiles source data from a lexical model project to a .model\_info. The compiler does not read or write from filesystem or network directly, but relies on callbacks for all external IO.
+
+**Signature:**
+
+```typescript
+export declare class ModelInfoCompiler implements KeymanCompiler
+```
+**Implements:** KeymanCompiler
+
+## Constructors
+
+| Constructor | Modifiers | Description |
+| --- | --- | --- |
+| [(constructor)()](./kmc-model-info.modelinfocompiler._constructor_.md) | | Constructs a new instance of the ModelInfoCompiler class |
+
+## Methods
+
+| Method | Modifiers | Description |
+| --- | --- | --- |
+| [init(callbacks, options)](./kmc-model-info.modelinfocompiler.init.md) | | Initialize the compiler. Copies options. |
+| [run(inputFilename, outputFilename)](./kmc-model-info.modelinfocompiler.run.md) | |
Builds .model\_info file with metadata from the model and package source file. Returns an object containing binary artifacts on success. The files are passed in by name, and the compiler will use callbacks as passed to the [ModelInfoCompiler.init()](./kmc-model-info.modelinfocompiler.init.md) function to read any input files by disk.
This function is intended for use within the lexical-models repository. While many of the parameters could be deduced from each other, they are specified here to reduce the number of places the filenames are constructed.
Write artifacts from a successful compile to disk, via callbacks methods. The artifacts written may include:
- .model\_info file - metadata file used by keyman.com
|
+
diff --git a/developer/docs/help/reference/api/kmc-model-info.modelinfocompiler.run.md b/developer/docs/help/reference/api/kmc-model-info.modelinfocompiler.run.md
new file mode 100644
index 00000000000..ead3fa50032
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-model-info.modelinfocompiler.run.md
@@ -0,0 +1,29 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-model-info](./kmc-model-info.md) > [ModelInfoCompiler](./kmc-model-info.modelinfocompiler.md) > [run](./kmc-model-info.modelinfocompiler.run.md)
+
+## ModelInfoCompiler.run() method
+
+Builds .model\_info file with metadata from the model and package source file. Returns an object containing binary artifacts on success. The files are passed in by name, and the compiler will use callbacks as passed to the [ModelInfoCompiler.init()](./kmc-model-info.modelinfocompiler.init.md) function to read any input files by disk.
+
+This function is intended for use within the lexical-models repository. While many of the parameters could be deduced from each other, they are specified here to reduce the number of places the filenames are constructed.
+
+**Signature:**
+
+```typescript
+run(inputFilename: string, outputFilename?: string): Promise;
+```
+
+## Parameters
+
+| Parameter | Type | Description |
+| --- | --- | --- |
+| inputFilename | string | |
+| outputFilename | string | _(Optional)_ |
+
+**Returns:**
+
+Promise<[ModelInfoCompilerResult](./kmc-model-info.modelinfocompilerresult.md)>
+
+Binary artifacts on success, null on failure.
+
diff --git a/developer/docs/help/reference/api/kmc-model-info.modelinfocompiler.write.md b/developer/docs/help/reference/api/kmc-model-info.modelinfocompiler.write.md
new file mode 100644
index 00000000000..e2d2e5fd27e
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-model-info.modelinfocompiler.write.md
@@ -0,0 +1,28 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-model-info](./kmc-model-info.md) > [ModelInfoCompiler](./kmc-model-info.modelinfocompiler.md) > [write](./kmc-model-info.modelinfocompiler.write.md)
+
+## ModelInfoCompiler.write() method
+
+Write artifacts from a successful compile to disk, via callbacks methods. The artifacts written may include:
+
+- .model\_info file - metadata file used by keyman.com
+
+**Signature:**
+
+```typescript
+write(artifacts: ModelInfoCompilerArtifacts): Promise;
+```
+
+## Parameters
+
+| Parameter | Type | Description |
+| --- | --- | --- |
+| artifacts | [ModelInfoCompilerArtifacts](./kmc-model-info.modelinfocompilerartifacts.md) | object containing artifact binary data to write out |
+
+**Returns:**
+
+Promise<boolean>
+
+true on success
+
diff --git a/developer/docs/help/reference/api/kmc-model-info.modelinfocompilerartifacts.md b/developer/docs/help/reference/api/kmc-model-info.modelinfocompilerartifacts.md
new file mode 100644
index 00000000000..1c7e2e7df65
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-model-info.modelinfocompilerartifacts.md
@@ -0,0 +1,21 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-model-info](./kmc-model-info.md) > [ModelInfoCompilerArtifacts](./kmc-model-info.modelinfocompilerartifacts.md)
+
+## ModelInfoCompilerArtifacts interface
+
+Internal in-memory build artifacts from a successful compilation
+
+**Signature:**
+
+```typescript
+export interface ModelInfoCompilerArtifacts extends KeymanCompilerArtifacts
+```
+**Extends:** KeymanCompilerArtifacts
+
+## Properties
+
+| Property | Modifiers | Type | Description |
+| --- | --- | --- | --- |
+| [model\_info](./kmc-model-info.modelinfocompilerartifacts.model_info.md) | | KeymanCompilerArtifact | Binary model info filedata and filename - used by keyman.com |
+
diff --git a/developer/docs/help/reference/api/kmc-model-info.modelinfocompilerartifacts.model_info.md b/developer/docs/help/reference/api/kmc-model-info.modelinfocompilerartifacts.model_info.md
new file mode 100644
index 00000000000..1f66e96f344
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-model-info.modelinfocompilerartifacts.model_info.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-model-info](./kmc-model-info.md) > [ModelInfoCompilerArtifacts](./kmc-model-info.modelinfocompilerartifacts.md) > [model\_info](./kmc-model-info.modelinfocompilerartifacts.model_info.md)
+
+## ModelInfoCompilerArtifacts.model\_info property
+
+Binary model info filedata and filename - used by keyman.com
+
+**Signature:**
+
+```typescript
+model_info: KeymanCompilerArtifact;
+```
diff --git a/developer/docs/help/reference/api/kmc-model-info.modelinfocompileroptions.md b/developer/docs/help/reference/api/kmc-model-info.modelinfocompileroptions.md
new file mode 100644
index 00000000000..984d9bf70a7
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-model-info.modelinfocompileroptions.md
@@ -0,0 +1,21 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-model-info](./kmc-model-info.md) > [ModelInfoCompilerOptions](./kmc-model-info.modelinfocompileroptions.md)
+
+## ModelInfoCompilerOptions interface
+
+Options for the .model\_info compiler
+
+**Signature:**
+
+```typescript
+export interface ModelInfoCompilerOptions extends CompilerOptions
+```
+**Extends:** CompilerOptions
+
+## Properties
+
+| Property | Modifiers | Type | Description |
+| --- | --- | --- | --- |
+| [sources](./kmc-model-info.modelinfocompileroptions.sources.md) | | [ModelInfoSources](./kmc-model-info.modelinfosources.md) | Description of sources and metadata required to build a .model\_info file |
+
diff --git a/developer/docs/help/reference/api/kmc-model-info.modelinfocompileroptions.sources.md b/developer/docs/help/reference/api/kmc-model-info.modelinfocompileroptions.sources.md
new file mode 100644
index 00000000000..40c986596e9
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-model-info.modelinfocompileroptions.sources.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-model-info](./kmc-model-info.md) > [ModelInfoCompilerOptions](./kmc-model-info.modelinfocompileroptions.md) > [sources](./kmc-model-info.modelinfocompileroptions.sources.md)
+
+## ModelInfoCompilerOptions.sources property
+
+Description of sources and metadata required to build a .model\_info file
+
+**Signature:**
+
+```typescript
+sources: ModelInfoSources;
+```
diff --git a/developer/docs/help/reference/api/kmc-model-info.modelinfocompilerresult.artifacts.md b/developer/docs/help/reference/api/kmc-model-info.modelinfocompilerresult.artifacts.md
new file mode 100644
index 00000000000..9dc3bdf6ea4
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-model-info.modelinfocompilerresult.artifacts.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-model-info](./kmc-model-info.md) > [ModelInfoCompilerResult](./kmc-model-info.modelinfocompilerresult.md) > [artifacts](./kmc-model-info.modelinfocompilerresult.artifacts.md)
+
+## ModelInfoCompilerResult.artifacts property
+
+Internal in-memory build artifacts from a successful compilation. Caller can write these to disk with [ModelInfoCompiler.write()](./kmc-model-info.modelinfocompiler.write.md)
+
+**Signature:**
+
+```typescript
+artifacts: ModelInfoCompilerArtifacts;
+```
diff --git a/developer/docs/help/reference/api/kmc-model-info.modelinfocompilerresult.md b/developer/docs/help/reference/api/kmc-model-info.modelinfocompilerresult.md
new file mode 100644
index 00000000000..0980afd7f76
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-model-info.modelinfocompilerresult.md
@@ -0,0 +1,21 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-model-info](./kmc-model-info.md) > [ModelInfoCompilerResult](./kmc-model-info.modelinfocompilerresult.md)
+
+## ModelInfoCompilerResult interface
+
+Build artifacts from the .model\_info compiler
+
+**Signature:**
+
+```typescript
+export interface ModelInfoCompilerResult extends KeymanCompilerResult
+```
+**Extends:** KeymanCompilerResult
+
+## Properties
+
+| Property | Modifiers | Type | Description |
+| --- | --- | --- | --- |
+| [artifacts](./kmc-model-info.modelinfocompilerresult.artifacts.md) | | [ModelInfoCompilerArtifacts](./kmc-model-info.modelinfocompilerartifacts.md) | Internal in-memory build artifacts from a successful compilation. Caller can write these to disk with [ModelInfoCompiler.write()](./kmc-model-info.modelinfocompiler.write.md) |
+
diff --git a/developer/docs/help/reference/api/kmc-model-info.modelinfosources.forpublishing.md b/developer/docs/help/reference/api/kmc-model-info.modelinfosources.forpublishing.md
new file mode 100644
index 00000000000..0877529d209
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-model-info.modelinfosources.forpublishing.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-model-info](./kmc-model-info.md) > [ModelInfoSources](./kmc-model-info.modelinfosources.md) > [forPublishing](./kmc-model-info.modelinfosources.forpublishing.md)
+
+## ModelInfoSources.forPublishing property
+
+Return an error if project does not meet requirements of lexical-models repository
+
+**Signature:**
+
+```typescript
+forPublishing: boolean;
+```
diff --git a/developer/docs/help/reference/api/kmc-model-info.modelinfosources.kmpfilename.md b/developer/docs/help/reference/api/kmc-model-info.modelinfosources.kmpfilename.md
new file mode 100644
index 00000000000..4d47a8ea9a4
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-model-info.modelinfosources.kmpfilename.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-model-info](./kmc-model-info.md) > [ModelInfoSources](./kmc-model-info.modelinfosources.md) > [kmpFileName](./kmc-model-info.modelinfosources.kmpfilename.md)
+
+## ModelInfoSources.kmpFileName property
+
+The compiled package filename and relative path (.kmp)
+
+**Signature:**
+
+```typescript
+kmpFileName: string;
+```
diff --git a/developer/docs/help/reference/api/kmc-model-info.modelinfosources.kmpjsondata.md b/developer/docs/help/reference/api/kmc-model-info.modelinfosources.kmpjsondata.md
new file mode 100644
index 00000000000..d0fbf7ebb7a
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-model-info.modelinfosources.kmpjsondata.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-model-info](./kmc-model-info.md) > [ModelInfoSources](./kmc-model-info.modelinfosources.md) > [kmpJsonData](./kmc-model-info.modelinfosources.kmpjsondata.md)
+
+## ModelInfoSources.kmpJsonData property
+
+The data from the .kps file, transformed to kmp.json
+
+**Signature:**
+
+```typescript
+kmpJsonData: KmpJsonFile.KmpJsonFile;
+```
diff --git a/developer/docs/help/reference/api/kmc-model-info.modelinfosources.kpsfilename.md b/developer/docs/help/reference/api/kmc-model-info.modelinfosources.kpsfilename.md
new file mode 100644
index 00000000000..c80e6755afe
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-model-info.modelinfosources.kpsfilename.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-model-info](./kmc-model-info.md) > [ModelInfoSources](./kmc-model-info.modelinfosources.md) > [kpsFilename](./kmc-model-info.modelinfosources.kpsfilename.md)
+
+## ModelInfoSources.kpsFilename property
+
+The source package filename and relative path (.kps)
+
+**Signature:**
+
+```typescript
+kpsFilename: string;
+```
diff --git a/developer/docs/help/reference/api/kmc-model-info.modelinfosources.lastcommitdate.md b/developer/docs/help/reference/api/kmc-model-info.modelinfosources.lastcommitdate.md
new file mode 100644
index 00000000000..c78c7d5b9db
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-model-info.modelinfosources.lastcommitdate.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-model-info](./kmc-model-info.md) > [ModelInfoSources](./kmc-model-info.modelinfosources.md) > [lastCommitDate](./kmc-model-info.modelinfosources.lastcommitdate.md)
+
+## ModelInfoSources.lastCommitDate property
+
+Last modification date for files in the project folder 'YYYY-MM-DDThh:mm:ssZ'
+
+**Signature:**
+
+```typescript
+lastCommitDate?: string;
+```
diff --git a/developer/docs/help/reference/api/kmc-model-info.modelinfosources.md b/developer/docs/help/reference/api/kmc-model-info.modelinfosources.md
new file mode 100644
index 00000000000..c74ab3cafd6
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-model-info.modelinfosources.md
@@ -0,0 +1,27 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-model-info](./kmc-model-info.md) > [ModelInfoSources](./kmc-model-info.modelinfosources.md)
+
+## ModelInfoSources class
+
+Description of sources and metadata required to build a .model\_info file
+
+**Signature:**
+
+```typescript
+export declare class ModelInfoSources
+```
+
+## Properties
+
+| Property | Modifiers | Type | Description |
+| --- | --- | --- | --- |
+| [forPublishing](./kmc-model-info.modelinfosources.forpublishing.md) | | boolean | Return an error if project does not meet requirements of lexical-models repository |
+| [kmpFileName](./kmc-model-info.modelinfosources.kmpfilename.md) | | string | The compiled package filename and relative path (.kmp) |
+| [kmpJsonData](./kmc-model-info.modelinfosources.kmpjsondata.md) | | KmpJsonFile.KmpJsonFile | The data from the .kps file, transformed to kmp.json |
+| [kpsFilename](./kmc-model-info.modelinfosources.kpsfilename.md) | | string | The source package filename and relative path (.kps) |
+| [lastCommitDate?](./kmc-model-info.modelinfosources.lastcommitdate.md) | | string | _(Optional)_ Last modification date for files in the project folder 'YYYY-MM-DDThh:mm:ssZ' |
+| [model\_id](./kmc-model-info.modelinfosources.model_id.md) | | string | The identifier for the model |
+| [modelFileName](./kmc-model-info.modelinfosources.modelfilename.md) | | string | The compiled model filename and relative path (.js) |
+| [sourcePath](./kmc-model-info.modelinfosources.sourcepath.md) | | string | The path in the keymanapp/lexical-models repo where this model may be found |
+
diff --git a/developer/docs/help/reference/api/kmc-model-info.modelinfosources.model_id.md b/developer/docs/help/reference/api/kmc-model-info.modelinfosources.model_id.md
new file mode 100644
index 00000000000..d0d40cde82b
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-model-info.modelinfosources.model_id.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-model-info](./kmc-model-info.md) > [ModelInfoSources](./kmc-model-info.modelinfosources.md) > [model\_id](./kmc-model-info.modelinfosources.model_id.md)
+
+## ModelInfoSources.model\_id property
+
+The identifier for the model
+
+**Signature:**
+
+```typescript
+model_id: string;
+```
diff --git a/developer/docs/help/reference/api/kmc-model-info.modelinfosources.modelfilename.md b/developer/docs/help/reference/api/kmc-model-info.modelinfosources.modelfilename.md
new file mode 100644
index 00000000000..2f4567e44fe
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-model-info.modelinfosources.modelfilename.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-model-info](./kmc-model-info.md) > [ModelInfoSources](./kmc-model-info.modelinfosources.md) > [modelFileName](./kmc-model-info.modelinfosources.modelfilename.md)
+
+## ModelInfoSources.modelFileName property
+
+The compiled model filename and relative path (.js)
+
+**Signature:**
+
+```typescript
+modelFileName: string;
+```
diff --git a/developer/docs/help/reference/api/kmc-model-info.modelinfosources.sourcepath.md b/developer/docs/help/reference/api/kmc-model-info.modelinfosources.sourcepath.md
new file mode 100644
index 00000000000..7df6ad03e57
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-model-info.modelinfosources.sourcepath.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-model-info](./kmc-model-info.md) > [ModelInfoSources](./kmc-model-info.modelinfosources.md) > [sourcePath](./kmc-model-info.modelinfosources.sourcepath.md)
+
+## ModelInfoSources.sourcePath property
+
+The path in the keymanapp/lexical-models repo where this model may be found
+
+**Signature:**
+
+```typescript
+sourcePath: string;
+```
diff --git a/developer/docs/help/reference/api/kmc-model.casedwordformtokeyspec.md b/developer/docs/help/reference/api/kmc-model.casedwordformtokeyspec.md
new file mode 100644
index 00000000000..5403c6f0c5b
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-model.casedwordformtokeyspec.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-model](./kmc-model.md) > [CasedWordformToKeySpec](./kmc-model.casedwordformtokeyspec.md)
+
+## CasedWordformToKeySpec type
+
+Simplifies input text to facilitate finding entries within a lexical model's lexicon, using the model's `applyCasing` function to assist in the keying process. 14.0
+
+**Signature:**
+
+```typescript
+export type CasedWordformToKeySpec = (term: string, applyCasing?: CasingFunction) => string;
+```
diff --git a/developer/docs/help/reference/api/kmc-model.lexicalmodelcompiler.init.md b/developer/docs/help/reference/api/kmc-model.lexicalmodelcompiler.init.md
new file mode 100644
index 00000000000..56e6fc5c9bc
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-model.lexicalmodelcompiler.init.md
@@ -0,0 +1,27 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-model](./kmc-model.md) > [LexicalModelCompiler](./kmc-model.lexicalmodelcompiler.md) > [init](./kmc-model.lexicalmodelcompiler.init.md)
+
+## LexicalModelCompiler.init() method
+
+Initialize the compiler. There are currently no options specific to the lexical model compiler
+
+**Signature:**
+
+```typescript
+init(callbacks: CompilerCallbacks, _options: CompilerOptions): Promise;
+```
+
+## Parameters
+
+| Parameter | Type | Description |
+| --- | --- | --- |
+| callbacks | CompilerCallbacks | Callbacks for external interfaces, including message reporting and file io |
+| \_options | CompilerOptions | |
+
+**Returns:**
+
+Promise<boolean>
+
+always succeeds and returns true
+
diff --git a/developer/docs/help/reference/api/kmc-model.lexicalmodelcompiler.md b/developer/docs/help/reference/api/kmc-model.lexicalmodelcompiler.md
new file mode 100644
index 00000000000..c7cab5b8f1c
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-model.lexicalmodelcompiler.md
@@ -0,0 +1,23 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-model](./kmc-model.md) > [LexicalModelCompiler](./kmc-model.lexicalmodelcompiler.md)
+
+## LexicalModelCompiler class
+
+Compiles a .model.ts file to a .model.js. The compiler does not read or write from filesystem or network directly, but relies on callbacks for all external IO.
+
+**Signature:**
+
+```typescript
+export declare class LexicalModelCompiler implements KeymanCompiler
+```
+**Implements:** KeymanCompiler
+
+## Methods
+
+| Method | Modifiers | Description |
+| --- | --- | --- |
+| [init(callbacks, \_options)](./kmc-model.lexicalmodelcompiler.init.md) | | Initialize the compiler. There are currently no options specific to the lexical model compiler |
+| [run(inputFilename, outputFilename)](./kmc-model.lexicalmodelcompiler.run.md) | | Compiles a .model.ts file to .model.js. Returns an object containing binary artifacts on success. The files are passed in by name, and the compiler will use callbacks as passed to the [LexicalModelCompiler.init()](./kmc-model.lexicalmodelcompiler.init.md) function to read any input files by disk. |
+| [write(artifacts)](./kmc-model.lexicalmodelcompiler.write.md) | |
Write artifacts from a successful compile to disk, via callbacks methods. The artifacts written may include:
- .model.js file - Javascript lexical model for web and touch platforms
|
+
diff --git a/developer/docs/help/reference/api/kmc-model.lexicalmodelcompiler.run.md b/developer/docs/help/reference/api/kmc-model.lexicalmodelcompiler.run.md
new file mode 100644
index 00000000000..e96c22038a8
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-model.lexicalmodelcompiler.run.md
@@ -0,0 +1,27 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-model](./kmc-model.md) > [LexicalModelCompiler](./kmc-model.lexicalmodelcompiler.md) > [run](./kmc-model.lexicalmodelcompiler.run.md)
+
+## LexicalModelCompiler.run() method
+
+Compiles a .model.ts file to .model.js. Returns an object containing binary artifacts on success. The files are passed in by name, and the compiler will use callbacks as passed to the [LexicalModelCompiler.init()](./kmc-model.lexicalmodelcompiler.init.md) function to read any input files by disk.
+
+**Signature:**
+
+```typescript
+run(inputFilename: string, outputFilename?: string): Promise;
+```
+
+## Parameters
+
+| Parameter | Type | Description |
+| --- | --- | --- |
+| inputFilename | string | |
+| outputFilename | string | _(Optional)_ |
+
+**Returns:**
+
+Promise<[LexicalModelCompilerResult](./kmc-model.lexicalmodelcompilerresult.md)>
+
+Binary artifacts on success, null on failure.
+
diff --git a/developer/docs/help/reference/api/kmc-model.lexicalmodelcompiler.write.md b/developer/docs/help/reference/api/kmc-model.lexicalmodelcompiler.write.md
new file mode 100644
index 00000000000..c3fd2f6934c
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-model.lexicalmodelcompiler.write.md
@@ -0,0 +1,28 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-model](./kmc-model.md) > [LexicalModelCompiler](./kmc-model.lexicalmodelcompiler.md) > [write](./kmc-model.lexicalmodelcompiler.write.md)
+
+## LexicalModelCompiler.write() method
+
+Write artifacts from a successful compile to disk, via callbacks methods. The artifacts written may include:
+
+- .model.js file - Javascript lexical model for web and touch platforms
+
+**Signature:**
+
+```typescript
+write(artifacts: LexicalModelCompilerArtifacts): Promise;
+```
+
+## Parameters
+
+| Parameter | Type | Description |
+| --- | --- | --- |
+| artifacts | [LexicalModelCompilerArtifacts](./kmc-model.lexicalmodelcompilerartifacts.md) | object containing artifact binary data to write out |
+
+**Returns:**
+
+Promise<boolean>
+
+always returns true
+
diff --git a/developer/docs/help/reference/api/kmc-model.lexicalmodelcompilerartifacts.js.md b/developer/docs/help/reference/api/kmc-model.lexicalmodelcompilerartifacts.js.md
new file mode 100644
index 00000000000..99f75d6a584
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-model.lexicalmodelcompilerartifacts.js.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-model](./kmc-model.md) > [LexicalModelCompilerArtifacts](./kmc-model.lexicalmodelcompilerartifacts.md) > [js](./kmc-model.lexicalmodelcompilerartifacts.js.md)
+
+## LexicalModelCompilerArtifacts.js property
+
+Javascript model filedata and filename - installable into KeymanWeb, Keyman mobile products
+
+**Signature:**
+
+```typescript
+js: KeymanCompilerArtifact;
+```
diff --git a/developer/docs/help/reference/api/kmc-model.lexicalmodelcompilerartifacts.md b/developer/docs/help/reference/api/kmc-model.lexicalmodelcompilerartifacts.md
new file mode 100644
index 00000000000..903a3948a7d
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-model.lexicalmodelcompilerartifacts.md
@@ -0,0 +1,21 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-model](./kmc-model.md) > [LexicalModelCompilerArtifacts](./kmc-model.lexicalmodelcompilerartifacts.md)
+
+## LexicalModelCompilerArtifacts interface
+
+Internal in-memory build artifacts from a successful compilation
+
+**Signature:**
+
+```typescript
+export interface LexicalModelCompilerArtifacts extends KeymanCompilerArtifacts
+```
+**Extends:** KeymanCompilerArtifacts
+
+## Properties
+
+| Property | Modifiers | Type | Description |
+| --- | --- | --- | --- |
+| [js](./kmc-model.lexicalmodelcompilerartifacts.js.md) | | KeymanCompilerArtifact | Javascript model filedata and filename - installable into KeymanWeb, Keyman mobile products |
+
diff --git a/developer/docs/help/reference/api/kmc-model.lexicalmodelcompilerresult.artifacts.md b/developer/docs/help/reference/api/kmc-model.lexicalmodelcompilerresult.artifacts.md
new file mode 100644
index 00000000000..2d4259416db
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-model.lexicalmodelcompilerresult.artifacts.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-model](./kmc-model.md) > [LexicalModelCompilerResult](./kmc-model.lexicalmodelcompilerresult.md) > [artifacts](./kmc-model.lexicalmodelcompilerresult.artifacts.md)
+
+## LexicalModelCompilerResult.artifacts property
+
+Internal in-memory build artifacts from a successful compilation. Caller can write these to disk with [LexicalModelCompiler.write()](./kmc-model.lexicalmodelcompiler.write.md)
+
+**Signature:**
+
+```typescript
+artifacts: LexicalModelCompilerArtifacts;
+```
diff --git a/developer/docs/help/reference/api/kmc-model.lexicalmodelcompilerresult.md b/developer/docs/help/reference/api/kmc-model.lexicalmodelcompilerresult.md
new file mode 100644
index 00000000000..eb541fc46a9
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-model.lexicalmodelcompilerresult.md
@@ -0,0 +1,21 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-model](./kmc-model.md) > [LexicalModelCompilerResult](./kmc-model.lexicalmodelcompilerresult.md)
+
+## LexicalModelCompilerResult interface
+
+Build artifacts from the lexical model compiler
+
+**Signature:**
+
+```typescript
+export interface LexicalModelCompilerResult extends KeymanCompilerResult
+```
+**Extends:** KeymanCompilerResult
+
+## Properties
+
+| Property | Modifiers | Type | Description |
+| --- | --- | --- | --- |
+| [artifacts](./kmc-model.lexicalmodelcompilerresult.artifacts.md) | | [LexicalModelCompilerArtifacts](./kmc-model.lexicalmodelcompilerartifacts.md) | Internal in-memory build artifacts from a successful compilation. Caller can write these to disk with [LexicalModelCompiler.write()](./kmc-model.lexicalmodelcompiler.write.md) |
+
diff --git a/developer/docs/help/reference/api/kmc-model.lexicalmodelsource.applycasing.md b/developer/docs/help/reference/api/kmc-model.lexicalmodelsource.applycasing.md
new file mode 100644
index 00000000000..fb8f9c11005
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-model.lexicalmodelsource.applycasing.md
@@ -0,0 +1,15 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-model](./kmc-model.md) > [LexicalModelSource](./kmc-model.lexicalmodelsource.md) > [applyCasing](./kmc-model.lexicalmodelsource.applycasing.md)
+
+## LexicalModelSource.applyCasing property
+
+Specifies the casing rules for a language. Should implement three casing forms: - 'lower' -- a fully-lowercased version of the text appropriate for the language's use of the writing system. - 'upper' -- a fully-uppercased version of the text - 'initial' -- a version preserving the input casing aside from the initial character, which is uppercased (like with proper nouns and sentence-initial words in English sentences.)
+
+This is only utilized if `languageUsesCasing` is defined and set to `true`. 14.0
+
+**Signature:**
+
+```typescript
+readonly applyCasing?: CasingFunction;
+```
diff --git a/developer/docs/help/reference/api/kmc-model.lexicalmodelsource.languageusescasing.md b/developer/docs/help/reference/api/kmc-model.lexicalmodelsource.languageusescasing.md
new file mode 100644
index 00000000000..51d899d4748
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-model.lexicalmodelsource.languageusescasing.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-model](./kmc-model.md) > [LexicalModelSource](./kmc-model.lexicalmodelsource.md) > [languageUsesCasing](./kmc-model.lexicalmodelsource.languageusescasing.md)
+
+## LexicalModelSource.languageUsesCasing property
+
+When set to `true`, suggestions will attempt to match the case of the input text even if the lexicon entries use a different casing scheme due to search term keying effects. 14.0
+
+**Signature:**
+
+```typescript
+readonly languageUsesCasing?: boolean;
+```
diff --git a/developer/docs/help/reference/api/kmc-model.lexicalmodelsource.md b/developer/docs/help/reference/api/kmc-model.lexicalmodelsource.md
new file mode 100644
index 00000000000..f3f88cfd6fe
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-model.lexicalmodelsource.md
@@ -0,0 +1,27 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-model](./kmc-model.md) > [LexicalModelSource](./kmc-model.lexicalmodelsource.md)
+
+## LexicalModelSource interface
+
+Base interface for a lexical model source definition
+
+**Signature:**
+
+```typescript
+export interface LexicalModelSource extends LexicalModelDeclaration
+```
+**Extends:** LexicalModelDeclaration
+
+## Properties
+
+| Property | Modifiers | Type | Description |
+| --- | --- | --- | --- |
+| [applyCasing?](./kmc-model.lexicalmodelsource.applycasing.md) | readonly | CasingFunction |
_(Optional)_ Specifies the casing rules for a language. Should implement three casing forms: - 'lower' -- a fully-lowercased version of the text appropriate for the language's use of the writing system. - 'upper' -- a fully-uppercased version of the text - 'initial' -- a version preserving the input casing aside from the initial character, which is uppercased (like with proper nouns and sentence-initial words in English sentences.)
This is only utilized if languageUsesCasing is defined and set to true. 14.0
|
+| [languageUsesCasing?](./kmc-model.lexicalmodelsource.languageusescasing.md) | readonly | boolean | _(Optional)_ When set to true, suggestions will attempt to match the case of the input text even if the lexicon entries use a different casing scheme due to search term keying effects. 14.0 |
+| [punctuation?](./kmc-model.lexicalmodelsource.punctuation.md) | readonly | LexicalModelPunctuation | _(Optional)_ Punctuation and spacing suggested by the model. |
+| [rootClass?](./kmc-model.lexicalmodelsource.rootclass.md) | readonly | string | _(Optional)_ The name of the type to instantiate (without parameters) as the base object for a custom predictive model. |
+| [searchTermToKey?](./kmc-model.lexicalmodelsource.searchtermtokey.md) | readonly | [WordformToKeySpec](./kmc-model.wordformtokeyspec.md) | _(Optional)_ How to simplify words, to convert them into simplified search keys This often involves removing accents, lowercasing, etc. |
+| [sources](./kmc-model.lexicalmodelsource.sources.md) | readonly | Array<string> | |
+| [wordBreaker?](./kmc-model.lexicalmodelsource.wordbreaker.md) | readonly | [WordBreakerSpec](./kmc-model.wordbreakerspec.md) \| [SimpleWordBreakerSpec](./kmc-model.simplewordbreakerspec.md) |
_(Optional)_ Which word breaker to use. Choose from:
- 'default' -- breaks according to Unicode UAX \#29 §4.1 Default Word Boundary Specification, which works well for \*most\* languages. - 'ascii' -- a very simple word breaker, for demonstration purposes only. - word breaking function -- provide your own function that breaks words. - class-based word-breaker - may be supported in the future.
|
+
diff --git a/developer/docs/help/reference/api/kmc-model.lexicalmodelsource.punctuation.md b/developer/docs/help/reference/api/kmc-model.lexicalmodelsource.punctuation.md
new file mode 100644
index 00000000000..be062d49237
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-model.lexicalmodelsource.punctuation.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-model](./kmc-model.md) > [LexicalModelSource](./kmc-model.lexicalmodelsource.md) > [punctuation](./kmc-model.lexicalmodelsource.punctuation.md)
+
+## LexicalModelSource.punctuation property
+
+Punctuation and spacing suggested by the model.
+
+**Signature:**
+
+```typescript
+readonly punctuation?: LexicalModelPunctuation;
+```
diff --git a/developer/docs/help/reference/api/kmc-model.lexicalmodelsource.rootclass.md b/developer/docs/help/reference/api/kmc-model.lexicalmodelsource.rootclass.md
new file mode 100644
index 00000000000..2e4371eed16
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-model.lexicalmodelsource.rootclass.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-model](./kmc-model.md) > [LexicalModelSource](./kmc-model.lexicalmodelsource.md) > [rootClass](./kmc-model.lexicalmodelsource.rootclass.md)
+
+## LexicalModelSource.rootClass property
+
+The name of the type to instantiate (without parameters) as the base object for a custom predictive model.
+
+**Signature:**
+
+```typescript
+readonly rootClass?: string;
+```
diff --git a/developer/docs/help/reference/api/kmc-model.lexicalmodelsource.searchtermtokey.md b/developer/docs/help/reference/api/kmc-model.lexicalmodelsource.searchtermtokey.md
new file mode 100644
index 00000000000..ca9d0d8ae8e
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-model.lexicalmodelsource.searchtermtokey.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-model](./kmc-model.md) > [LexicalModelSource](./kmc-model.lexicalmodelsource.md) > [searchTermToKey](./kmc-model.lexicalmodelsource.searchtermtokey.md)
+
+## LexicalModelSource.searchTermToKey property
+
+How to simplify words, to convert them into simplified search keys This often involves removing accents, lowercasing, etc.
+
+**Signature:**
+
+```typescript
+readonly searchTermToKey?: WordformToKeySpec;
+```
diff --git a/developer/docs/help/reference/api/kmc-model.lexicalmodelsource.sources.md b/developer/docs/help/reference/api/kmc-model.lexicalmodelsource.sources.md
new file mode 100644
index 00000000000..6d11c0d372f
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-model.lexicalmodelsource.sources.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-model](./kmc-model.md) > [LexicalModelSource](./kmc-model.lexicalmodelsource.md) > [sources](./kmc-model.lexicalmodelsource.sources.md)
+
+## LexicalModelSource.sources property
+
+**Signature:**
+
+```typescript
+readonly sources: Array;
+```
diff --git a/developer/docs/help/reference/api/kmc-model.lexicalmodelsource.wordbreaker.md b/developer/docs/help/reference/api/kmc-model.lexicalmodelsource.wordbreaker.md
new file mode 100644
index 00000000000..c8f9053d263
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-model.lexicalmodelsource.wordbreaker.md
@@ -0,0 +1,15 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-model](./kmc-model.md) > [LexicalModelSource](./kmc-model.lexicalmodelsource.md) > [wordBreaker](./kmc-model.lexicalmodelsource.wordbreaker.md)
+
+## LexicalModelSource.wordBreaker property
+
+Which word breaker to use. Choose from:
+
+- 'default' -- breaks according to Unicode UAX \#29 §4.1 Default Word Boundary Specification, which works well for \*most\* languages. - 'ascii' -- a very simple word breaker, for demonstration purposes only. - word breaking function -- provide your own function that breaks words. - class-based word-breaker - may be supported in the future.
+
+**Signature:**
+
+```typescript
+readonly wordBreaker?: WordBreakerSpec | SimpleWordBreakerSpec;
+```
diff --git a/developer/docs/help/reference/api/kmc-model.md b/developer/docs/help/reference/api/kmc-model.md
new file mode 100644
index 00000000000..fa0d8688436
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-model.md
@@ -0,0 +1,30 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-model](./kmc-model.md)
+
+## kmc-model package
+
+## Classes
+
+| Class | Description |
+| --- | --- |
+| [LexicalModelCompiler](./kmc-model.lexicalmodelcompiler.md) | Compiles a .model.ts file to a .model.js. The compiler does not read or write from filesystem or network directly, but relies on callbacks for all external IO. |
+
+## Interfaces
+
+| Interface | Description |
+| --- | --- |
+| [LexicalModelCompilerArtifacts](./kmc-model.lexicalmodelcompilerartifacts.md) | Internal in-memory build artifacts from a successful compilation |
+| [LexicalModelCompilerResult](./kmc-model.lexicalmodelcompilerresult.md) | Build artifacts from the lexical model compiler |
+| [LexicalModelSource](./kmc-model.lexicalmodelsource.md) | Base interface for a lexical model source definition |
+| [WordBreakerSpec](./kmc-model.wordbreakerspec.md) |
Keyman 14.0+ word breaker specification:
Can support all old word breaking specification, but can also be extended with options.
14.0
|
+
+## Type Aliases
+
+| Type Alias | Description |
+| --- | --- |
+| [CasedWordformToKeySpec](./kmc-model.casedwordformtokeyspec.md) | Simplifies input text to facilitate finding entries within a lexical model's lexicon, using the model's applyCasing function to assist in the keying process. 14.0 |
+| [SimpleWordBreakerSpec](./kmc-model.simplewordbreakerspec.md) |
Simplified word breaker specification.
11.0
|
+| [SimpleWordformToKeySpec](./kmc-model.simplewordformtokeyspec.md) | Simplifies input text to facilitate finding entries within a lexical model's lexicon. 11.0 |
+| [WordformToKeySpec](./kmc-model.wordformtokeyspec.md) | Simplifies input text to facilitate finding entries within a lexical model's lexicon. |
+
diff --git a/developer/docs/help/reference/api/kmc-model.simplewordbreakerspec.md b/developer/docs/help/reference/api/kmc-model.simplewordbreakerspec.md
new file mode 100644
index 00000000000..420a61218ea
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-model.simplewordbreakerspec.md
@@ -0,0 +1,15 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-model](./kmc-model.md) > [SimpleWordBreakerSpec](./kmc-model.simplewordbreakerspec.md)
+
+## SimpleWordBreakerSpec type
+
+Simplified word breaker specification.
+
+ 11.0
+
+**Signature:**
+
+```typescript
+export type SimpleWordBreakerSpec = 'default' | 'ascii' | WordBreakingFunction;
+```
diff --git a/developer/docs/help/reference/api/kmc-model.simplewordformtokeyspec.md b/developer/docs/help/reference/api/kmc-model.simplewordformtokeyspec.md
new file mode 100644
index 00000000000..65e1cfb656d
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-model.simplewordformtokeyspec.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-model](./kmc-model.md) > [SimpleWordformToKeySpec](./kmc-model.simplewordformtokeyspec.md)
+
+## SimpleWordformToKeySpec type
+
+Simplifies input text to facilitate finding entries within a lexical model's lexicon. 11.0
+
+**Signature:**
+
+```typescript
+export type SimpleWordformToKeySpec = (term: string) => string;
+```
diff --git a/developer/docs/help/reference/api/kmc-model.wordbreakerspec.joinwordsat.md b/developer/docs/help/reference/api/kmc-model.wordbreakerspec.joinwordsat.md
new file mode 100644
index 00000000000..8a5c7d7e683
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-model.wordbreakerspec.joinwordsat.md
@@ -0,0 +1,17 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-model](./kmc-model.md) > [WordBreakerSpec](./kmc-model.wordbreakerspec.md) > [joinWordsAt](./kmc-model.wordbreakerspec.joinwordsat.md)
+
+## WordBreakerSpec.joinWordsAt property
+
+If present, joins words that were split by the word breaker together at the given strings. e.g.,
+
+joinWordsAt: \['-'\] // to keep hyphenated items together
+
+ 14.0
+
+**Signature:**
+
+```typescript
+readonly joinWordsAt?: string[];
+```
diff --git a/developer/docs/help/reference/api/kmc-model.wordbreakerspec.md b/developer/docs/help/reference/api/kmc-model.wordbreakerspec.md
new file mode 100644
index 00000000000..564f4ee8d57
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-model.wordbreakerspec.md
@@ -0,0 +1,26 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-model](./kmc-model.md) > [WordBreakerSpec](./kmc-model.wordbreakerspec.md)
+
+## WordBreakerSpec interface
+
+Keyman 14.0+ word breaker specification:
+
+Can support all old word breaking specification, but can also be extended with options.
+
+ 14.0
+
+**Signature:**
+
+```typescript
+export interface WordBreakerSpec
+```
+
+## Properties
+
+| Property | Modifiers | Type | Description |
+| --- | --- | --- | --- |
+| [joinWordsAt?](./kmc-model.wordbreakerspec.joinwordsat.md) | readonly | string\[\] |
_(Optional)_ If present, joins words that were split by the word breaker together at the given strings. e.g.,
joinWordsAt: \['-'\] // to keep hyphenated items together
_(Optional)_ Overrides word splitting behaviour for certain scripts. For example, specifing that spaces break words in certain South-East Asian scripts that otherwise do not use spaces.
14.0
|
+| [use](./kmc-model.wordbreakerspec.use.md) | readonly | [SimpleWordBreakerSpec](./kmc-model.simplewordbreakerspec.md) | |
+
diff --git a/developer/docs/help/reference/api/kmc-model.wordbreakerspec.overridescriptdefaults.md b/developer/docs/help/reference/api/kmc-model.wordbreakerspec.overridescriptdefaults.md
new file mode 100644
index 00000000000..4c21721cc9c
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-model.wordbreakerspec.overridescriptdefaults.md
@@ -0,0 +1,15 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-model](./kmc-model.md) > [WordBreakerSpec](./kmc-model.wordbreakerspec.md) > [overrideScriptDefaults](./kmc-model.wordbreakerspec.overridescriptdefaults.md)
+
+## WordBreakerSpec.overrideScriptDefaults property
+
+Overrides word splitting behaviour for certain scripts. For example, specifing that spaces break words in certain South-East Asian scripts that otherwise do not use spaces.
+
+ 14.0
+
+**Signature:**
+
+```typescript
+readonly overrideScriptDefaults?: OverrideScriptDefaults;
+```
diff --git a/developer/docs/help/reference/api/kmc-model.wordbreakerspec.use.md b/developer/docs/help/reference/api/kmc-model.wordbreakerspec.use.md
new file mode 100644
index 00000000000..5e3844226ad
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-model.wordbreakerspec.use.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-model](./kmc-model.md) > [WordBreakerSpec](./kmc-model.wordbreakerspec.md) > [use](./kmc-model.wordbreakerspec.use.md)
+
+## WordBreakerSpec.use property
+
+**Signature:**
+
+```typescript
+readonly use: SimpleWordBreakerSpec;
+```
diff --git a/developer/docs/help/reference/api/kmc-model.wordformtokeyspec.md b/developer/docs/help/reference/api/kmc-model.wordformtokeyspec.md
new file mode 100644
index 00000000000..5a2b4951a3e
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-model.wordformtokeyspec.md
@@ -0,0 +1,15 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-model](./kmc-model.md) > [WordformToKeySpec](./kmc-model.wordformtokeyspec.md)
+
+## WordformToKeySpec type
+
+Simplifies input text to facilitate finding entries within a lexical model's lexicon.
+
+**Signature:**
+
+```typescript
+export type WordformToKeySpec = SimpleWordformToKeySpec | CasedWordformToKeySpec;
+```
+**References:** [SimpleWordformToKeySpec](./kmc-model.simplewordformtokeyspec.md), [CasedWordformToKeySpec](./kmc-model.casedwordformtokeyspec.md)
+
diff --git a/developer/docs/help/reference/api/kmc-package.kmpcompiler.init.md b/developer/docs/help/reference/api/kmc-package.kmpcompiler.init.md
new file mode 100644
index 00000000000..c1010a494ee
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-package.kmpcompiler.init.md
@@ -0,0 +1,27 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-package](./kmc-package.md) > [KmpCompiler](./kmc-package.kmpcompiler.md) > [init](./kmc-package.kmpcompiler.init.md)
+
+## KmpCompiler.init() method
+
+Initialize the compiler. Copies options.
+
+**Signature:**
+
+```typescript
+init(callbacks: CompilerCallbacks, options: KmpCompilerOptions): Promise;
+```
+
+## Parameters
+
+| Parameter | Type | Description |
+| --- | --- | --- |
+| callbacks | CompilerCallbacks | Callbacks for external interfaces, including message reporting and file io |
+| options | [KmpCompilerOptions](./kmc-package.kmpcompileroptions.md) | Compiler options |
+
+**Returns:**
+
+Promise<boolean>
+
+false if initialization fails
+
diff --git a/developer/docs/help/reference/api/kmc-package.kmpcompiler.md b/developer/docs/help/reference/api/kmc-package.kmpcompiler.md
new file mode 100644
index 00000000000..f23b5025fa0
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-package.kmpcompiler.md
@@ -0,0 +1,29 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-package](./kmc-package.md) > [KmpCompiler](./kmc-package.kmpcompiler.md)
+
+## KmpCompiler class
+
+Compiles a .kps file to a .kmp archive. The compiler does not read or write from filesystem or network directly, but relies on callbacks for all external IO.
+
+**Signature:**
+
+```typescript
+export declare class KmpCompiler implements KeymanCompiler
+```
+**Implements:** KeymanCompiler
+
+## Properties
+
+| Property | Modifiers | Type | Description |
+| --- | --- | --- | --- |
+| [normalizePath](./kmc-package.kmpcompiler.normalizepath.md) | readonly | (path: string) => string | |
+
+## Methods
+
+| Method | Modifiers | Description |
+| --- | --- | --- |
+| [init(callbacks, options)](./kmc-package.kmpcompiler.init.md) | | Initialize the compiler. Copies options. |
+| [run(inputFilename, outputFilename)](./kmc-package.kmpcompiler.run.md) | | Compiles a .kps file to .kmp file. Returns an object containing binary artifacts on success. The files are passed in by name, and the compiler will use callbacks as passed to the [KmpCompiler.init()](./kmc-package.kmpcompiler.init.md) function to read any input files by disk. |
+| [write(artifacts)](./kmc-package.kmpcompiler.write.md) | |
Write artifacts from a successful compile to disk, via callbacks methods. The artifacts written may include:
- .kmp file - binary keyboard package used by Keyman on desktop and touch platforms
|
+
diff --git a/developer/docs/help/reference/api/kmc-package.kmpcompiler.normalizepath.md b/developer/docs/help/reference/api/kmc-package.kmpcompiler.normalizepath.md
new file mode 100644
index 00000000000..cf754363740
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-package.kmpcompiler.normalizepath.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-package](./kmc-package.md) > [KmpCompiler](./kmc-package.kmpcompiler.md) > [normalizePath](./kmc-package.kmpcompiler.normalizepath.md)
+
+## KmpCompiler.normalizePath property
+
+**Signature:**
+
+```typescript
+readonly normalizePath: (path: string) => string;
+```
diff --git a/developer/docs/help/reference/api/kmc-package.kmpcompiler.run.md b/developer/docs/help/reference/api/kmc-package.kmpcompiler.run.md
new file mode 100644
index 00000000000..4b584dd7979
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-package.kmpcompiler.run.md
@@ -0,0 +1,27 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-package](./kmc-package.md) > [KmpCompiler](./kmc-package.kmpcompiler.md) > [run](./kmc-package.kmpcompiler.run.md)
+
+## KmpCompiler.run() method
+
+Compiles a .kps file to .kmp file. Returns an object containing binary artifacts on success. The files are passed in by name, and the compiler will use callbacks as passed to the [KmpCompiler.init()](./kmc-package.kmpcompiler.init.md) function to read any input files by disk.
+
+**Signature:**
+
+```typescript
+run(inputFilename: string, outputFilename?: string): Promise;
+```
+
+## Parameters
+
+| Parameter | Type | Description |
+| --- | --- | --- |
+| inputFilename | string | |
+| outputFilename | string | _(Optional)_ |
+
+**Returns:**
+
+Promise<[KmpCompilerResult](./kmc-package.kmpcompilerresult.md)>
+
+Binary artifacts on success, null on failure.
+
diff --git a/developer/docs/help/reference/api/kmc-package.kmpcompiler.write.md b/developer/docs/help/reference/api/kmc-package.kmpcompiler.write.md
new file mode 100644
index 00000000000..c4878571df5
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-package.kmpcompiler.write.md
@@ -0,0 +1,28 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-package](./kmc-package.md) > [KmpCompiler](./kmc-package.kmpcompiler.md) > [write](./kmc-package.kmpcompiler.write.md)
+
+## KmpCompiler.write() method
+
+Write artifacts from a successful compile to disk, via callbacks methods. The artifacts written may include:
+
+- .kmp file - binary keyboard package used by Keyman on desktop and touch platforms
+
+**Signature:**
+
+```typescript
+write(artifacts: KmpCompilerArtifacts): Promise;
+```
+
+## Parameters
+
+| Parameter | Type | Description |
+| --- | --- | --- |
+| artifacts | [KmpCompilerArtifacts](./kmc-package.kmpcompilerartifacts.md) | object containing artifact binary data to write out |
+
+**Returns:**
+
+Promise<boolean>
+
+true on success
+
diff --git a/developer/docs/help/reference/api/kmc-package.kmpcompilerartifacts.kmp.md b/developer/docs/help/reference/api/kmc-package.kmpcompilerartifacts.kmp.md
new file mode 100644
index 00000000000..612daab90f5
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-package.kmpcompilerartifacts.kmp.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-package](./kmc-package.md) > [KmpCompilerArtifacts](./kmc-package.kmpcompilerartifacts.md) > [kmp](./kmc-package.kmpcompilerartifacts.kmp.md)
+
+## KmpCompilerArtifacts.kmp property
+
+Binary keyboard package filedata and filename - installable into Keyman desktop and mobile projects
+
+**Signature:**
+
+```typescript
+kmp: KeymanCompilerArtifact;
+```
diff --git a/developer/docs/help/reference/api/kmc-package.kmpcompilerartifacts.md b/developer/docs/help/reference/api/kmc-package.kmpcompilerartifacts.md
new file mode 100644
index 00000000000..1a7f6f883ea
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-package.kmpcompilerartifacts.md
@@ -0,0 +1,21 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-package](./kmc-package.md) > [KmpCompilerArtifacts](./kmc-package.kmpcompilerartifacts.md)
+
+## KmpCompilerArtifacts interface
+
+Internal in-memory build artifacts from a successful compilation
+
+**Signature:**
+
+```typescript
+export interface KmpCompilerArtifacts extends KeymanCompilerArtifacts
+```
+**Extends:** KeymanCompilerArtifacts
+
+## Properties
+
+| Property | Modifiers | Type | Description |
+| --- | --- | --- | --- |
+| [kmp](./kmc-package.kmpcompilerartifacts.kmp.md) | | KeymanCompilerArtifact | Binary keyboard package filedata and filename - installable into Keyman desktop and mobile projects |
+
diff --git a/developer/docs/help/reference/api/kmc-package.kmpcompileroptions.md b/developer/docs/help/reference/api/kmc-package.kmpcompileroptions.md
new file mode 100644
index 00000000000..763eb040004
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-package.kmpcompileroptions.md
@@ -0,0 +1,15 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-package](./kmc-package.md) > [KmpCompilerOptions](./kmc-package.kmpcompileroptions.md)
+
+## KmpCompilerOptions interface
+
+Options for the .kps compiler
+
+**Signature:**
+
+```typescript
+export interface KmpCompilerOptions extends CompilerOptions
+```
+**Extends:** CompilerOptions
+
diff --git a/developer/docs/help/reference/api/kmc-package.kmpcompilerresult.artifacts.md b/developer/docs/help/reference/api/kmc-package.kmpcompilerresult.artifacts.md
new file mode 100644
index 00000000000..4905af4ac7c
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-package.kmpcompilerresult.artifacts.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-package](./kmc-package.md) > [KmpCompilerResult](./kmc-package.kmpcompilerresult.md) > [artifacts](./kmc-package.kmpcompilerresult.artifacts.md)
+
+## KmpCompilerResult.artifacts property
+
+Internal in-memory build artifacts from a successful compilation. Caller can write these to disk with [KmpCompiler.write()](./kmc-package.kmpcompiler.write.md)
+
+**Signature:**
+
+```typescript
+artifacts: KmpCompilerArtifacts;
+```
diff --git a/developer/docs/help/reference/api/kmc-package.kmpcompilerresult.md b/developer/docs/help/reference/api/kmc-package.kmpcompilerresult.md
new file mode 100644
index 00000000000..3371a01f9ef
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-package.kmpcompilerresult.md
@@ -0,0 +1,21 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-package](./kmc-package.md) > [KmpCompilerResult](./kmc-package.kmpcompilerresult.md)
+
+## KmpCompilerResult interface
+
+Build artifacts from the .kps compiler
+
+**Signature:**
+
+```typescript
+export interface KmpCompilerResult extends KeymanCompilerResult
+```
+**Extends:** KeymanCompilerResult
+
+## Properties
+
+| Property | Modifiers | Type | Description |
+| --- | --- | --- | --- |
+| [artifacts](./kmc-package.kmpcompilerresult.artifacts.md) | | [KmpCompilerArtifacts](./kmc-package.kmpcompilerartifacts.md) | Internal in-memory build artifacts from a successful compilation. Caller can write these to disk with [KmpCompiler.write()](./kmc-package.kmpcompiler.write.md) |
+
diff --git a/developer/docs/help/reference/api/kmc-package.md b/developer/docs/help/reference/api/kmc-package.md
new file mode 100644
index 00000000000..53ab00c1907
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-package.md
@@ -0,0 +1,25 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-package](./kmc-package.md)
+
+## kmc-package package
+
+## Classes
+
+| Class | Description |
+| --- | --- |
+| [KmpCompiler](./kmc-package.kmpcompiler.md) | Compiles a .kps file to a .kmp archive. The compiler does not read or write from filesystem or network directly, but relies on callbacks for all external IO. |
+| [WindowsPackageInstallerCompiler](./kmc-package.windowspackageinstallercompiler.md) | Compiles a .kps file to a .exe installer. The compiler does not read or write from filesystem or network directly, but relies on callbacks for all external IO. |
+
+## Interfaces
+
+| Interface | Description |
+| --- | --- |
+| [KmpCompilerArtifacts](./kmc-package.kmpcompilerartifacts.md) | Internal in-memory build artifacts from a successful compilation |
+| [KmpCompilerOptions](./kmc-package.kmpcompileroptions.md) | Options for the .kps compiler |
+| [KmpCompilerResult](./kmc-package.kmpcompilerresult.md) | Build artifacts from the .kps compiler |
+| [WindowsPackageInstallerCompilerArtifacts](./kmc-package.windowspackageinstallercompilerartifacts.md) | Internal in-memory build artifacts from a successful compilation |
+| [WindowsPackageInstallerCompilerOptions](./kmc-package.windowspackageinstallercompileroptions.md) | Options for the .kps Windows package installer compiler |
+| [WindowsPackageInstallerCompilerResult](./kmc-package.windowspackageinstallercompilerresult.md) | Build artifacts from the .kps Windows package installer compiler |
+| [WindowsPackageInstallerSources](./kmc-package.windowspackageinstallersources.md) | Sources and metadata for the Windows package installer compiler |
+
diff --git a/developer/docs/help/reference/api/kmc-package.windowspackageinstallercompiler.init.md b/developer/docs/help/reference/api/kmc-package.windowspackageinstallercompiler.init.md
new file mode 100644
index 00000000000..160d3265414
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-package.windowspackageinstallercompiler.init.md
@@ -0,0 +1,27 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-package](./kmc-package.md) > [WindowsPackageInstallerCompiler](./kmc-package.windowspackageinstallercompiler.md) > [init](./kmc-package.windowspackageinstallercompiler.init.md)
+
+## WindowsPackageInstallerCompiler.init() method
+
+Initialize the compiler. Copies options.
+
+**Signature:**
+
+```typescript
+init(callbacks: CompilerCallbacks, options: WindowsPackageInstallerCompilerOptions): Promise;
+```
+
+## Parameters
+
+| Parameter | Type | Description |
+| --- | --- | --- |
+| callbacks | CompilerCallbacks | Callbacks for external interfaces, including message reporting and file io |
+| options | [WindowsPackageInstallerCompilerOptions](./kmc-package.windowspackageinstallercompileroptions.md) | Compiler options |
+
+**Returns:**
+
+Promise<boolean>
+
+false if initialization fails
+
diff --git a/developer/docs/help/reference/api/kmc-package.windowspackageinstallercompiler.md b/developer/docs/help/reference/api/kmc-package.windowspackageinstallercompiler.md
new file mode 100644
index 00000000000..c691fece706
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-package.windowspackageinstallercompiler.md
@@ -0,0 +1,23 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-package](./kmc-package.md) > [WindowsPackageInstallerCompiler](./kmc-package.windowspackageinstallercompiler.md)
+
+## WindowsPackageInstallerCompiler class
+
+Compiles a .kps file to a .exe installer. The compiler does not read or write from filesystem or network directly, but relies on callbacks for all external IO.
+
+**Signature:**
+
+```typescript
+export declare class WindowsPackageInstallerCompiler implements KeymanCompiler
+```
+**Implements:** KeymanCompiler
+
+## Methods
+
+| Method | Modifiers | Description |
+| --- | --- | --- |
+| [init(callbacks, options)](./kmc-package.windowspackageinstallercompiler.init.md) | | Initialize the compiler. Copies options. |
+| [run(inputFilename, outputFilename)](./kmc-package.windowspackageinstallercompiler.run.md) | | Compiles a .kps file to .exe Windows package installer file. Returns an object containing binary artifacts on success. The files are passed in by name, and the compiler will use callbacks as passed to the [WindowsPackageInstallerCompiler.init()](./kmc-package.windowspackageinstallercompiler.init.md) function to read any input files by disk. |
+| [write(artifacts)](./kmc-package.windowspackageinstallercompiler.write.md) | |
Write artifacts from a successful compile to disk, via callbacks methods. The artifacts written may include:
- .exe file - binary Windows package installer executable file
|
+
diff --git a/developer/docs/help/reference/api/kmc-package.windowspackageinstallercompiler.run.md b/developer/docs/help/reference/api/kmc-package.windowspackageinstallercompiler.run.md
new file mode 100644
index 00000000000..1e79d3d9124
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-package.windowspackageinstallercompiler.run.md
@@ -0,0 +1,27 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-package](./kmc-package.md) > [WindowsPackageInstallerCompiler](./kmc-package.windowspackageinstallercompiler.md) > [run](./kmc-package.windowspackageinstallercompiler.run.md)
+
+## WindowsPackageInstallerCompiler.run() method
+
+Compiles a .kps file to .exe Windows package installer file. Returns an object containing binary artifacts on success. The files are passed in by name, and the compiler will use callbacks as passed to the [WindowsPackageInstallerCompiler.init()](./kmc-package.windowspackageinstallercompiler.init.md) function to read any input files by disk.
+
+**Signature:**
+
+```typescript
+run(inputFilename: string, outputFilename?: string): Promise;
+```
+
+## Parameters
+
+| Parameter | Type | Description |
+| --- | --- | --- |
+| inputFilename | string | |
+| outputFilename | string | _(Optional)_ |
+
+**Returns:**
+
+Promise<[WindowsPackageInstallerCompilerResult](./kmc-package.windowspackageinstallercompilerresult.md)>
+
+Binary artifacts on success, null on failure.
+
diff --git a/developer/docs/help/reference/api/kmc-package.windowspackageinstallercompiler.write.md b/developer/docs/help/reference/api/kmc-package.windowspackageinstallercompiler.write.md
new file mode 100644
index 00000000000..487696c5292
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-package.windowspackageinstallercompiler.write.md
@@ -0,0 +1,28 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-package](./kmc-package.md) > [WindowsPackageInstallerCompiler](./kmc-package.windowspackageinstallercompiler.md) > [write](./kmc-package.windowspackageinstallercompiler.write.md)
+
+## WindowsPackageInstallerCompiler.write() method
+
+Write artifacts from a successful compile to disk, via callbacks methods. The artifacts written may include:
+
+- .exe file - binary Windows package installer executable file
+
+**Signature:**
+
+```typescript
+write(artifacts: WindowsPackageInstallerCompilerArtifacts): Promise;
+```
+
+## Parameters
+
+| Parameter | Type | Description |
+| --- | --- | --- |
+| artifacts | [WindowsPackageInstallerCompilerArtifacts](./kmc-package.windowspackageinstallercompilerartifacts.md) | object containing artifact binary data to write out |
+
+**Returns:**
+
+Promise<boolean>
+
+true on success
+
diff --git a/developer/docs/help/reference/api/kmc-package.windowspackageinstallercompilerartifacts.exe.md b/developer/docs/help/reference/api/kmc-package.windowspackageinstallercompilerartifacts.exe.md
new file mode 100644
index 00000000000..1fd3e8e522e
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-package.windowspackageinstallercompilerartifacts.exe.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-package](./kmc-package.md) > [WindowsPackageInstallerCompilerArtifacts](./kmc-package.windowspackageinstallercompilerartifacts.md) > [exe](./kmc-package.windowspackageinstallercompilerartifacts.exe.md)
+
+## WindowsPackageInstallerCompilerArtifacts.exe property
+
+Binary package installer filedata and filename - installable into Keyman desktop and mobile projects
+
+**Signature:**
+
+```typescript
+exe: KeymanCompilerArtifact;
+```
diff --git a/developer/docs/help/reference/api/kmc-package.windowspackageinstallercompilerartifacts.md b/developer/docs/help/reference/api/kmc-package.windowspackageinstallercompilerartifacts.md
new file mode 100644
index 00000000000..2a21d3a3116
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-package.windowspackageinstallercompilerartifacts.md
@@ -0,0 +1,21 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-package](./kmc-package.md) > [WindowsPackageInstallerCompilerArtifacts](./kmc-package.windowspackageinstallercompilerartifacts.md)
+
+## WindowsPackageInstallerCompilerArtifacts interface
+
+Internal in-memory build artifacts from a successful compilation
+
+**Signature:**
+
+```typescript
+export interface WindowsPackageInstallerCompilerArtifacts extends KeymanCompilerArtifacts
+```
+**Extends:** KeymanCompilerArtifacts
+
+## Properties
+
+| Property | Modifiers | Type | Description |
+| --- | --- | --- | --- |
+| [exe](./kmc-package.windowspackageinstallercompilerartifacts.exe.md) | | KeymanCompilerArtifact | Binary package installer filedata and filename - installable into Keyman desktop and mobile projects |
+
diff --git a/developer/docs/help/reference/api/kmc-package.windowspackageinstallercompileroptions.md b/developer/docs/help/reference/api/kmc-package.windowspackageinstallercompileroptions.md
new file mode 100644
index 00000000000..b0aed5a5052
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-package.windowspackageinstallercompileroptions.md
@@ -0,0 +1,21 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-package](./kmc-package.md) > [WindowsPackageInstallerCompilerOptions](./kmc-package.windowspackageinstallercompileroptions.md)
+
+## WindowsPackageInstallerCompilerOptions interface
+
+Options for the .kps Windows package installer compiler
+
+**Signature:**
+
+```typescript
+export interface WindowsPackageInstallerCompilerOptions extends KmpCompilerOptions
+```
+**Extends:** [KmpCompilerOptions](./kmc-package.kmpcompileroptions.md)
+
+## Properties
+
+| Property | Modifiers | Type | Description |
+| --- | --- | --- | --- |
+| [sources](./kmc-package.windowspackageinstallercompileroptions.sources.md) | | [WindowsPackageInstallerSources](./kmc-package.windowspackageinstallersources.md) | Sources and metadata for the Windows package installer compiler |
+
diff --git a/developer/docs/help/reference/api/kmc-package.windowspackageinstallercompileroptions.sources.md b/developer/docs/help/reference/api/kmc-package.windowspackageinstallercompileroptions.sources.md
new file mode 100644
index 00000000000..de1b74dbd7a
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-package.windowspackageinstallercompileroptions.sources.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-package](./kmc-package.md) > [WindowsPackageInstallerCompilerOptions](./kmc-package.windowspackageinstallercompileroptions.md) > [sources](./kmc-package.windowspackageinstallercompileroptions.sources.md)
+
+## WindowsPackageInstallerCompilerOptions.sources property
+
+Sources and metadata for the Windows package installer compiler
+
+**Signature:**
+
+```typescript
+sources: WindowsPackageInstallerSources;
+```
diff --git a/developer/docs/help/reference/api/kmc-package.windowspackageinstallercompilerresult.artifacts.md b/developer/docs/help/reference/api/kmc-package.windowspackageinstallercompilerresult.artifacts.md
new file mode 100644
index 00000000000..d1ea7e4a5a3
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-package.windowspackageinstallercompilerresult.artifacts.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-package](./kmc-package.md) > [WindowsPackageInstallerCompilerResult](./kmc-package.windowspackageinstallercompilerresult.md) > [artifacts](./kmc-package.windowspackageinstallercompilerresult.artifacts.md)
+
+## WindowsPackageInstallerCompilerResult.artifacts property
+
+Internal in-memory build artifacts from a successful compilation. Caller can write these to disk with [WindowsPackageInstallerCompiler.write()](./kmc-package.windowspackageinstallercompiler.write.md)
+
+**Signature:**
+
+```typescript
+artifacts: WindowsPackageInstallerCompilerArtifacts;
+```
diff --git a/developer/docs/help/reference/api/kmc-package.windowspackageinstallercompilerresult.md b/developer/docs/help/reference/api/kmc-package.windowspackageinstallercompilerresult.md
new file mode 100644
index 00000000000..de9701b148c
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-package.windowspackageinstallercompilerresult.md
@@ -0,0 +1,21 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-package](./kmc-package.md) > [WindowsPackageInstallerCompilerResult](./kmc-package.windowspackageinstallercompilerresult.md)
+
+## WindowsPackageInstallerCompilerResult interface
+
+Build artifacts from the .kps Windows package installer compiler
+
+**Signature:**
+
+```typescript
+export interface WindowsPackageInstallerCompilerResult extends KeymanCompilerResult
+```
+**Extends:** KeymanCompilerResult
+
+## Properties
+
+| Property | Modifiers | Type | Description |
+| --- | --- | --- | --- |
+| [artifacts](./kmc-package.windowspackageinstallercompilerresult.artifacts.md) | | [WindowsPackageInstallerCompilerArtifacts](./kmc-package.windowspackageinstallercompilerartifacts.md) | Internal in-memory build artifacts from a successful compilation. Caller can write these to disk with [WindowsPackageInstallerCompiler.write()](./kmc-package.windowspackageinstallercompiler.write.md) |
+
diff --git a/developer/docs/help/reference/api/kmc-package.windowspackageinstallersources.appname.md b/developer/docs/help/reference/api/kmc-package.windowspackageinstallersources.appname.md
new file mode 100644
index 00000000000..ca6069c3802
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-package.windowspackageinstallersources.appname.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-package](./kmc-package.md) > [WindowsPackageInstallerSources](./kmc-package.windowspackageinstallersources.md) > [appName](./kmc-package.windowspackageinstallersources.appname.md)
+
+## WindowsPackageInstallerSources.appName property
+
+**Signature:**
+
+```typescript
+appName?: string;
+```
diff --git a/developer/docs/help/reference/api/kmc-package.windowspackageinstallersources.licensefilename.md b/developer/docs/help/reference/api/kmc-package.windowspackageinstallersources.licensefilename.md
new file mode 100644
index 00000000000..d96a5a29757
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-package.windowspackageinstallersources.licensefilename.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-package](./kmc-package.md) > [WindowsPackageInstallerSources](./kmc-package.windowspackageinstallersources.md) > [licenseFilename](./kmc-package.windowspackageinstallersources.licensefilename.md)
+
+## WindowsPackageInstallerSources.licenseFilename property
+
+**Signature:**
+
+```typescript
+licenseFilename: string;
+```
diff --git a/developer/docs/help/reference/api/kmc-package.windowspackageinstallersources.md b/developer/docs/help/reference/api/kmc-package.windowspackageinstallersources.md
new file mode 100644
index 00000000000..0130266bad5
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-package.windowspackageinstallersources.md
@@ -0,0 +1,26 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-package](./kmc-package.md) > [WindowsPackageInstallerSources](./kmc-package.windowspackageinstallersources.md)
+
+## WindowsPackageInstallerSources interface
+
+Sources and metadata for the Windows package installer compiler
+
+**Signature:**
+
+```typescript
+export interface WindowsPackageInstallerSources
+```
+
+## Properties
+
+| Property | Modifiers | Type | Description |
+| --- | --- | --- | --- |
+| [appName?](./kmc-package.windowspackageinstallersources.appname.md) | | string | _(Optional)_ |
+| [licenseFilename](./kmc-package.windowspackageinstallersources.licensefilename.md) | | string | |
+| [msiFilename](./kmc-package.windowspackageinstallersources.msifilename.md) | | string | |
+| [setupExeFilename](./kmc-package.windowspackageinstallersources.setupexefilename.md) | | string | |
+| [startDisabled](./kmc-package.windowspackageinstallersources.startdisabled.md) | | boolean | |
+| [startWithConfiguration](./kmc-package.windowspackageinstallersources.startwithconfiguration.md) | | boolean | |
+| [titleImageFilename?](./kmc-package.windowspackageinstallersources.titleimagefilename.md) | | string | _(Optional)_ |
+
diff --git a/developer/docs/help/reference/api/kmc-package.windowspackageinstallersources.msifilename.md b/developer/docs/help/reference/api/kmc-package.windowspackageinstallersources.msifilename.md
new file mode 100644
index 00000000000..f6cc53efbc1
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-package.windowspackageinstallersources.msifilename.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-package](./kmc-package.md) > [WindowsPackageInstallerSources](./kmc-package.windowspackageinstallersources.md) > [msiFilename](./kmc-package.windowspackageinstallersources.msifilename.md)
+
+## WindowsPackageInstallerSources.msiFilename property
+
+**Signature:**
+
+```typescript
+msiFilename: string;
+```
diff --git a/developer/docs/help/reference/api/kmc-package.windowspackageinstallersources.setupexefilename.md b/developer/docs/help/reference/api/kmc-package.windowspackageinstallersources.setupexefilename.md
new file mode 100644
index 00000000000..8e00113ffa5
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-package.windowspackageinstallersources.setupexefilename.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-package](./kmc-package.md) > [WindowsPackageInstallerSources](./kmc-package.windowspackageinstallersources.md) > [setupExeFilename](./kmc-package.windowspackageinstallersources.setupexefilename.md)
+
+## WindowsPackageInstallerSources.setupExeFilename property
+
+**Signature:**
+
+```typescript
+setupExeFilename: string;
+```
diff --git a/developer/docs/help/reference/api/kmc-package.windowspackageinstallersources.startdisabled.md b/developer/docs/help/reference/api/kmc-package.windowspackageinstallersources.startdisabled.md
new file mode 100644
index 00000000000..89ba631c164
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-package.windowspackageinstallersources.startdisabled.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-package](./kmc-package.md) > [WindowsPackageInstallerSources](./kmc-package.windowspackageinstallersources.md) > [startDisabled](./kmc-package.windowspackageinstallersources.startdisabled.md)
+
+## WindowsPackageInstallerSources.startDisabled property
+
+**Signature:**
+
+```typescript
+startDisabled: boolean;
+```
diff --git a/developer/docs/help/reference/api/kmc-package.windowspackageinstallersources.startwithconfiguration.md b/developer/docs/help/reference/api/kmc-package.windowspackageinstallersources.startwithconfiguration.md
new file mode 100644
index 00000000000..6ec14bae7a6
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-package.windowspackageinstallersources.startwithconfiguration.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-package](./kmc-package.md) > [WindowsPackageInstallerSources](./kmc-package.windowspackageinstallersources.md) > [startWithConfiguration](./kmc-package.windowspackageinstallersources.startwithconfiguration.md)
+
+## WindowsPackageInstallerSources.startWithConfiguration property
+
+**Signature:**
+
+```typescript
+startWithConfiguration: boolean;
+```
diff --git a/developer/docs/help/reference/api/kmc-package.windowspackageinstallersources.titleimagefilename.md b/developer/docs/help/reference/api/kmc-package.windowspackageinstallersources.titleimagefilename.md
new file mode 100644
index 00000000000..c663ba44d06
--- /dev/null
+++ b/developer/docs/help/reference/api/kmc-package.windowspackageinstallersources.titleimagefilename.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [@keymanapp/kmc-package](./kmc-package.md) > [WindowsPackageInstallerSources](./kmc-package.windowspackageinstallersources.md) > [titleImageFilename](./kmc-package.windowspackageinstallersources.titleimagefilename.md)
+
+## WindowsPackageInstallerSources.titleImageFilename property
+
+**Signature:**
+
+```typescript
+titleImageFilename?: string;
+```
diff --git a/developer/docs/help/reference/bcp-47.md b/developer/docs/help/reference/bcp-47.md
new file mode 100644
index 00000000000..f5c8a49539e
--- /dev/null
+++ b/developer/docs/help/reference/bcp-47.md
@@ -0,0 +1,113 @@
+---
+title: BCP 47 language tags
+---
+
+Packages and lexical models may reference a [BCP 47 language tag][1]. A BCP
+47 tag is a standard way of referencing a language, used widely in the computer
+industry. Keyman uses BCP 47 tags in a number of areas, including:
+
+* providing language metadata about installed keyboards to the operating system
+ and to applications, so that, for example, spell checking has the correct
+ language, through [package metadata](../guides/distribute/)
+* [linking lexical models to keyboard layouts](../guides/lexical-models/)
+* facilitating searches for keyboards and lexical models on the Keyman site,
+ through a [.keyboard_info file](/developer/cloud/keyboard_info)
+
+A BCP 47 language tag is made up of multiple subtags. There are many possible
+subtags, but only three types are currently used in most places in Keyman
+Developer:
+
+* [language subtag](#toc-the-language-subtag)
+* [script subtag](#toc-the-script-subtag)
+* [region subtag](#toc-the-region-subtag)
+
+BCP 47 tags are case insensitive, but there are conventions for casing which you
+should use for readability; see the subtag descriptions for details.
+
+The following are all examples of valid BCP 47 tags:
+
+* `en`: English
+* `en-US`: English, in United States
+* `km-Khmr-KH`: Khmer, written in the Khmer script, in Cambodia
+* `km-fonipa`: Khmer, transcribed in IPA
+
+### The language subtag
+
+The only required option is the Language subtag, which is an [ISO 639-1][2] or
+[ISO 639-3][3] code.
+
+ISO 639-1 tags are a two-letter code. ISO 639-3 tags are a three-letter code.
+First, try to find your language on the list of two-letter ISO 639-1 codes.
+[This Wikipedia page][4] lists all of the two-letter codes.
+
+If you can't find a two-letter code, you'll need to find the closest
+three-letter code. You can use [Glottolog][5] to search for your language, and
+it will give you an appropriate code. In this example, I searched Glottolog for
+“[Saanich][6]” (name of the First Nations that speak SENĆOŦEN) and found `str`
+as the code for all Straits Salish languages.
+
+The Language subtag is conventionally written in lower case.
+
+The next two subtags are **optional**, however, they allow you to be more
+specific about your language.
+
+### The script subtag
+
+The Script subtag allows you to specify the writing system used in your language
+model or keyboard. If your language only uses one writing system, omit the
+Script subtag.
+
+Otherwise, in cases where a language can be written in many different writing
+systems, you can choose the four letter [ISO 15924][7] script tag that your
+keyboard or lexical model produces.
+
+For example, Plains Cree can either be written in _standard Roman orthography_,
+a **Latin** derived script, or it can be written in _syllabics_, which is part
+of the **Canadian Aboriginal syllabics** family of writing systems. If I wrote a
+keyboard or lexical model that produced syllabics, I would choose `Cans`, as
+that is the **ISO 15924** tag for Canadian Aboriginal syllabics.
+
+The Script subtag is conventionally written in title case - first letter
+capitalized.
+
+### The region subtag
+
+The Region subtag allows you to specify the region your language or dialect is
+spoken in. If your language is only spoken in one region, omit the Region
+subtag.
+
+Otherwise, some languages vary between different regions and countries. In our
+example, SENĆOŦEN describes the language that covers entire W̱SÁNEĆ region, so
+this field may be left blank.
+
+However, large languages, like English, Spanish, or French have quite different
+vocabulary and even different grammatical rules from region to region and
+country to country. For example, the variety of Spanish spoken in Spain
+regularly uses words that are uncommon or even vulgar in both in Mexico, and in
+Latin America. Additionally, regions may have vocabulary that doesn't exist in
+the other regions where the language is spoken.
+
+If I were working with a language specific to one country, I would use the [ISO
+3166-1 alpha-2][8] country code for the region subtag. For example, `ES` for
+Spain or `MX` for Mexico.
+
+However, if I were working with Latin American Spanish (a group of countries), I
+would need to specify Latin America's [UN M49][9] region code. For Latin
+America, its code is `419`. My lexical model would not suggest words that are
+common in Spain, but vulgar in Latin America, however it would predict words
+like "pupupsas" and "chuchitos", which are words that are uncommon in both Spain
+and Mexico.
+
+Another common UN M49 region code is `001` for the whole world.
+
+Alphabetic region subtags are conventionally written in upper case.
+
+[1]: https://en.wikipedia.org/wiki/IETF_language_tag
+[2]: https://en.wikipedia.org/wiki/ISO_639-1
+[3]: https://en.wikipedia.org/wiki/ISO_639-3
+[4]: https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes
+[5]: https://glottolog.org/glottolog/language
+[6]: https://glottolog.org/resource/languoid/id/saan1246
+[7]: https://en.wikipedia.org/wiki/ISO_15924
+[8]: https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2
+[9]: https://en.wikipedia.org/wiki/UN_M49
diff --git a/developer/docs/help/reference/editor-themes.md b/developer/docs/help/reference/editor-themes.md
new file mode 100644
index 00000000000..3a624c923fc
--- /dev/null
+++ b/developer/docs/help/reference/editor-themes.md
@@ -0,0 +1,46 @@
+---
+title: Custom Editor Themes
+---
+
+Keyman Developer embeds the Monaco open source text editor. The
+highlighting and styles for the text editor are customisable with a JSON
+file configured in the [Options dialog](../context/options). The JSON
+file must be a valid [Monaco theme](https://microsoft.github.io/monaco-editor/playground.html#customizing-the-appearence-tokens-and-colors)
+(passed as second parameter to `monaco.editor.defineTheme`).
+
+## Example
+
+```js
+{
+ "base": "vs-dark",
+ "inherit": true,
+ "rules": [
+ { "token": "comment", "foreground": "ffa500", "background": "303030", "fontStyle": "italic underline" },
+ { "token": "comment.js", "foreground": "008800", "fontStyle": "bold" },
+ { "token": "comment.css", "foreground": "0000ff", "fontStyle": "bold", "inherit": false, "background": "808080" }
+ ]
+}
+```
+
+## Keyman keyboard language tokens
+
+The Keyman keyboard language highlighting uses the following token
+names:
+
+| Token | Source Element |
+|----------------|-------------------------------------------------|
+| annotation | System store names |
+| bracket | |
+| comment | Comments |
+| identifier | Store and Group names |
+| invalid | Invalid code |
+| keyword | Keywords such as `begin`, `any` |
+| number | Decimal character codes, deprecated, e.g. `d65` |
+| number.hex | Unicode character codes, e.g. `U+1234` |
+| number.octal | Character codes expressed in octal |
+| operator | `+`, `>` and other operators |
+| string | Strings |
+| string.invalid | Unterminated strings |
+| string.quote | `'` and `"` characters |
+| tag | Virtual keys |
+| white | |
\ No newline at end of file
diff --git a/developer/docs/help/reference/errors/index.md b/developer/docs/help/reference/errors/index.md
new file mode 100644
index 00000000000..c5e8acc3680
--- /dev/null
+++ b/developer/docs/help/reference/errors/index.md
@@ -0,0 +1,4 @@
+---
+title: Errors
+redirect: ../messages
+---
diff --git a/developer/docs/help/reference/file-layout.md b/developer/docs/help/reference/file-layout.md
new file mode 100644
index 00000000000..e466d7c05b0
--- /dev/null
+++ b/developer/docs/help/reference/file-layout.md
@@ -0,0 +1,131 @@
+---
+title: Keyman Developer structure for standard keyboard source file folders
+---
+
+Keyman Developer generates keyboard project files in a standard layout. [Keyman
+Cloud](/developer/keyboards/) uses this file layout. It is highly recommended to
+adhere to this file layout.
+
+## About the source files
+
+When you create and build a project, you'll end up with the following structure.
+Some files may be not be present, depending on the targets you specify.
+
+```plain
+sample
+│ HISTORY.md
+│ LICENSE.md
+│ README.md
+│ sample.kpj
+│ sample.kpj.user
+│
+├───build
+| sample.keyboard_info
+│ sample.kmp
+│ sample.kmx
+│ sample.kvk
+│ sample.js
+│
+└───source
+ readme.htm
+ sample.keyman-touch-layout
+ sample.kmn
+ sample.kps
+ sample.kvks
+ welcome.htm
+```
+
+Now that probably seems like a whole lot of files! But each file has a purpose.
+
+### Root folder
+
+`sample.kpj`
+: The main project file. This contains references to all the components:
+ keyboards, models, and packages.
+
+`sample.kpj.user`
+
+: A user preference file. You can safely delete it at any time (you'll lose some
+ remembered settings from Keyman Developer, but nothing consequential) and it
+ should not be shared in a git repository.
+
+Some of these files are metadata files: information about your keyboard project
+that will help you and others maintain your keyboard project in the future.
+
+For example, you may wish to make your keyboard project into a git repository
+and push it an online public git host such as GitHub or GitLab.
+
+`README.md`
+: Provides an introduction to the keyboard project when others stumble across
+ it. This file is in [Markdown](https://commonmark.org) format.
+
+`LICENSE.md`
+: Explains the rights that you give to others (by default, it will be the MIT
+ license -- which is what we require for Keyman Cloud -- but you can change
+ that to any license you wish). This file is in
+ [Markdown](https://commonmark.org) format.
+
+`HISTORY.md`
+: A place to record changes you make to the project over time. This file is in
+[Markdown](https://commonmark.org) format.
+
+* Note: Keyman Developer 16 included a .keyboard_info file in this folder. This
+ file is no longer required, as the required metadata can be generated from
+ the keyboard source files instead.
+
+### Source folder
+
+The files in the source folder are more fully described in the Keyman Developer
+[user guide](../..).
+
+[`source/sample.kmn`](/developer/language)
+: Your keyboard source. It will compile to a `.kmx` (desktop targets) or a `.js`
+ (web / touch targets).
+
+[`source/sample.kps`](../reference/file-types/kps)
+: The package source, which is used to create a compressed `.kmp` archive of the
+ files needed for distribution.
+
+`source/readme.htm`
+: Introductory web page for end users of your keyboard, shown before they
+ install it, so they know what it is used for.
+
+`source/welcome.htm`
+: A web page which describes how to use your keyboard, ideally with examples.
+
+[`source/sample.keyman-touch-layout`](../reference/file-types/keyman-touch-layout)
+: This touch layout file description is in JSON format and most easily visually
+ edited with the Keyman Developer touch layout editor. This file is optional;
+ remove the reference from the keyboard source if you don't wish to use it.
+
+[`source/sample.kvks`](../reference/file-types/kvks):
+: The on screen keyboard template for desktop platforms. It is in XML format and
+ most easily visually edited with the Keyman Developer on screen keyboard
+ editor. This file is optional; remove the reference from the keyboard source
+ if you don't wish to use it.
+
+### Build folder
+
+All of the files in the build folder are generated by the `kmc` compiler. None
+of these files should be included in a git repository.
+
+`build/sample.keyboard_info`
+: A [metadata file](/developer/cloud/keyboard_info) detailing the
+ keyboard's origin, version, requirements, and capabilities.
+ This file is built automatically by the kmc compiler. Please see
+ [.keyboard_info specification](/developer/cloud/keyboard_info) for more
+ details.
+
+`build/sample.kmp`
+: The installable package file -- installable in all Keyman end user products
+
+`build/sample.js`
+: The keyboard compiled to Javascript for use with KeymanWeb
+
+`build/sample.kvk`
+: The compiled on screen keyboard. This intermediate file should not be
+ distributed; it is included in the compiled package.
+
+`build/sample.kmx`
+: The compiled keyboard. This intermediate file should not be distributed; it is
+ included in the compiled package.
diff --git a/developer/docs/help/reference/file-types/ico.md b/developer/docs/help/reference/file-types/ico.md
new file mode 100644
index 00000000000..33331318781
--- /dev/null
+++ b/developer/docs/help/reference/file-types/ico.md
@@ -0,0 +1,20 @@
+---
+title: ICO files
+---
+
+Used by:
+: Keyman Desktop,
+ Keyman Developer.
+
+Description:
+: An .ICO file is a standard Windows
+ [icon](https://en.wikipedia.org/wiki/ICO_(file_format)) file.
+
+Details:
+: An .ICO file is an icon which is used in the user interface to
+ indicate that the keyboard is active. See the [bitmap
+ store](../../../language/reference/bitmap)
+
+Distributed with keyboard:
+: An .ICO file is associated with a specific keyboard. A generic
+ keyboard icon will be displayed if no icon is specified.
diff --git a/developer/docs/help/reference/file-types/index.md b/developer/docs/help/reference/file-types/index.md
new file mode 100644
index 00000000000..a4a47591bf4
--- /dev/null
+++ b/developer/docs/help/reference/file-types/index.md
@@ -0,0 +1,45 @@
+---
+title: File Types
+---
+
+[ICO files](ico)
+: Keyboard icon files (binary file format)
+
+[KMN files](kmn)
+: Keyboard source files (text file format)
+
+[KMP files](kmp)
+: Keyboard package files (zip file format)
+
+[KMX files](kmx)
+: Keyboard files (binary file format)
+
+[KPJ files](kpj)
+: Keyman Developer Project files (XML file format)
+
+[KPS files](kps)
+: Keyboard package source files (XML file format)
+
+[KVK files](kvk)
+: Visual keyboard files (binary file format)
+
+[KVKS files](kvks)
+: Visual keyboard source files (XML file format)
+
+[MODEL.JS files](model-js)
+: Lexical model files (text file format)
+
+[MODEL.TS files](model-ts)
+: Lexical model definition files (text file format)
+
+[TSV files](tsv)
+: Word list source files (tab-separated values file format)
+
+[TTF files](ttf)
+: TrueType font files (binary file format)
+
+[keyman-touch-layout files](keyman-touch-layout)
+: Keyman touch layout source files (JSON file format)
+
+[XML files](xml)
+: LDML keyboard source files (XML file format)
diff --git a/developer/docs/help/reference/file-types/keyman-touch-layout.md b/developer/docs/help/reference/file-types/keyman-touch-layout.md
new file mode 100644
index 00000000000..8fa7312808e
--- /dev/null
+++ b/developer/docs/help/reference/file-types/keyman-touch-layout.md
@@ -0,0 +1,847 @@
+---
+title: keyman-touch-layout files
+---
+
+Used by:
+: Keyman Developer.
+
+Description:
+: A .keyman-touch-layout file is a JSON format file that describes a
+ keyboard layout for touch devices.
+
+Details:
+: Referenced by a Keyman keyboard ([.KMN](kmn)) and compiles into the
+ target keyboard .js file.
+
+Distributed with keyboard:
+: This is a keyboard development file and should not be distributed
+ with your package.
+
+---
+
+## Sample JSON
+
+This cut-down sample shows just a single key, with the key cap "ឆ", and a single longpress key under it with the key cap "ឈ".
+
+```json
+{
+ "phone": {
+ "displayUnderlying": false,
+ "font": "Khmer Busra Kbd",
+ "fontsize": "0.8em",
+ "layer": [ {
+ "id": "default",
+ "row": [ {
+ "id": 1,
+ "key": [ {
+ "id": "K_Q", "text": "ឆ",
+ "sk": [ { "text": "ឈ", "id": "K_Q", "layer": "shift" } ]
+ } ]
+ } ]
+ } ]
+ }
+}
+```
+
+
+
+
+
+## Key properties
+
+For each visual key, the appearance and behaviour is determined by a number of
+properties:
+
+### Key code
+
+Each key must be given an identifying key code which is unique to the key layer.
+Key codes by and large correspond to the virtual key codes used when creating a
+keyboard program for a desktop keyboard, and should start with `K_`, for keys
+mapped to standard Keyman virtual key names, e.g. `K_HYPHEN`, and `T_` or `U_`
+for user-defined names, e.g. `T_ZZZ`. If keyboard rules exist matching the key
+code in context, then the output from the key will be determined by the
+processing of those rules. It is usually best to include explicit rules to
+manage the output from each key, but if no rules matching the key code are
+included in the keyboard program, and the key code matches the pattern
+`U_xxxx[_yyyy...]` (where `xxxx` and
+`yyyy` are 4 to 6-digit hex strings), then the Unicode characters
+`U+xxxx` and `U+yyyy` will be output. As of Keyman 15, you
+can use more than one Unicode character value in the id (earlier versions
+permitted only one). The key code is always required, and a default code will
+usually be generated automatically by Keyman Developer.
+
+- `K_xxxx` is used for a standard Keyman Desktop key name, e.g.
+ `K_W`, `K_ENTER`. You cannot make up your own `K_xxxx` names.
+ Many of the `K_` ids have overloaded output behaviour, for instance, if no
+ rule is matched for `K_W`, Keyman will output 'w' when it is touched. The
+ standard key names are listed in [Virtual Keys and Virtual Character
+ Keys](/developer/language/guide/virtual-keys "Virtual Keys and Virtual
+Character Keys"). Typically, you would use only the "common" virtual key
+ codes.
+
+- `T_xxxx` is used for any user defined names, e.g. `T_SCHWA`. If you wanted
+ to use it, `T_ENTER` would also be valid. If no rule matches it, the key
+ will have no output behaviour.
+
+- `U_####[_####]` is used as a shortcut for a key that will output those
+ Unicode values, if no rule matches it. This is similar to the overloaded
+ behaviour for `K_` ids. Thus `####` must be valid Unicode characters. E.g.
+ `U_0259` would generate a schwa if no rule matches. It is still valid to
+ have a rule such as `+ [U_0259] > ...`
+
+As noted above, some `K_xxxx` codes emit characters, if no rule is defined.
+There are also some codes which have special functions:
+
+
+
+
+
Identifier
+
Meaning
+
+
+
+
+
`K_ENTER`
+
Submit a form, or add a new line (multi-line); the key action may vary depending on the situation.
+
+
+
`K_BKSP`
+
Delete back a single character. This key, if held down, will repeat. It is the only key code which triggers
+ repeat behavior.
+
+
+
`K_LOPT`
+
Open the language menu (aka Globe key).
+
+
+
`K_ROPT`
+
Hide the on screen keyboard.
+
+
+
`K_TAB`, `K_TABBACK`, `K_TABFWD`
+
Move to next or previous element in a form. Note that these key functions are normally
+ implemented outside the touch layout, so should not typically be used. `K_TAB` will go to previous
+ element if used with the `shift` modifier.
+
+
+
+
+Any key can be used to switch keyboard layers (see
+[`nextlayer`](#toc-nextlayer)), but the following layer-switching key codes have
+been added for switching to some commonly used secondary layers. Note that these
+keys have no specific meaning; you must still set the `nextlayer` property on
+the key.
+
+
+
+
+
Identifier
+
Meaning
+
+
+
+
+
`K_NUMERALS`
+
Switch to a numeric layer
+
+
+
`K_SYMBOLS`
+
Switch to a symbol layer
+
+
+
`K_CURRENCIES`
+
Switch to a currency layer
+
+
+
`K_SHIFTED`
+
Switch to a shift layer
+
+
+
`K_ALTGR`
+
Switch to a right-alt layer (desktop compatibility)
+
+
+
+
+### Key text
+
+The key text is simply the character (or characters) that you want to appear on
+the key cap. This will usually be the same as the characters generated when the
+key is touched, unless contextual rules are used to generate output according to
+a multi-key sequence, as will be true for the GFF Amharic keyboard. Unicode
+characters can be specified either as a string using a target font or using the
+standard hex notation `\uxxxx`. This may be sometimes more convenient, for
+example, for characters from an uninstalled font, or for diacritic characters
+that do not render well alone.
+
+A number of special text labels are recognized as identifying special purpose
+keys, such as Shift, Backspace, Enter, etc., for which icons are more
+appropriately used than a text label. A special font including these icons is
+included with Keyman and automatically embedded and used in any web page using
+Keyman. The list of icons in the font will probably be extended in future, but
+for now the following special labels are recognized:
+
+
+
+
+
Text String
+
Key Cap
+
Key Purpose
+
+
+
+
+
`*Shift*`
+
+
Select Shift layer (inactive). Use on the Shift key to indicate that it switches to the shift layer.
+
+
+
`*Shifted*`
+
+
Select Shift layer (active). Use on the Shift key on the shift layer to switch back to the default layer.
+
+
+
`*ShiftLock*`
+
+
Switch to Caps layer (inactive). Not commonly used; generally double-tap on Shift key is used to access the
+ caps layer.
+
+
+
`*ShiftedLock*`
+
+
Switch to Caps layer (active). Use on the Shift key on the caps layer to switch back to the default layer.
+
+
+
+
`*Enter*`
+
or
+
Return or Enter key (shape determined by writing system direction)
+
+
+
`*LTREnter*`
+
+
Return or Enter key (left-to-right script shape)
+
+
+
`*RTLEnter*`
+
+
Return or Enter key (right-to-left script shape)
+
+
+
`*BkSp*`
+
or
+
Backspace key (shape determined by writing system direction)
+
+
+
`*LTRBkSp*`
+
+
Backspace key (left-to-right script shape)
+
+
+
`*RTLBkSp*`
+
+
Backspace key (right-to-left script shape)
+
+
+
`*Menu*`
+
+
Globe key; display the language menu. Use on the `K_LOPT` key.
+
+
+
`*Hide*`
+
+
Hide the on screen keyboard. Use on the `K_ROPT` key.
+
+
+
`*ABC*`
+
+
Select alphabetic layer (Uppercase)
+
+
+
`*abc*`
+
+
Select alphabetic layer (Lowercase)
+
+
+
`*123*`
+
+
Select the numeric layer
+
+
+
`*Symbol*`
+
+
Select the symbol layer
+
+
+
`*Currency*`
+
+
Select the currency symbol layer
+
+
+
`*ZWNJ*`
+
(iOS) or (Android)
+
Zero Width Non Joiner (shape determined by current platform)
+
+
+
`*ZWNJiOS*`
+
+
Zero Width Non Joiner (iOS style shape)
+
+
+
`*ZWNJAndroid*`
+
+
Zero Width Non Joiner (Android style shape)
+
+
+
`*ZWNJGeneric*`
+
+
Zero Width Non Joiner (not platform-specific)
+
+
+
`*Sp*`
+
+
Regular space
+
+
+
`*NBSp*`
+
+
No-Break Space
+
+
+
`*NarNBSp*`
+
+
Narrow No-Break Space
+
+
+
`*EnQ*`
+
+
En Quad
+
+
+
`*EmQ*`
+
+
Em Quad
+
+
+
`*EnSp*`
+
+
En Space
+
+
+
`*EmSp*`
+
+
Em Space
+
+
+
`*PunctSp*`
+
+
Punctuation Space
+
+
+
`*ThSp*`
+
+
Thin Space
+
+
+
`*HSp*`
+
+
Hair Space
+
+
+
`*ZWSp*`
+
+
Zero Width Space
+
+
+
`*ZWJ*`
+
+
Zero Width Joiner
+
+
+
`*WJ*`
+
+
Word Joiner
+
+
+
`*CGJ*`
+
+
Combining Grapheme Joiner
+
+
+
`*LTRM*`
+
+
Left-to-right Mark
+
+
+
`*RTLM*`
+
+
Right-to-left Mark
+
+
+
`*SH*`
+
+
Soft Hyphen
+
+
+
`*HTab*`
+
+
Horizontal Tabulation
+
+
+
+
+
+The following additional symbols are also available, but intended for working
+with legacy desktop layouts, and not recommended for general use:
+
+
+
+### Key type
+
+The general appearance of each key is determined by the key type, which is
+selected (in Keyman Developer) from a drop-down list. While generally behavior
+is not impacted by the key type, Spacer keys cannot be selected.
+
+
+
+
+
Key Type
+
Value
+
Meaning
+
+
+
+
+
Default
+
`0`
+
Any normal key that emits a character
+
+
+
Special
+
`1`
+
The frame keys such as Shift, Enter, BkSp.
+
+
+
Special (active)
+
`2`
+
A frame key which is currently active, such as the Shift key on the shift layer.
+
+
+
Deadkey
+
`8`
+
Does not impact behavior, but colors the key differently to indicate it has a special function, such as a
+ desktop-style deadkey.
+
+
+
Blank
+
`9`
+
A blank key, which may be used to maintain a layout shape. Usually colored differently. Does not impact
+ behavior.
+
+
+
Spacer
+
`10`
+
Does not render the key, but leaves a same-sized gap in its place. The key cannot be selected.