# Custom layouts & the layout editor

> Edit any keyboard layout visually, or as raw JSON, and share the result.

URL: https://wmkeyboard.pages.dev/languages/custom-layouts/
Section: Languages

A layout is the key grid itself: which key sits where, what it types, what its long-press alternates are. WM Keyboard ships 1,732 of them, and every one is editable: rearrange a few keys, add a Ctrl key for a terminal app, or build a language layout from scratch, then share the result as a file.

  languages/key-layouts-gallery — The Key layouts screen: your own layouts, the shipped set, and an import row.

## Editing a layout

Layouts of your own are always listed, on or off, and the switch at the start of each row's buttons turns one on or off without leaving the screen. A layout of your own also shows on the Layouts shelf of its language under [Languages](https://wmkeyboard.pages.dev/languages/switching/), next to the shipped ones, so either place works. Shipped ones appear here once you've turned them on under Languages. There's no edit button: tap the row itself and the visual editor opens. The grid renders with your actual theme, key shape, and font, so what you edit is what you'll type on. The ⋮ button at the end of a row holds the rest: export the layout to a file, duplicate it, and delete it (or, on a shipped layout you've edited, reset it). Swiping the row to the side shows the same three. On a layout of your own the menu also has **Share with everyone**, which sends it to the community addon repository; see [Sharing with everyone](https://wmkeyboard.pages.dev/addons/community/).

Each layout row also has a chevron. Open it and the layout's layers list underneath, one row each: the nine typing layers and then every panel (Emoji, GIF, Sticker, Clipboard, Text editing, Trackpad, Numpad and, outside the F-Droid build, Handwriting), with a pencil on the ones this layout has authored and **Standard** on the ones it inherits. Tap a layer row and the editor opens on that tab. The list is closed by default, so the screen stays a list of layouts until you ask.

**Start a new layout** is the first row under Your layouts, for a grid that isn't a rearranged anything. It builds an empty four-row skeleton and opens it. Only the plain character keys are cleared, so enter, shift, space and delete keep the grid usable while you fill it in.

  languages/layout-editor-grid — The visual layout editor, showing the Letters layer with a key selected.

A layout has up to nine layers (Letters, Symbols, Symbols 2, Number, Phone, Date, Time, Date & time, and Fn), switchable with tabs across the top, followed by a tab for each of the [panels](#panel-layouts). The tabs are pinned with the grid, above it, so they stay where they are when a layer with more or fewer rows comes up. None of them has to be defined. Any layer you leave untouched falls back to the built-in grid for it, the shipped QWERTY's, so a layout that only rearranges letters still gets a working number pad and symbol page without you drawing three hundred keys yourself. The Number layer is also what the Numpad tool, and a long press on ?123, put on screen, so a layout that authors one gets its own pad there too, in the order it drew it. The five keypads (Number, Phone, Date, Time and Date & time) keep their shared keys in the same places: the digits, ⌫ in the right column just above ⏎, and space at the top of that column on every pad that has one. Only the characters each field needs, like * and # on Phone or : on Time, sit in different slots, so a change to a shared key is the same edit on every pad. Letters gets one extra guarantee the others don't, though. See [Details & edge cases](#details--edge-cases). A pencil icon on a tab marks a layer this layout has authored its own copy of. An unauthored tab shows the built-in grid live, with a note that editing it will fork a copy just for this layout.

An alphabet that takes several pages, like the Hindi alphabetical board's 1/4 to 4/4, has a tab for each further page after the layers: **Page 2** puts that page in the grid to edit like the letters. Shift and the page key don't move the grid in the editor, so these tabs are the way there. **Reset this layer** on a page removes the page, and undo goes back one step on the tab you are on. A new page still takes the raw JSON: add the layer, then a key with a `layer` action that opens it. On an Indic layout, `"afterConsonant": "page2"` on the letters layer makes that page take the letters' place whenever the text before the cursor ends in a consonant. Each vowel sign, virama or anusvara key on it then shows the consonant with its sign, and a tap replaces the sign the consonant already has. This is how the Samsung Indic boards bring up their vowel signs.

The grid stays pinned at the top of the screen, edge to edge, and draws the layer the way the keyboard draws it: the corner hints, the enter key's own icon, the language name on the spacebar, the press and hold shortcuts and accents your settings add to keys, and the rows the keyboard draws around the grid. With **Number row** on in [Layout & size](https://wmkeyboard.pages.dev/reference/settings/layout/), the number row is in the preview above the grid; on Symbols it is joined by the row the keyboard shows in place of the digits, and with **Arrow keys row** on, the arrow keys row is under it, or at the very top with **Arrow keys on top**. Tap a key on any of those rows to edit it, the same as a key in the grid. To change the number row or the arrow keys row for every layout at once, rather than in each one, use **Number row keys** and **Arrow keys row keys** in [Layout & size](https://wmkeyboard.pages.dev/reference/settings/layout/#number-row); a layout's own row still wins over them.

On a small screen the grid can leave little room for the settings under it. Drag either of the two corner marks under the grid inward or up to shrink it, keys, labels and icons together, down to 35% of its full size; drag outward or down to bring it back, or double-tap a mark to return to full size. The size is kept for the next time you open the editor, on this phone only.

Under the layer tabs on Letters, **Show as in** redraws the grid for an **Email address** or a **Web address** field, the two kinds of field that change keys on the letters: an email field puts `@` on the comma key, and a web address field keeps the comma but starts its press and hold, and its corner hint, with `/`. Both add `.com` and the other endings to the full stop, and a web address field's enter shows Go. To stop a key changing, open it and set **Field adaptation**, under More, to **Exactly as laid out**.

A "Show the shift plane" toggle redraws every key with its `shiftLabel` (or an uppercased label) instead of the primary one, so you can check the shifted layer without switching layers.

Keys set to "switch layout", "emoji panel", "toolbox" or "open a tool" draw an icon in the grid rather than the label stored on them. The globe key is the one you'll meet: its stored label is the emoji 🌐, but the keyboard has always drawn its own monochrome globe icon there instead, and now the editor does too, so the grid matches what you actually type on. A tool key wears the tool's own icon the same way, unless you give it a label, in which case the label wins. Change the key's action and it goes back to drawing its label.

### Editing a key

Tap any key and its settings open on the page under the pinned grid, so the key and the grid stay on screen together. A strip at the top names the key and has its arrows, a duplicate button, **Add key after**, **Copy**, **Paste after**, **Delete** and a × that closes the settings. Under it the settings come in groups: **Label and action**, **Press and hold** (with the popup and the corner hint), and the folds **Flicks**, **Size and look** and **More**, which open with a tap on their heading and stay open on later keys. **Flicks** is open already on a key that has flicks.

  languages/key-edit-sheet — The key edit sheet: label, output, action, width, and long-press alternates.

- **Label**: what's drawn on the key. On Shift and caps lock, a label replaces the arrow: write `A`, `ABC` or a word and the key shows that instead, lit in the accent colour while shift is on. Leave it blank, or write `⇧` or `⇪`, and the key keeps the arrow your icon pack draws. Any other action key left blank draws its action's icon, the same one the grid preview and the press and hold popup show.
- **Output**: what it actually types; leave it blank and it types the label.
- **Shift label**: leave it blank and Shift uppercases the label.
- **Flick directions**: what a short swipe from the key in one of eight directions types instead of the tap, the way the [Japanese Flick](https://wmkeyboard.pages.dev/languages/cjk/#japanese-input) pad reaches い, う, え and お from あ, and the way the shipped **English flick (3×3)** layout keeps all twenty-six letters on nine keys, MessagEase style, with `o` carrying q, u, p, c, b, g, d and j on its eight arms. See [Flick keys](https://wmkeyboard.pages.dev/typing/gestures/#flick-keys) for how the gesture reads. A key with no flicks shows an "Add flick directions" row; tap it, or open a key that already has some, and a 3×3 pad appears with the key's own label in the middle and a cell for each of the eight arms, the way the key itself draws them. Tap a cell to edit that arm in the field under the pad; a cell with nothing in it shows a faint plus. **Swipes only**, above the pad, empties the middle: the key then has no centre character, a tap on it types nothing, and only its swipes do, the way an Unexpected Keyboard key with corners and no centre works. Leaving the label empty on a key with flicks does the same. Turning the switch off gives the middle the key's first flick character back. Leave the field blank for no flick that way, and **Remove all flick directions** under the pad clears the lot. The grid preview draws each flick's character at the edge or corner of the key it is flicked towards, so you can see the whole pad at a glance, and the keyboard draws them the same way unless [Show flick arms on keys](https://wmkeyboard.pages.dev/typing/gestures/#flick-keys) is off. A layer with flick keys has its own **Show flick arms here** switch below its rows, for showing or hiding them on that layer alone. Under each direction that types something is a **with Shift** field: what that flick types while Shift is on, the way the shift label works for the tap. Leave it blank and Shift makes the flick a capital letter, as it does for the label, and so does a swipe out to the arm and back again. A kana pad with a Shift key can so put katakana on its hiragana keys: あ with Shift types ア, and its left flick い types イ. The flick cross shows the shifted kana while Shift is on. A layout that uses the four corners needs this release or newer to open; an older build declines the whole file rather than guess.

  Keys that do something other than type get flicks too: Enter, Shift, ?123, ⌫, the globe and emoji keys, Space, and the rest, everything except a key set to repeat. On Space an arm takes its direction from the spacebar's swipes, and the directions without one keep them ([how](https://wmkeyboard.pages.dev/typing/gestures/#flick-keys)). Each arm types the text in its field, or runs an action if you tap its bolt, and the tap stays the key's own action. So Enter with `?` on its flick up types one `?` and neither sends nor starts a new line. In the JSON these text arms are the same `flick` map a letter key uses.

  Any flick can also **do an action** instead of typing: tap the ⚡ button at the end of its field and pick one from the same list as the press and hold actions. **Several text editing actions** asks for a list and runs them in turn, so one flick can be start of line then page up, or up then right. A text editing action asks which operation next, so a key can move the cursor with its flicks. A flick that moves the cursor or deletes keeps going while you hold your finger on it, the same as the key for it, whether the move is a text editing action, a cursor tool such as Cursor word left, or an arrow key sent as a key press. Turn on **Hold a flick to repeat it** under Key press / Timing and a flick that types does the same: held out past the repeat delay, it types its letter again and again, at the **Repeating key interval**, until you lift. Action flicks work on action keys too, where they are the only kind: open a Select key, tap "Add flick directions", and give its arms the four arrows. The flick cross, the key's face and the grid preview show each action's icon. Choosing an action for a direction removes that direction's text. A modifier, ?123 or 🌐 key given action flicks flicks instead of starting its drag. The delete keys take flicks, so ⌫ can carry forward delete on its flick up: they keep their hold-to-repeat, and the delete swipe keeps every direction no arm points, so an up arm leaves the leftward word swipe as it was. A flick has to leave the key before the repeat starts. The arrow keys take flicks the same way, so ← can carry word left and ↑ page up, and holding one still keeps moving the cursor. That includes the arrow keys row and any key with **Repeat while held** on. The space bar takes flicks as well, and its swipes keep every direction no arm points.

  Change a text key to an action afterwards and its text flicks go quiet: the key neither shows them in the flick cross nor types them, though the JSON keeps them in case you change it back. The fields are `flick`, `flickShift` and `flickActions` in the JSON, keyed by direction: `left`, `up`, `right`, `down`, `up_left`, `up_right`, `down_left` and `down_right`.
- **Becomes 小゛゜ after a kana**, on the keys of a Japanese layout: a switch, off by default, that makes the key stand in for the 小゛゜ key while the last kana you typed (hiragana or katakana) has a small, ゛ or ゜ form. The key shows 小゛゜ and a tap changes that kana; when the word is done, or the kana has no other form (な, ん), it goes back to what it was. Turn it on for the globe or emoji key and you can delete the separate 小゛゜ key and give its space to something else, the way phone kana pads do it. The preview marks such a key with a small 小゛゜ in its corner. The field is `kanaVariantWhileComposing` in the JSON.
- **Action**: opens a picker with 39 options across seven groups: typing (types text, Shift, caps lock, Delete, forward delete, Space, Enter, a new-line key that always types a line break even where Enter would send, and the Japanese 小゛゜ key, which changes the last kana you typed to its small, ゛ or ゜ form, and the two Korean keys of the [나랏글 pad](https://wmkeyboard.pages.dev/languages/cjk/), 획추가 and 쌍자음, which add a stroke to the last Korean letter you typed (ㄱ to ㅋ, ㅏ to ㅑ) or double it (ㄱ to ㄲ), and the [T9 Pinyin](https://wmkeyboard.pages.dev/languages/cjk/) pad's syllable list, a scrolling column that lists the syllables your keys could spell while you type and the key's long-press characters the rest of the time; give it a few rows' height), layers (symbols page, letters page, emoji panel, the toolbox, which opens the same grid of tools as the button at the start of the toolbar, switch layout, switch keyboard, [switch to a specific keyboard](https://wmkeyboard.pages.dev/languages/switching/#switching-from-androids-system-menu) for a key that hands the field to one named keyboard app with no picker in between, Fn layer, "open a layout" for one of your [secondary layouts](#secondary-layouts), and "toggle a keyboard mode" for one of your [keyboard modes](https://wmkeyboard.pages.dev/typing/modes/#keyboard-modes)), modifiers (Ctrl, Alt, Meta; tap to arm, double-tap to lock, or [drag from it onto another key](https://wmkeyboard.pages.dev/typing/gestures/#dragging-from-a-modifier-key) to fire the combination in one movement, the same as dragging from Shift), keys apps understand (Tab, Esc, arrow keys), a text editing action (the same twenty-five operations the text editing panel's keys run: the four arrows, word left and right, page up and down, start and end of the line, start and end of the text, Select, Select all, Select word, Select line, Cut, Copy, Paste, Delete, Forward delete, Delete word, Delete word forward, [Retype word](https://wmkeyboard.pages.dev/smart/suggestions/#retyping-the-last-word) and [No space](https://wmkeyboard.pages.dev/typing/glide-typing/#the-space-after-a-glided-word), whose hold arms it for the next key, picked on a second screen the moment you choose it; the arrows, the word and page moves, the two deletes and the two word deletes repeat while held, the rest can hold to an alternate), chorded input (braille dot, morse dot, morse dash), and a catch-all group for an "Open a tool" key, a broadcast-intent key (fires an Android broadcast for automation apps like Tasker) and a "Nothing" key that deliberately swallows taps. Giving a key a different action clears the label and icon it had, so a key that was Del and is now Ctrl draws what the keyboard draws for Ctrl until you label it yourself. A key that switches to typing text keeps its label. The list is longer than the dialog, so it scrolls, and a rail down its right edge says so: one segment for each group, sized by how many actions the group holds, with the group you are reading lit. Drag the rail to move through the list quickly, and the group name follows your finger.
- **Tool**, on an "Open a tool" key only: which of the keyboard's tools the key opens, picked from a list of every tool this build has. The key wears the tool's own icon, and pressing it does exactly what tapping that tool on the toolbar does. The tool doesn't have to be on your toolbar for the key to work, so this is a way to keep voice typing or the editing pad one press away without spending a toolbar slot on it.
- **Layout**, on an "Open a layout" key only: which of your secondary layouts the key shows, picked from a list of them by name. The layout's name lands as the key's label unless you already wrote one. Pressing the key shows that grid in place of the letters; pressing it again, or any ABC key, goes back.
- **Mode**, on a "Toggle a keyboard mode" key only: which of your keyboard modes the key turns on, picked from a list of them by name. The mode's name lands as the key's label unless you already wrote one, and an unlabelled key shows ◐. Pressing the key picks that mode the way the Modes tool does; pressing it again goes back to automatic. The same action can sit on a flick, a long-press alternate, a multi-finger swipe or a `?123` chord. The key holds on to the mode by its id, so renaming the mode keeps the key working, and a key naming a mode you've since deleted does nothing. In the JSON it is `{"type": "mode", "id": "…"}`.
- **Width**: a slider, a minus / value / plus stepper, preset chips for 1 / 1.25 / 1.5 / 2 / 4, and a "Fill the row" button. The stepper is the one to reach for when a preset is close but not right: the buttons move by 0.05, and the field between them takes an exact value you type. Widths go to two decimals, so **1.43** (the width seven keys need to fill a ten-wide grid) is something you set here rather than in the JSON. **Share of the row** under the stepper says the same width as a percentage of the grid, and takes one: type `10` and a key on a ten-wide grid becomes 1.00 wide, `12.5` makes it 1.25. The layout still stores grid units, so the file reads the same either way.
- **Covers N rows**: chips that make the key taller, so it reaches down into the rows below it. Leave it at 1 (the default, and what every shipped layout uses) for an ordinary key. Set it to 2 and the key draws twice as tall, and the row under it lays its own keys out *around* the column the tall key is standing in, the way cells flow around a `rowspan` in a table. That's how arrangements like ClearFlow put a full-height Enter down the right-hand edge. The chips stop at the number of rows left below the key, so a key on the last row doesn't offer the control at all.
- On every key except a panel component: **Icon**, a picker that draws the icon in place of the label. It lists the keyboard glyphs first (arrows, editing, keyboard chrome), then the app's own icons, with each icon's name under it and a search box. The name is what a hand-written layout stores in `icon`. Names match case-insensitively, shorthand aliases like `delete` for `backspace` or `globe` for `language` also work, and an unrecognized name falls back to drawing the label. Every icon the keyboard draws anywhere of its own accord is in the list, so any tool's glyph can go on a key. The search box takes more than the stored name: an alias and the name of the tool that wears the icon both find it, so searching for "clipboard" lands on the paste glyph and "selection mode" on `SelectAll`. An icon on Shift, Enter, Delete or the globe key replaces the glyph the icon pack gives it. On the space bar the icon replaces the language name, and **Keep the language name** (`iconBesideLabel`) draws it before the name instead.
- On text keys only: **Icon hint** (the same picker, for the corner hint, under Press and hold), and **Field adaptation** under More (none, comma slot, period slot, or exactly as laid out). An email field replaces the comma slot with `@` and a web-address field puts `/` first on its press and hold, while the period slot keeps its character and gains domain endings like `.com` on its long-press. "None" means *work it out from the key*, which is what every layout written before this control existed relies on: a `.` or a `,` on the bottom row fills its slot whether or not it says so. **Exactly as laid out** is the way to say you meant it: the key fills no slot and nothing substitutes its character, which matters on a language that ends sentences with something other than a full stop. On Japanese, Bengali, Hindi and the rest, a bottom-row `.` on the letters page is otherwise drawn as that language's own mark (`。`, `।`) with the full stop moved to its long-press. Every other layer (the symbols pages, an Fn or Number layer, a secondary grid) leaves the key alone and puts the mark on the long-press instead, so a decimal point is always one tap.
- **Repeat while held**: a switch that makes holding the key keep doing what it does, the way holding Delete does. It waits out the same **Time before a key repeats** every other repeat does and then runs at **Repeating key interval**, both under [Key press](https://wmkeyboard.pages.dev/reference/settings/keypress/#timing). Arrow keys are the case it exists for: add a row of ← ↓ ↑ →, turn this on for each, and a hold walks the cursor instead of asking for one tap per character. It's offered wherever a second press does something the first didn't, so not on a layer switch, a modifier, a tool or the globe key, and not on the keys that already repeat by themselves. It costs the key its press and hold, so the alternates fields below vanish while it's on, though its flicks stay: a flick that leaves the key before the repeat starts is still a flick; what you typed into them is kept and comes back when you turn it off. The field is `repeatOnHold` in the JSON.
- **Alternates for press and hold** (space-separated, so an alternate can't itself contain a space; multi-character entries like `.com` work fine, and the first one doubles as the small corner hint) and **Actions for press and hold**, a chip each for the entries in that same popup that do something instead of typing. Tap "Add an action" and pick from the same list the Action row offers, minus "types text" and "Nothing": Tab, a layer switch, a modifier, or "Open a tool" for voice typing, the editing pad, the clipboard and the rest. Tap a chip to change it, or the × to remove it. Both fields show on any key whose press and hold is free, which is every key except Delete, forward delete, Space and the braille dots, since those four hold to repeat or to chord. Enter is the one most people want: give it Tab, and a form is one keyboard away from filling itself in. Once a key has both, **Where the actions go** moves the actions from the end of the popup to the front or after any one of the alternates, so they can be one short slide from the key while a plain hold still types the first alternate. The field is `actionAlternatesAt` in the JSON.
- **Action on press and hold**: one action that holding the key runs straight away, with no popup, such as Cut on X or forward delete on .. A delete or a cursor move repeats while you hold. It takes the key's press and hold, so the alternates fields go away while it's set and come back when you remove it. Not on Space or a key with flicks. The field is `holdAction` in the JSON.
- **Columns in the popup**, once the key actually has alternates: a minus / value / plus stepper that fixes how many of them go on a row *for this key*. It starts at "From settings", which hands the key to the keyboard-wide **Alternate key columns** row under [Key press](https://wmkeyboard.pages.dev/reference/settings/keypress/#alternate-keys), and steps on through 3 to 11. It's per key because the right answer is: a `1` carrying a dozen fractions wants a grid five or six wide, while an `e` carrying four accents wants the single row it already gets. A number set here wins over the setting; every other key on the layout keeps following it. The field is `alternateColumns` in the JSON, where 0 means "from settings".
- **Hint position**: moves this key's corner hint alone, one dp a press, for a glyph whose shape needs it.
- **Flick labels** (on a key with swipe arms): the size of the labels drawn at its edges, apart from the key's own label and its corner hint, and **Flick labels further in**, which moves them in from the edges or out towards them one dp a press.
- **Hint size**: this key's corner hint as a multiple of every hint's size, from ×0.5 to ×3, for the one key whose hint needs to be bigger than the rest. **Same as every key** follows **Key hint font size** under Appearance. Hidden on a key whose hint is turned off.
- **Label size**: how big this one key's caption is drawn, as a multiple of an ordinary letter. Automatic is the default and is what every shipped key uses: a letter is drawn large, and a multi-character mode label like `?123` is drawn smaller. Setting a size replaces that decision, so ×1 is a real instruction and means "draw this word at full letter size". Chips offer ×0.5, ×0.7, ×1 and ×1.3, and a slider covers ×0.3 to ×2. This is where a [HeliBoard label flag](https://wmkeyboard.pages.dev/reference/importing-other-keyboards/#label-flags-become-label-sizes) lands when you import a layout. On a key that shows an icon instead of text, such as Delete, Enter or an action in the middle of a flick key, the same row sizes the icon.
- **The popup**, under the alternates: the press and hold popup as the keyboard draws it, in its columns, with the actions in their place among the characters. The entry a plain press and hold types is outlined. Entries your settings add rather than the layout (the press and hold shortcuts on a, c, v, x, z and y, the accents, a field's web endings) are faded, so you can see which ones are yours to change here.
- **Corner hint text**, on text keys: what the corner of the key shows, in place of the first alternate, such as `,?!` on a full stop. It changes only the corner, not what a press and hold types or the order of the popup. Leave it blank for the first alternate. An icon hint wins when both are set. The field is `hint` in the JSON.
- **Corner hint**: three ways this one key can answer the keyboard-wide **Press and hold hints** switch under [Key press](https://wmkeyboard.pages.dev/reference/settings/keypress/#press-and-hold-shortcuts). "From settings" is the default and follows it. "Never" turns the little hint off on this key alone, and nothing else about the key changes: long-press still opens the alternates, they're just not advertised in the corner any more. "Always" is the opposite, and the reason it exists: turn the hints off everywhere, then put them back on the two or three keys whose alternates nobody would guess. The row only shows up on a key that has a hint to draw (an icon hint, or character alternates on a key that opens the popup), so it doesn't take up a row on the other forty keys. In a hand-written layout the two halves are the `hideHint` and `forceHint` fields, and `hideHint` wins if a file sets both.

The strip at the top of the key's settings deletes the key, duplicates it, adds a new key after it, or moves it. Left and right still swap it with a row neighbor, and up and down move it into the row above or below. The grid preview itself is draggable too. Press and hold a key, then drag it to any row, and a caret marks where it will land. Let go on the line between two rows, or above or below them all, and the caret lies flat across the grid: the key gets a new row of its own there, and a row the move leaves empty goes.

**Copy** puts the key on the clipboard whole, with its flicks, icon, corner hint, alternates and size, and **Paste after** puts the keys on the clipboard straight after the key you have open. The clipboard carries them between layers and between layouts, so a key built on Letters can be pasted onto Symbols 2 or into another layout.

**Select several**, beside undo and redo, picks more than one key: tap keys to pick them and tap one again to put it back. Picked keys get one section for all of them: **Copy**, **Paste after**, **Delete them**, and their width, label size and corner hint set in one go. Keys side by side in one row also move together, with the arrows or by dragging any one of them, which is how the four arrows at the top of Symbols 2 move as a block. Two fields exist only in the raw JSON, because they're rare enough that a dedicated control would crowd the sheet for everyone else. One is a clipboard action (select all / copy / paste / cut / undo / redo, fired on long-press instead of the alternates popup). The other is `multitap`, a list of what tapping the key again types, the way a phone keypad works: `"output": "ㄱ", "multitap": ["ㅋ", "ㄲ"]` types ㄱ, then ㅋ in its place on a second tap, then ㄲ, then ㄱ again. A tap only continues the cycle when it's the same key within a second of the last one; a pause, or any other key, starts over. The [Cheonjiin pad](https://wmkeyboard.pages.dev/languages/cjk/#two-different-input-flows) uses it for its consonants and its .,?! key.

### Rows and layers

Select a key and a row action bar appears under the key's settings: **Add key** puts a new key at the end of that row, and the other two buttons duplicate the row or delete it (disabled once only one row is left). A row with no keys in it shows in the preview as an **Empty row** cell; tap it to select the row and delete it. A warning line shows if the row's total width doesn't match the layer's overall grid weight, red when the row is wider than the grid and squeezed, plain when it is narrower and drawn centred. **Row width** sets the row's width directly, every key keeping its share, so any row can be short and centred the way the second row of QWERTY is. A **Fit the row to the grid** button under the warning makes the row exactly the grid's width in one press: it scales every key in the row by the same factor, so a row whose keys are deliberately several different sizes keeps those proportions and still lands exactly on the grid. That's the one to use on a mixed-size row. "Fill the row" in the key sheet is the other answer, and it gives all the slack to the one key you have open.

A row height slider (×0.4–×2.5, with a stepper and 0.75/1/1.25/1.5/2 presets) adjusts just that row, and the preview draws each row at the height you set it to.

**Add a special key** puts a ready-made key after the selected one: Shift, caps lock, ⌫, forward delete, Ctrl, Alt, Meta, Tab, Esc, the arrow keys, the layer, emoji and toolbox keys, and every text editing operation by name, such as Copy, Paste, Select all or Word left. Each comes with its action set and the label or icon the keyboard draws for it.

A row narrower than the grid is centred, with equal gaps at both ends. That's how a nine-key home row sits under a ten-key top row. The one exception is a row with a space bar on it: the space bar stretches to take up the slack instead, so the bottom row always reaches both edges of the keyboard however wide the letters above it are. It's the same rule the tablet widening uses. A row with no space bar, or with two, is centred as before.

Below the grid: "Add a row" appends an empty one, "Reorder rows" opens a drag-reorder dialog, "Reorder keys in row N" appears once a multi-key row is selected, and "Reset this layer" (shown only on an authored layer) drops your changes and falls back to the inherited grid.

### Copying a layer

Two rows above "Reset this layer" move one layer at a time. **Copy this layer** puts the grid you're looking at on the system clipboard, and **Paste a layer** replaces the grid you're looking at with whatever layer is on the clipboard. That covers four jobs with one pair of buttons: duplicate the Date pad into the Time tab, carry a symbols page from one layout to another, hand someone a layout's letters grid in a chat message, or keep a plain-text backup of a layer you're about to rewrite.

The clipboard holds the same JSON the [raw editor](#reaching-the-raw-json) prints for that layer, wrapped in a small header naming the format and which layer it came from. That header is why pasting a shopping list is rejected rather than half-read, and why the confirmation can tell you what landed where. Copying works on an inherited layer too, which is the common case for sharing, since most layouts change the letters, or the letters and the symbols, and nothing else.

A pasted layer goes through the same repair pass an imported layout gets, judged against the layer it lands in rather than the one it came from, so a key naming an action this build doesn't have is dropped and reported instead of breaking the grid. What it doesn't do is add keys. Paste a grid with no space bar into Letters and it stays exactly as you pasted it, with the missing keys named in the Problems list. Paste is one undo step, so a paste onto the wrong tab is one press to take back.

Undo and redo hold up to 30 whole-layout snapshots for the session (not saved between visits), and everything you type into one key's settings coalesces into a single undo step, so relabeling a key isn't twenty tiny undos. There's no save button: every change writes through immediately, and the screen says so ("The app saves every change").

A "Problems" section at the bottom lists live validation findings as you edit. See [Details & edge cases](#details--edge-cases) for what gets checked.

### The bottom row and its settings

The [Bottom row keys](https://wmkeyboard.pages.dev/reference/settings/layout/) settings change the bottom row of every layout as the keyboard draws it: **Emoji key instead of 🌐**, **Swap the comma and 🌐 keys** and **Keep the 🌐 key in one place** are all on by default, and **Show the 🌐 key** and **Comma key opens emoji** can change it too. The editor draws the row the same way the keyboard does, so with the default settings a shipped layout stored as ?123 · `,` · 🌐 · space shows ?123 · emoji · `,` · space in the editor as well. Under the grid, a note names the settings that change this layer's row, with a link to them.

Moving or changing a key those settings moved or changed (the emoji key, the comma beside it, a spacebar they resized) turns on **Keep this bottom row as it is here** for that layer. The layer then keeps the row exactly as the grid showed it, your change included, and the settings leave it alone from then on, so a key you place stays where you put it. You can also turn the switch on yourself before editing. The switch stays on the page whenever any of those settings is on, so you can always turn it back off. Turn it off and the settings arrange the row again; on a layer you haven't changed in any other way, that also puts back the standard row. Keys the settings don't touch, like the full stop or Enter, edit in place without turning anything on.

The switch is per layer. The symbols page a layout still inherits keeps following the settings, and since the letters were written down as the settings drew them, the two rows still match on the way into ?123. In the raw JSON it is `"bottomRowAsLaidOut": true` on the layer.

### Keeping a layer open

Normally the keyboard goes back to the letters every time it opens: minimise it, switch apps, tap into another field, and whatever layer you were on is gone. **Keep this layer open** is a switch on every layer except the letters of an ordinary layout (and on the one grid of a secondary layout), and it changes that rule for that layer alone. The same switch sits in each [panel layout](#panel-layouts)'s editor, so the text editing pad, the emoji panel, the clipboard or the trackpad can stay open across fields the same way. With it on, the layer stays until you press a key or a tool that takes you somewhere else. Closing the keyboard is not that, and neither is a new field, an app switch, or the keyboard being shut down and started again in the background between one field and the next.

It's a per-layer switch on purpose. One symbols page you actually live on can stay put while the same layout's Fn layer still springs back, with no list of "persist if…" conditions to maintain. The catch is in the switch's own subtitle: the keyboard can no longer take you off the layer by itself, so it needs an ABC key, a symbols key, or the Secondary layout tool on the toolbar. The Problems list repeats the reminder for as long as the switch is on, as a warning that stops nothing.

### Label font and size

Two rows below the grid set the type for the whole layout, not one layer and not one key.

**Label font** picks the face this layout's captions are drawn in. It offers the same three sources the [theme font](https://wmkeyboard.pages.dev/themes/fonts/) does: follow whatever is already set, a Google Fonts family, or a font from your installed library. On a layout whose language uses a script with its own font list (Bengali, Devanagari, Arabic and the rest), the picker narrows to faces that carry that script.

The layout's font wins over the theme's, and loses to a font you picked for that script yourself under [Fonts](https://wmkeyboard.pages.dev/reference/settings/fonts/). For a Latin, Cyrillic or Greek layout that means the English font: set it to anything other than System default and it wins over this layout's font. The order runs: your own font for the script, then this layout's, then the theme's, then the automatic face for the script. Naming a font that isn't installed on a device costs nothing: it falls through to the next answer instead of leaving the keys blank, which is what lets a layout file carry a font across devices.

**Label size** is a multiplier, not a size. Automatic is the default. Set ×0.8 and every label on this layout is drawn a fifth smaller than it would otherwise be, whatever that turns out to be: your Appearance font size, your accessibility font size and any size the theme sets are all still in effect underneath it. That is the point. A layout that replaced the number would quietly throw away larger type you set on purpose. The range is ×0.5 to ×2.

**Label size on this layer** is the same control for one layer, and it sits above the layout-wide one. It replaces the layout size rather than stacking on it, which is what keeps the two independent: grow the letters to ×1.4 and the symbol page stays exactly where it was, as long as you give the symbol page a size of its own. Leave it on Automatic (the default on every layer) and the layer follows the layout. Setting it on a layer you haven't edited yet forks that layer's grid into this layout, the same as any other edit to an inherited layer.

**Key height on this layout**, **Key spacing on this layout**, **Left padding on this layout** and **Right padding on this layout** give this layout sizes of its own, so a one-handed grid can be narrow and tall without dragging QWERTY along with it. Each one reads "Same as everywhere" with the size it is following until you set it; tap it for a slider, and **Same as everywhere** in that dialog drops the layout's own value again. The layout's sizes win over a theme's and over **Layout & size**, and lose only to a size you set for one screen shape under **Per-screen sizing**. The sizes travel with the layout when you share or export it.

All three are stored in the layout, so they export with it and they apply only while that layout is the one you're typing on. Switching language switches them back.

### A theme of its own

A layout can name a theme, and so can each of its layers. The **Theme** row sits under the layout's name in **The whole layout**, and a **Theme for the … layer** row sits with the other layer-scoped controls in **This layer**. Once either names a theme you made, an **Edit** button on the row opens it in the theme editor. Both open the same picker the [keyboard modes](https://wmkeyboard.pages.dev/typing/modes/) use, with every built-in theme and every theme you've made, so a custom theme is as pickable as a shipped one. **Inherit** is the default on both and means what it says: the layout takes whatever theme is set, and the layer takes the layout's.

While a layout with a theme is on screen, the keyboard draws in it. It replaces the theme you set elsewhere and it replaces an automatic light-and-dark pair for as long as the layout shows, and your own selection doesn't change: switch to another language and its theme comes back. A layer's theme beats the layout's, so a symbols page can wear its own colours over a layout that has some of its own. The [panels](#panel-layouts) have the same row, **Theme for this panel**, on their tabs and in the shared panel editors: while that panel is open the keyboard draws in its theme, and a panel with none inherits the layout's. The preview in the editor draws in the theme the grid you're editing asks for. A theme you've since deleted is ignored rather than removed from the layout, so a layout exported with its theme still remembers the pairing.

In the picker, **Inherit** sits alone at the top. **Default (system)** is not another way to inherit: it is the built-in Material You theme, and it's listed first under **Built-in themes**. Picking it pins that grid to Material You whatever theme you set elsewhere. While any layout you type with, one of its pages, one of its panel tabs or a shared panel layout names a theme other than the one you picked, the [Themes screen](https://wmkeyboard.pages.dev/themes/overview/) and the [theme editor](https://wmkeyboard.pages.dev/themes/editor/) list each one in a notice at the top, and its **Clear** button puts them all back to inherit.

### Different keys sideways

**Show this key**, on a key's own page, keeps it to one way up: **Always**, **Upright** or **Sideways**. A key for one way up takes no room the other way and the rest of its row closes up, so a duplicate `g` and `v` for two thumbs can sit in the middle of a row only when the phone is turned sideways. The editor always draws every key so you can reach it. A row left with no keys goes altogether.

**Layout when sideways** is a choice for the whole layout: while the screen is sideways the keyboard draws that layout's keys instead. It offers layouts of the same language that type the same way, since only the grid changes; the language, the suggestions and what the keys compose stay this layout's. Both choices work on a copy of a built-in layout too. Copy it first from the layout's ⋮ menu.

### Tablet keys

**Add tablet keys automatically** is a switch for the whole layout, not one layer. On a tablet the keyboard widens every ordinary alphabetic grid by a column each side and fills the space with Tab, backslash, caps lock, a second shift and the rest, described in full under [size & position](https://wmkeyboard.pages.dev/typing/size-position/#on-a-tablet). It's on for every layout, including yours.

Turn it off for a layout you've already laid out wide by hand, or one you want kept exactly as authored. If the switch says the setting has no effect, the widening already declines this layout on its own (it does that for flick pads, braille, morse, any grid with no shift key, and any grid with a key that covers more than one row), and the switch is only there to record your answer in case the layout changes later.

Your phone grid is what the editor shows and what you're editing. The tablet keys are added on top of it at typing time, so there's no second grid to keep in step.

### Reaching the raw JSON

"Edit as JSON" at the bottom of the grid editor opens the layout as text, for anything the grid editor has no control for: clipboard actions, or pasting in a grid you built elsewhere.

The layout's **name**, its **language** and its **typing method** used to live here too. All three are rows in the grid editor now, so a duplicate can be renamed and re-languaged without touching the JSON. To type one language on another language's layout, QWERTZ for English say, there's a shorter way: **Use a layout from another language** on that language's screen under [Languages](https://wmkeyboard.pages.dev/languages/overview/) does the copy and the language change in one pick. The language picker is the same searchable registry dialog the foreign-layout import uses, and the language is what picks the dictionary, autocorrect, script rules, dictation and shift behaviour for the grid. Typing method defaults to *follow the language*, which is right for almost every layout. Override it only when one language has both a phonetic and a direct grid.

  languages/layout-json-editor — The raw JSON editor, with syntax highlighting and an Apply button.

This screen works differently from the grid editor. It's a draft with an explicit **Apply** button rather than autosave, because half-typed JSON isn't a valid layout and there's nothing sensible to save as you type. The text starts as your layout, JSON-encoded. Applying it parses the text, and invalid JSON shows "That is not valid layout JSON.", jumps to the first error, opens **Problems** and stops there. A valid parse always keeps the id you were already editing, so pasting a different layout's id in is deliberately ignored and you can't overwrite a different layout by accident. It then runs the result through the same repair pass used on import (see below) and shows what got fixed. If nothing needed fixing, the screen closes for you. If something did, a dialog lists the changes, and the screen closes once you have read them. Back leaves without saving.

"Edit as JSON" opens a full-screen code editor, the same one the plugin editor uses, and it knows the layout format. It's monospaced, on a code background, with the standard editor colors, numbered lines, the cursor's line lit and bracket pairs boxed. Long lines scroll sideways rather than wrapping, which keeps one number against one line; **Wrap** in the menu turns that off. Undo, redo and **Apply** sit in the top bar.

It suggests as you type, from the format itself. Type `"` inside a key and the list shows the key's properties, the required `label` first, each with its type and default. Choosing one writes the property, its colon and a blank of the right kind (`""`, `[]`, `{}`, or `true` for a switch that is off by default) with the cursor in the blank, and when that blank has values of its own the list opens again. Pick `action`, then `tool`, then a tool, and you have written a tool key without typing a word. The values come from your device: tools, editing operations and panel components by their names in your language, icon names, your themes and fonts, your secondary layouts, and every language, found by its name as well as its id. In a row, a word like `shift` offers a whole shift key, and inside `layers` the list offers each layer you have not written yet, with a row ready to fill. Matching forgives shorthand, so `lp` finds `longPress`. **Suggest** on the code keys, Ctrl+Space or Ctrl+I opens the list anywhere.

It checks as you type, too. A problem gets a squiggle and a colored line number, and **Problems** under the code lists them all; tap one to jump there. The checks follow what Apply does. An error is something Apply can't read, like a missing `label`, a number in quote marks, or an `op` this version does not have. A warning is something Apply survives by ignoring or changing it, like a misspelt property (named with the one it's close to), an action type it does not know, or a width past what the keyboard allows. Once the text parses, the grid editor's own checks run as well, so a letters layer with no delete key is flagged here the same as it is there.

A strip under the code explains whatever the cursor is on: the property, with its type, its default and a sentence about what it does, or for an action's `type`, the action and the fields it takes. Tap the strip for the whole paragraph.

On a soft keyboard, a row of code keys sits on top of it: `"`, `:`, `,`, both kinds of bracket, `true`, `false` and `null`, a second page of commands (duplicate or move a line, select, jump to a block's start or end, find, format), and two cursor keys you can slide like a trackpad. Arrange the row from **Code keys** in the menu. The menu also has **Find** (with replace, regular expressions, and case and whole-word matching), **Go to line**, **Fold all** and **Unfold all** (every object and list folds from a chevron in the margin), text size, **Copy** and **Paste**.

**Format** re-indents the whole document with two spaces per level, and keeps an object or a list on one line while that line stays under 100 characters. A three-item list of long-press alternates stays a three-item list rather than becoming five lines, and a key is one line rather than six. The screen already opens that way, so Format is only for text you've pasted or hand-edited. QWERTY reads as 252 lines here where one field per line made it 1,142. On a hardware keyboard, the editor takes the keys Visual Studio Code uses, listed below.

Typing gets a few editor habits too. A newline keeps the indentation of the line above and goes one step deeper after `{` or `[`. Typing `{`, `[` or `"` brings its closing half, typing a closing half where one already sits steps over it instead of doubling it, and backspacing over an empty pair takes both halves. None of this rewrites your text: what you typed stays exactly where you typed it until you press Format or Apply.

#### Keyboard shortcuts

With a physical keyboard, this editor and the plugin editor take the keys that Visual Studio Code uses on Windows and Linux, and ⌘ works as Ctrl. While the keyboard is attached, the menu shows the keys of each command beside it, and the row of code keys steps aside.

| Keys | What they do |
| --- | --- |
| `Ctrl+F`, `Ctrl+H` | Find, or find and replace. A selection on one line becomes the search. |
| `Enter`, `Shift+Enter`, `F3`, `Shift+F3` | The next or the previous result. In the replace field, `Enter` replaces the result that shows and `Ctrl+Alt+Enter` replaces them all. |
| `Alt+C`, `Alt+W`, `Alt+R` | In the find bar: match case, whole words, regular expression. |
| `Esc` | Closes the suggestion list or the find bar. In the code, `Esc` does not leave the editor. |
| `Ctrl+G` | Go to line. |
| `Ctrl+Space`, `Ctrl+I` | Suggest at the cursor. |
| `Tab`, `Shift+Tab` | Indent at the cursor, or move whole selected lines in. `Shift+Tab` moves them back out. |
| `Ctrl+]`, `Ctrl+[` | Move the lines at the cursor in or out. |
| `Alt+Up`, `Alt+Down` | Move the lines up or down. |
| `Shift+Alt+Up`, `Shift+Alt+Down` | Copy the lines up or down. |
| `Ctrl+Shift+K` | Delete the lines. |
| `Ctrl+Enter`, `Ctrl+Shift+Enter` | A new line below or above, with the same indent. |
| `Ctrl+X`, `Ctrl+C`, `Ctrl+V` | With nothing selected, cut or copy the whole line, and paste it back in above the line of the cursor. |
| `Ctrl+L` | Select the line. Each press adds the next line. |
| `Shift+Alt+Right`, `Shift+Alt+Left` | Grow the selection to the word, the line, the block and the document, and shrink it back. |
| `Home` | The first character of the line that is not a space, then the start of the line. |
| `Ctrl+Home`, `Ctrl+End`, `Page Up`, `Page Down` | The start, the end, one screen up or one screen down. Add `Shift` to select. |
| `Ctrl+Up`, `Ctrl+Down` | Scroll one line, and leave the cursor where it is. |
| `Ctrl+Shift+\` | Go to the matching bracket. |
| `Ctrl+Shift+[`, `Ctrl+Shift+]` | Fold or unfold the block at the cursor. |
| `Ctrl+K Ctrl+0`, `Ctrl+K Ctrl+J` | Fold every block, unfold every block. |
| `Ctrl+Z`, `Ctrl+Y`, `Ctrl+Shift+Z` | Undo, redo. |
| `Shift+Alt+F` | Format. |
| `Alt+Z` | Wrap long lines, or stop wrapping them. |
| `Ctrl+=`, `Ctrl+-`, `Ctrl+0` | Larger text, smaller text, the usual size. The size and the wrapping stay as you left them, in every code editor in the app. |
| `F8`, `Shift+F8` | The next or the previous problem. |
| `Ctrl+Shift+M` | Show or hide **Problems**. |
| `F1`, `Ctrl+Shift+P` | Open the menu. |
| `Ctrl+S` | Here, **Apply**. In the plugin editor, save the draft now. |
| `Ctrl+/`, `Shift+Alt+A` | Plugin editor: a line comment, a block comment. JSON has no comments. |
| `F5`, `Shift+F5` | Plugin editor: run or stop the plugin. |
| `F12`, `F2` | Plugin editor: go to the definition, rename. |
| `Ctrl+Shift+O`, `Ctrl+Shift+Y` | Plugin editor: **Outline**, **Console**. |

The keys that the text field knows already keep working: `Ctrl+A`, the arrow keys with `Ctrl` and `Shift`, and `Ctrl+Backspace` to delete a word. The editor has one cursor, so the VS Code keys for more cursors, such as `Ctrl+D`, do nothing here.

When WM Keyboard is your keyboard, it takes `Ctrl+Space` to change the language before the editor gets the key (see [Switching language](https://wmkeyboard.pages.dev/typing/hardware-keyboard/#switching-language)). Use `Ctrl+I` for suggestions, or turn that setting off.

#### Every property and value

Each section below opens to the full list for one part of the format: every property an object takes, with its type, its default and what it does, every action type with its fields, and every value a fixed list allows. The lists are read from the app's own code when these pages are built, so they match the version they describe. The editor's suggestions offer the same values as you type.

## Secondary layouts

A secondary layout is a grid of your own that isn't a language: a symbols page arranged your way, a macro pad of Ctrl shortcuts, a strip of arrows and function keys. You reach it from a key or from the toolbar rather than from the 🌐 key, and it shows *over* the letters of whatever layout you're typing with, so the dictionary and autocorrect stay that layout's. It never appears under Languages, is never a stop in the language cycle, and has no on/off switch: it's live as soon as something opens it.

**Start a secondary layout** heads its own group on the Key layouts screen. It builds three rows of blank keys plus an ABC key and opens the editor, and that's all it builds: nothing is copied from a shipped grid, and the repair pass that gives an ordinary layout its delete, space and enter keys leaves a secondary layout alone. A macro pad has no business being handed a space bar. The editor is the same one, minus the parts that don't apply: there's one grid rather than layer chips, no language or typing-method row, and no tablet widening.

Two things open one:

- **A key**, on any layout, set to the "Open a layout" action and pointed at the secondary layout by name. Pressing it shows that grid; pressing it again, an ABC key, or a ?123 key leaves it. The same action works as a press-and-hold alternate, so a hold on ?123 can reach your own symbols page without spending a key on it.
- **The Secondary layout tool** on the [toolbar](https://wmkeyboard.pages.dev/tools/overview/). With one secondary layout it shows that one; with several it opens a picker panel listing them by name, and a tap on a name shows that grid. **Layout to show** on the tool's page pins one instead, so the tool skips the picker and always shows that layout; "Ask each time" is the default. The tool only appears once you have at least one secondary layout: until then its row on the Tools screen is greyed out and reads "Needs a secondary layout", and its switch cannot be turned on. It's lit whenever any secondary layout is up, however it got there, so a press then always takes it down.

A secondary layout with no key that leaves it gets a warning in the Problems list ("make sure you have a way to exit this layout"), not a block: a fresh field closes it anyway unless you've also switched on [Keep this layer open](#keeping-a-layer-open), which is the combination the warning is really about. A key naming a secondary layout you've since deleted does nothing rather than opening something else.

Secondary layouts export and import like any other layout file, keys and all. The "open a layout" keys inside one carry the *id* of the layout they point at, so a set of them shared together keeps working; a single one imported on its own has keys that point at nothing until you re-pick them.

## Panel layouts

The emoji panel, the GIF and sticker panels, the clipboard panel, the text editing panel, the trackpad, the Numpad and the handwriting panel are layouts too: grids drawn by the same editor, with the same row tools, key sheet and undo, and the keyboard draws each panel from its grid.

They live in two places, and the second inherits from the first:

- **Shared panel layouts.** Layout & size / Key layouts / Panel layouts lists the eight panels (seven on the F-Droid build, which has no handwriting) and says **Standard** or **Your own** under each. Each one's editor is headed with the panel's own name ("Emoji", "Clipboard") rather than with the words "Panel layout", so the row and the page it opens carry the same word. These are what every layout uses unless it has a grid of its own. The same editor opens from the Emoji screen, the Clipboard screen, and the GIFs, Stickers, Text editing, Trackpad, Numpad and Handwriting tools' pages. **Use the standard layout** at the bottom puts the shipped grid back.
- **A layout's own panels.** Every key layout's editor has a tab for each panel, after the Fn tab, on the same strip as Letters and Symbols. A panel tab shows the shared grid until you change something there, then it forks a copy that belongs to this layout only, exactly the way an untouched Symbols tab inherits the shipped page. A pencil on the tab marks a panel this layout owns. **Use the shared panel** at the bottom drops the copy again. So a Bangla layout can carry its own text editing pad while your other layouts keep the shared one, and the panel's own font size, "keep this layer open" switch and rows are all per layout there.

Which grid the keyboard draws is decided when a field opens: the layout you're typing with, its own grid if it has one, else the shared one, else the shipped one.

What makes a panel different from a key layout is the **component**: a cell that holds a live part of the panel rather than a key. The emoji panel has four of them (the category tabs, the search box, the emoji grid and the switch to the GIF and sticker panels), the clipboard panel has four (the search box, the grid or list switch, the strip of copied fragments and the history), the GIF and sticker panels have two (their search and results, which is the search box, the source and category chips and the results as one cell, and the same switch the emoji panel has), the trackpad and the handwriting panel have one each (the pointing surface, and the writing area with its language chip, undo and full-screen buttons on it), and the text editing panel and the Numpad have none, since they are all keys. A component is placed like any key: pick **Panel component** in the Action picker, choose which one, then give it a width and a row span. The preview draws it hatched with its name. Every other cell is an ordinary key with every option a key layout offers, so a panel can carry a bottom row of abc, space and backspace, an Enter key, a tool key, or a text editing action.

Heights work differently from a key layout, because a panel always fills exactly the space the key rows would take, so the keyboard never changes size when a panel opens. Rows made of keys, or of the strip components (the tabs, a search box, the fragment strip), stay at key height, multiplied by their row height like any row. Rows that start the panel's body (the emoji grid, the clipboard history, the GIF or sticker results, the trackpad surface, the handwriting area) share whatever height the other rows leave, in proportion to their row heights. A strip with nothing to show, like the fragment strip when no clip has a code in it, takes no row at all. On the text editing panel there is no component, so all of its rows share the height by their row heights, which is how that pad has always been drawn. With the full-screen emoji panel on, a first row made only of the tabs, the search box and the switch moves up into the header beside the back button, the way the tabs always sat there. A GIF or sticker panel you haven't laid out yourself borrows its bottom row from the emoji panel: the row that holds the switch when it's keys and the switch alone, else the emoji panel's last row of keys, so a bottom row you rearranged goes with you from panel to panel. Once you edit the GIF or sticker panel, it is drawn as you laid it out. If it has no switch component, the switch sits beside its search box while the switch setting is on. The full-screen clipboard panel does the same with a first row made only of its search box and its grid or list switch. The switch hides while the history is empty, so a row holding just the switch and a hidden search box takes no room until there's a clip.

**Keep this layer open** is on every panel's editor too: on, that panel survives the keyboard closing and a change of field, and is left only by a key, a tool or the panel's own abc key. Back is the exception: for a panel you asked to keep, the ⌄ in the navigation bar puts the keyboard away as it does everywhere else, rather than closing the panel, and the panel is still there next time. (Esc on a physical keyboard still closes it, since there is no ⌄ there to use instead.) The Problems list carries a reminder for as long as it's on.

A few rules keep a panel drawable, and the editor lists them under Problems like a key layout's. A panel must hold its main component once (the emoji grid, the clipboard history, the GIF or sticker search and results, the trackpad surface, the handwriting area), and no component more than once. A component from another panel is not allowed, and neither are the two keys only a converted layout can use (a kana variant key, a Keyman key). Everything else a key layout offers works on a panel too: Shift and caps lock case the panel's own text keys and extend its cursor moves into a selection, the braille and morse keys type through the same engines they do on a grid, and ?123, Fn and "open a layout" close the panel on their way to that layer, the way abc closes it on the way to the letters. A drag off ?123 or abc closes nothing: it [shows that layer over the panel](https://wmkeyboard.pages.dev/typing/gestures/#dragging-from-123-or-abc) while you hold and types the key you lift on. The keyboard repairs what it draws rather than refusing: a missing main component is added in a row of its own, duplicates and foreign components are dropped, and an empty panel falls back to the shipped one. A panel without an abc key gets a warning and nothing more, since the toolbar and the Back button both leave a panel. With **Keep this layer open** on, though, Back no longer does, which is what the reminder above is about.

Two things stay outside the layout. While you are typing into the emoji, GIF, sticker or clipboard **search**, or editing a clip, the panel shrinks to its compact form and the real key rows come back underneath, exactly as before; the layout is what the panel shows the rest of the time. And the full-screen setting for the emoji, GIF, sticker and clipboard panels still applies: on, the layout fills the whole keyboard under a slim back header.

**Edit as JSON** works for a panel too, and the file names the panel and holds the grid in the same shape a key layout's layer does, with `"type": "field"` and a `kind` such as `emoji_grid` on a component cell.

## Layout files and sharing

A layout exports as a `.wmlayout.json` file: the same envelope format every shipped asset layout already uses internally, tagged `"format": "wmkeyboard-layout"`. Export and duplicate are in the ⋮ menu on every row of the layout gallery, both for your own layouts and the built-in ones.

**Export layouts in use**, under **Import a layout** at the bottom of the gallery, puts every layout that is on and every one of your own, secondary layouts included, in one `.wmlayouts.json` file, tagged `"format": "wmkeyboard-layouts"`, named like `Layouts-20261027-1405.wmlayouts.json`. **Import a layout** reads it back: the dialog lists the layouts inside and adds them all, beside any you already have. **Import a layout** also takes several files in one pick, layouts and files of several layouts alike, and lists everything they hold in that same dialog; the names of any files it could not read are said in a message after it.

To act on several of your own layouts at once, long-press one under Your layouts or Secondary layouts. The rows turn into checkboxes: tap more of them, or use **Select all** in the bar at the bottom. The upload icon there saves the ticked layouts in one `.wmlayouts.json` file, the same file **Export layouts in use** writes, and the trash icon deletes them after asking once. While picking, a tap ticks a row instead of opening the editor; the close button or Back stops picking. Built-in layouts can't be picked, since what they have to delete is only your edits, and **Reset** on each does that. Each keeps the id it had when nothing on this phone uses it, so a key in one layout that opens another still opens it after the move.

One layer travels on the clipboard instead of in a file, tagged `"format": "wmkeyboard-layer"`. See [Copying a layer](#copying-a-layer) for when that's the easier way to share a grid.

The exported filename comes from the layout's name (stripped of anything that isn't a letter, digit, space, underscore, or hyphen), falling back to "layout" if that leaves nothing, followed by the date and time of the export: `Mine-20261027-1405.wmlayout.json`. The file is indented JSON, so it reads and compares cleanly in any text editor. The save dialog opens in the folder your last layout export went to, and importing opens in the folder you last imported a layout from, apart from where backups go. Some phones (Samsung among them) only follow that when handed a folder rather than a file, so the app hands them the folder. Import accepts `.wmlayout.json` files through the in-app "Import a layout" row, or by tapping one directly in a file manager or chat app, since WM Keyboard registers as a handler for the `.wmlayout.json` extension. Either path shows the same confirm dialog with the layout's name, and an imported layout is never switched on automatically. It's added to your layouts, and you turn it on under Layout & size when you're ready. Layouts from other keyboards (FlorisBoard, HeliBoard, FUTO, a Keys Cafe `.kcf` from Samsung Keyboard, or a layout file made for a Windows, Mac or Linux computer) go through the second import row instead; see [Importing from other keyboards](https://wmkeyboard.pages.dev/reference/importing-other-keyboards/). The third row, **Import an input method**, makes a layout out of a `.cin` table, a Rime schema or an m17n `.mim` file; see [Input methods](https://wmkeyboard.pages.dev/reference/importing-other-keyboards/#input-methods-cin-rime-and-m17n).

Import is deliberately permissive about almost everything except one thing: the format tag inside the file. Malformed values, missing fields, an oversized row: all of that gets silently fixed by a repair pass, and the fixes are listed in the confirm dialog so you know what changed. Only a file that isn't recognizably a WM Keyboard layout at all gets rejected.

File type detection reads the file's content, not its extension. If a `.wmlayout.json` actually contains a settings backup (people do rename files), it imports as a backup, not a broken layout.

## Installing community layouts

Layout is one of the addon types a [repository](https://wmkeyboard.pages.dev/addons/installing/) can offer. Installing one works like a manual import (it's added to your layouts under Layout & size, not switched on) except the confirm prompt asks "Turn it on so you can type with it?" specifically, since a layout you can't type with yet isn't much use installed. Turning it on adds it to the same enabled-layouts list the Your layouts toggle uses.

Addon-installed layouts don't show up in the "Your layouts" group on the Key layouts screen, which is for layouts you built here. The switch that turns an installed layout on lives under Layout & size → Your layouts instead, and uninstalling it (from the [Addons](https://wmkeyboard.pages.dev/addons/managing/) screen) removes it the same way deleting a layout does.

## Options

Layout & size / Key layouts

This screen has two sections plus two import rows between them. **Your layouts** starts with **Start a new layout** and then lists every layout you've made or edited yourself, on or off. Duplicate leaves its result switched off (Import offers **Import and turn on** as well), and hiding an off layout would mean the thing you just made disappearing; the switch on each row turns it on in place. **Built in** lists the shipped layouts you've enabled (3,395 ship in total: 36 compiled built-ins, including QWERTY's English alternates like Dvorak and Colemak, plus 3,352 JSON assets covering the long tail of languages and scripts, including regional Latin standards like BÉPO and 862 converted Keyman keyboards). That's the one filtered group, so enable a layout for a language first under [Languages → Switching](https://wmkeyboard.pages.dev/languages/switching/) and it shows up here to edit.

There's no default value to quote for "which layouts exist": a fresh install starts with no custom layouts of your own. The active layout defaults to QWERTY, and the initial enabled set is seeded from your phone's own language list plus QWERTY. See [Languages overview](https://wmkeyboard.pages.dev/languages/overview/#what-ships) for how that seeding works.

## Details & edge cases

**Editing a shipped layout doesn't fork a new one.** Change a key on the built-in QWERTY or on a shipped Vietnamese Telex grid, and the edit is stored as an override under that same layout's id. It doesn't become a separate custom layout. That's why the delete button on a shipped row reads "Reset" instead of "Delete": there's no copy to delete, only an override to drop, which brings the original grid back.

**Duplicating never activates.** Copying any layout (shipped or your own) creates a new layout and opens it for editing, but never switches to it. A half-finished copy becoming the live keyboard mid-edit is exactly the situation where you'd be stuck unable to type well enough to undo it.

**Two passes keep you from getting locked out. One only reports, the other rewrites.** While you're editing, a continuous check flags problems without touching your grid. Blocking findings are a Letters layer with no keys at all or with no Delete, Space or Enter key, a layer with more than 8 rows, a row over 24 keys, a key with zero or non-finite width, a key set to cover fewer than 1 or more than 8 rows, a Braille key naming a dot outside 1–6, a key whose action this build doesn't recognise, and a cycled layer (Symbols, Symbols 2, Fn) with no way back to Letters. Seven findings are warnings that stop nothing: a missing Shift key, since a symbols-forward layout may not want one; an empty row; a row whose keys add up wider than the layer's grid; a key set to cover more rows than the layer has, which the keyboard stops at the last row; a key with neither a label nor an output, which types nothing and is deleted the moment the layout turns on; a layer set to [stay open](#keeping-a-layer-open), as a reminder that it needs a way off; and a [secondary layout](#secondary-layouts) with no key that leaves it. A secondary layout skips the delete, space and enter checks altogether, and the repair pass skips them too.

That reporting pass is what the enable toggle consults, wherever it lives for that layout. Try to switch on a layout with a blocking finding and it refuses, with a dialog headed "Fix these problems in the layout editor first:" that names every reason. It's the same list the editor prints, so the two can't drift apart, and turning a layout off is never blocked.

The second pass rewrites. It drops unrecognised keys, caps an oversized key or row, and guarantees the Letters layer has a delete, space and enter key by adding default ones if they're missing. It runs on import, when a layout becomes the one you're typing on, and again each time the keyboard builds the grid to draw. That last one is why editing a layout that's already switched on is survivable: your changes go live keystroke by keystroke, and the repair puts a backspace key back the moment you delete the last one. It's deliberately never applied to what's *stored* while you edit, because it would fight you, silently regrowing a row you just removed.

**A split keyboard ignores row spans.** Split mode cuts every row near its own midpoint and pushes the halves apart, and a key belonging to two rows can't survive that: the two halves would part company underneath it. So on a split board a tall key draws as an ordinary one-row key and the rows below it fill the width, which is what the same grid looked like before you set the span. Turn split off to see the layout as authored.

**Two version numbers, and they mean different things.** The file envelope has its own version (currently 1, unused for migration so far; only the format tag itself is checked on import). A layout's internal spec also carries its own version, separately, bumped when a stored field's shape changes.

**Below is a real shipped layout** (the Afar QWERTY grid) rendered from its actual `.wmlayout.json` file, the same format export and import use:
