Compare commits

...

13 Commits

Author SHA1 Message Date
Scai 3549722c4b
Merge 57fbee1490 into b91a995918 2025-05-21 12:39:17 -05:00
Daniel Lorch b91a995918
feat(ui): enable multiple keyboard layouts for "paste text" to remote host (#405)
* Enable multiple keyboard layouts for paste text from host

* Trema is the more robust method for capital umlauts

* Improve error handling and pre-loading

* Improve accent handling

* Remove obscure Alt-Gr keys, unsure if they are supported everywhere

* Add Swiss French

* Change line ordering

* Fix whitespace

* Add French (France)

* Add English (UK)

* Add Swedish

* Add Spanish

* Fix fr_FR special characters

* Add more keys to Spanish

* Remove default value shift: false

* Add Norwegian

* Operator precedence 🤦

* Add Italian

* Add Czech

* Move guard statements outside of loop

* Move language name definitions into the keyboard layout files

* Change the locale names to their native language

German->Deutsch et. al.

* Move hold key handling into Go backend analogous to https://www.kernel.org/doc/Documentation/usb/gadget_hid.txt

* Remove trailing whitespace

* Fix

* Add Belgisch Nederlands

* Add JSONRPC handling

* Use useSettingsStore

* Revert "Move hold key handling into Go backend analogous to https://www.kernel.org/doc/Documentation/usb/gadget_hid.txt"

This reverts commit 146cee9309.

* Move FeatureFlag to navigation

* Fix: flip Y/Z

* Add useEffect dependencies

* Embolden language

* Add to useCallback dependencies

---------

Co-authored-by: Marc Brooks <IDisposable@gmail.com>
2025-05-21 17:40:48 +02:00
Marc Brooks 590c606bb1
Handle panics when calling the RPCHandler instead of dying (#488)
Added a wrapper around the callRPCHandler function to recover from panic and translate that to and error so that the RPC thread doesn't just die when something malformed comes in. This should keep the Jet flying.
2025-05-21 15:18:11 +02:00
Scai 57fbee1490 chore: remove unnecessary actions 2025-01-08 00:51:44 +00:00
Scai 0e65c0a9a9 chore: add last go version to matrix 2025-01-08 00:41:33 +00:00
Scai 2dafb5c9c1 chore: update existing comment 2025-01-08 00:37:19 +00:00
Scai 566305549f chore: fix table comment 2025-01-08 00:34:49 +00:00
Scai 1505c37e4c chore: comment fix issues 2025-01-08 00:29:04 +00:00
Scai 564eee9b00 chore: change to artifact 2025-01-08 00:19:52 +00:00
Scai fab575dbe0 chore: permissions update on push 2025-01-08 00:17:38 +00:00
Scai 97958e7b86 chore: push workflow 2025-01-08 00:13:52 +00:00
Scai 2f7042df18 chore: increase permissions to content workflow 2025-01-07 22:58:23 +00:00
Scai 2cadda4e00 chore: create release workflow 2025-01-07 22:40:03 +00:00
24 changed files with 2184 additions and 139 deletions

126
.github/workflows/push.yaml vendored Normal file
View File

@ -0,0 +1,126 @@
name: Push
on:
push:
branches:
- main
pull_request:
branches:
- main
permissions:
id-token: write
contents: read
pull-requests: write
jobs:
build:
name: Build
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-22.04]
go: [1.21, 1.23.4]
node: [21]
goos: [linux]
goarch: [arm]
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
- name: Install Dependencies
working-directory: ui
run: npm ci
- name: Build UI
working-directory: ui
run: npm run build:device
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: ${{ matrix.go }}
- name: Install Go Dependencies
run: |
go mod download
- name: Build Binaries
env:
GOOS: ${{ matrix.goos }}
GOARCH: ${{ matrix.goarch }}
run: |
GOOS=linux GOARCH=arm GOARM=7 go build -ldflags="-s -w -X kvm.builtAppVersion=dev-${GIT_COMMIT:0:7}" -o bin/jetkvm_app cmd/main.go
chmod 755 bin/jetkvm_app
- name: Upload Debug Artifact
uses: actions/upload-artifact@v4
if: ${{ (github.ref == 'refs/heads/main' || github.event_name == 'pull_request') && matrix.go == '1.21' }}
with:
name: jetkvm_app_debug
path: bin/jetkvm_app
comment:
name: Comment
runs-on: ubuntu-latest
needs: build
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Generate Links
id: linksa
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
ARTIFACT_ID=$(gh api repos/${{ github.repository }}/actions/runs/${{ github.run_id }}/artifacts --jq '.artifacts[0].id')
echo "ARTIFACT_URL=https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}/artifacts/$ARTIFACT_ID" >> $GITHUB_ENV
echo "LATEST_COMMIT=$(git rev-parse --short HEAD)" >> $GITHUB_ENV
- name: Comment on PR
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
TITLE="${{ github.event.pull_request.title }}"
PR_NUMBER=${{ github.event.pull_request.number }}
else
TITLE="main branch"
fi
COMMENT=$(cat << EOF
✅ **Build successfully for $TITLE!**
| Name | Link |
|------------------|----------------------------------------------------------------------|
| 🔗 Debug Binary | [Download](${{ env.ARTIFACT_URL }}) |
| 🔗 Latest commit | [${{ env.LATEST_COMMIT }}](https://github.com/${{ github.repository }}/commit/${{ github.sha }}) |
EOF
)
# Post Comment
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
# Look for an existing comment
COMMENT_ID=$(gh api repos/${{ github.repository }}/issues/$PR_NUMBER/comments \
--jq '.[] | select(.body | contains("✅ **Build successfully for")) | .id')
if [ -z "$COMMENT_ID" ]; then
# Create a new comment if none exists
gh pr comment $PR_NUMBER --body "$COMMENT"
else
# Update the existing comment
gh api repos/${{ github.repository }}/issues/comments/$COMMENT_ID \
--method PATCH \
-f body="$COMMENT"
fi
else
# Log the comment for main branch
echo "$COMMENT"
fi

91
.github/workflows/release.yaml vendored Normal file
View File

@ -0,0 +1,91 @@
name: Release
on:
push:
tags:
- "v*"
jobs:
release:
name: Release
runs-on: ubuntu-22.04
permissions:
contents: write
packages: write
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 21
- name: Install Dependencies
working-directory: ui
run: npm ci
- name: Build UI
working-directory: ui
run: npm run build:device
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: 1.21
- name: Build Release Binaries
env:
REF: ${{ github.ref }}
run: |
GOOS=linux GOARCH=arm GOARM=7 go build -ldflags="-s -w -X kvm.builtAppVersion=${REF:11}" -o bin/jetkvm_app cmd/main.go
chmod 755 bin/jetkvm_app
- name: Create checksum
env:
REF: ${{ github.ref }}
run: |
SUM=$(shasum -a 256 bin/jetkvm_app | cut -d ' ' -f 1)
echo -e "\n#### SHA256 Checksum\n\`\`\`\n$SUM bin/jetkvm_app\n\`\`\`\n" >> ./RELEASE_CHANGELOG
echo -e "$SUM bin/jetkvm_app\n" > checksums.txt
- name: Create Release Branch
env:
REF: ${{ github.ref }}
run: |
BRANCH=release/${REF:10}
git config --local user.email "github-actions[bot]@users.noreply.github.com"
git config --local user.name "github-actions[bot]"
git checkout -b ${BRANCH}
git push -u origin ${BRANCH}
- name: Create Release
id: create_release
uses: softprops/action-gh-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
draft: true
prerelease: ${{ contains(github.ref, 'rc') || contains(github.ref, 'beta') || contains(github.ref, 'alpha') }}
body_path: ./RELEASE_CHANGELOG
- name: Upload JetKVM binary
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: bin/jetkvm_app
asset_name: jetkvm_app
asset_content_type: application/octet-stream
- name: Upload checksum
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./checksums.txt
asset_name: checksums.txt
asset_content_type: text/plain

View File

@ -87,6 +87,7 @@ type Config struct {
LocalAuthMode string `json:"localAuthMode"` //TODO: fix it with migration LocalAuthMode string `json:"localAuthMode"` //TODO: fix it with migration
WakeOnLanDevices []WakeOnLanDevice `json:"wake_on_lan_devices"` WakeOnLanDevices []WakeOnLanDevice `json:"wake_on_lan_devices"`
KeyboardMacros []KeyboardMacro `json:"keyboard_macros"` KeyboardMacros []KeyboardMacro `json:"keyboard_macros"`
KeyboardLayout string `json:"keyboard_layout"`
EdidString string `json:"hdmi_edid_string"` EdidString string `json:"hdmi_edid_string"`
ActiveExtension string `json:"active_extension"` ActiveExtension string `json:"active_extension"`
DisplayRotation string `json:"display_rotation"` DisplayRotation string `json:"display_rotation"`
@ -109,6 +110,7 @@ var defaultConfig = &Config{
ActiveExtension: "", ActiveExtension: "",
KeyboardMacros: []KeyboardMacro{}, KeyboardMacros: []KeyboardMacro{},
DisplayRotation: "270", DisplayRotation: "270",
KeyboardLayout: "en-US",
DisplayMaxBrightness: 64, DisplayMaxBrightness: 64,
DisplayDimAfterSec: 120, // 2 minutes DisplayDimAfterSec: 120, // 2 minutes
DisplayOffAfterSec: 1800, // 30 minutes DisplayOffAfterSec: 1800, // 30 minutes

View File

@ -466,7 +466,31 @@ func rpcSetTLSState(state TLSState) error {
return nil return nil
} }
func callRPCHandler(handler RPCHandler, params map[string]interface{}) (interface{}, error) { type RPCHandler struct {
Func interface{}
Params []string
}
// call the handler but recover from a panic to ensure our RPC thread doesn't collapse on malformed calls
func callRPCHandler(handler RPCHandler, params map[string]interface{}) (result interface{}, err error) {
// Use defer to recover from a panic
defer func() {
if r := recover(); r != nil {
// Convert the panic to an error
if e, ok := r.(error); ok {
err = e
} else {
err = fmt.Errorf("panic occurred: %v", r)
}
}
}()
// Call the handler
result, err = riskyCallRPCHandler(handler, params)
return result, err
}
func riskyCallRPCHandler(handler RPCHandler, params map[string]interface{}) (interface{}, error) {
handlerValue := reflect.ValueOf(handler.Func) handlerValue := reflect.ValueOf(handler.Func)
handlerType := handlerValue.Type() handlerType := handlerValue.Type()
@ -563,11 +587,6 @@ func callRPCHandler(handler RPCHandler, params map[string]interface{}) (interfac
return nil, errors.New("unexpected return values from handler") return nil, errors.New("unexpected return values from handler")
} }
type RPCHandler struct {
Func interface{}
Params []string
}
func rpcSetMassStorageMode(mode string) (string, error) { func rpcSetMassStorageMode(mode string) (string, error) {
logger.Info().Str("mode", mode).Msg("Setting mass storage mode") logger.Info().Str("mode", mode).Msg("Setting mass storage mode")
var cdrom bool var cdrom bool
@ -882,6 +901,18 @@ func rpcSetCloudUrl(apiUrl string, appUrl string) error {
return nil return nil
} }
func rpcGetKeyboardLayout() (string, error) {
return config.KeyboardLayout, nil
}
func rpcSetKeyboardLayout(layout string) error {
config.KeyboardLayout = layout
if err := SaveConfig(); err != nil {
return fmt.Errorf("failed to save config: %w", err)
}
return nil
}
func getKeyboardMacros() (interface{}, error) { func getKeyboardMacros() (interface{}, error) {
macros := make([]KeyboardMacro, len(config.KeyboardMacros)) macros := make([]KeyboardMacro, len(config.KeyboardMacros))
copy(macros, config.KeyboardMacros) copy(macros, config.KeyboardMacros)
@ -1047,6 +1078,8 @@ var rpcHandlers = map[string]RPCHandler{
"setUsbDevices": {Func: rpcSetUsbDevices, Params: []string{"devices"}}, "setUsbDevices": {Func: rpcSetUsbDevices, Params: []string{"devices"}},
"setUsbDeviceState": {Func: rpcSetUsbDeviceState, Params: []string{"device", "enabled"}}, "setUsbDeviceState": {Func: rpcSetUsbDeviceState, Params: []string{"device", "enabled"}},
"setCloudUrl": {Func: rpcSetCloudUrl, Params: []string{"apiUrl", "appUrl"}}, "setCloudUrl": {Func: rpcSetCloudUrl, Params: []string{"apiUrl", "appUrl"}},
"getKeyboardLayout": {Func: rpcGetKeyboardLayout},
"setKeyboardLayout": {Func: rpcSetKeyboardLayout, Params: []string{"layout"}},
"getKeyboardMacros": {Func: getKeyboardMacros}, "getKeyboardMacros": {Func: getKeyboardMacros},
"setKeyboardMacros": {Func: setKeyboardMacros, Params: []string{"params"}}, "setKeyboardMacros": {Func: setKeyboardMacros, Params: []string{"params"}},
} }

View File

@ -8,14 +8,21 @@ import { GridCard } from "@components/Card";
import { TextAreaWithLabel } from "@components/TextArea"; import { TextAreaWithLabel } from "@components/TextArea";
import { SettingsPageHeader } from "@components/SettingsPageheader"; import { SettingsPageHeader } from "@components/SettingsPageheader";
import { useJsonRpc } from "@/hooks/useJsonRpc"; import { useJsonRpc } from "@/hooks/useJsonRpc";
import { useHidStore, useRTCStore, useUiStore } from "@/hooks/stores"; import { useHidStore, useRTCStore, useUiStore, useSettingsStore } from "@/hooks/stores";
import { chars, keys, modifiers } from "@/keyboardMappings"; import { keys, modifiers } from "@/keyboardMappings";
import { layouts, chars } from "@/keyboardLayouts";
import notifications from "@/notifications"; import notifications from "@/notifications";
const hidKeyboardPayload = (keys: number[], modifier: number) => { const hidKeyboardPayload = (keys: number[], modifier: number) => {
return { keys, modifier }; return { keys, modifier };
}; };
const modifierCode = (shift?: boolean, altRight?: boolean) => {
return (shift ? modifiers["ShiftLeft"] : 0)
| (altRight ? modifiers["AltRight"] : 0)
}
const noModifier = 0
export default function PasteModal() { export default function PasteModal() {
const TextAreaRef = useRef<HTMLTextAreaElement>(null); const TextAreaRef = useRef<HTMLTextAreaElement>(null);
const setPasteMode = useHidStore(state => state.setPasteModeEnabled); const setPasteMode = useHidStore(state => state.setPasteModeEnabled);
@ -27,6 +34,18 @@ export default function PasteModal() {
const [invalidChars, setInvalidChars] = useState<string[]>([]); const [invalidChars, setInvalidChars] = useState<string[]>([]);
const close = useClose(); const close = useClose();
const keyboardLayout = useSettingsStore(state => state.keyboardLayout);
const setKeyboardLayout = useSettingsStore(
state => state.setKeyboardLayout,
);
useEffect(() => {
send("getKeyboardLayout", {}, resp => {
if ("error" in resp) return;
setKeyboardLayout(resp.result as string);
});
}, [send, setKeyboardLayout]);
const onCancelPasteMode = useCallback(() => { const onCancelPasteMode = useCallback(() => {
setPasteMode(false); setPasteMode(false);
setDisableVideoFocusTrap(false); setDisableVideoFocusTrap(false);
@ -37,33 +56,49 @@ export default function PasteModal() {
setPasteMode(false); setPasteMode(false);
setDisableVideoFocusTrap(false); setDisableVideoFocusTrap(false);
if (rpcDataChannel?.readyState !== "open" || !TextAreaRef.current) return; if (rpcDataChannel?.readyState !== "open" || !TextAreaRef.current) return;
if (!keyboardLayout) return;
if (!chars[keyboardLayout]) return;
const text = TextAreaRef.current.value; const text = TextAreaRef.current.value;
try { try {
for (const char of text) { for (const char of text) {
const { key, shift } = chars[char] ?? {}; const { key, shift, altRight, deadKey, accentKey } = chars[keyboardLayout][char]
if (!key) continue; if (!key) continue;
await new Promise<void>((resolve, reject) => { const keyz = [ keys[key] ];
send( const modz = [ modifierCode(shift, altRight) ];
"keyboardReport",
hidKeyboardPayload([keys[key]], shift ? modifiers["ShiftLeft"] : 0), if (deadKey) {
params => { keyz.push(keys["Space"]);
if ("error" in params) return reject(params.error); modz.push(noModifier);
send("keyboardReport", hidKeyboardPayload([], 0), params => { }
if (accentKey) {
keyz.unshift(keys[accentKey.key])
modz.unshift(modifierCode(accentKey.shift, accentKey.altRight))
}
for (const [index, kei] of keyz.entries()) {
await new Promise<void>((resolve, reject) => {
send(
"keyboardReport",
hidKeyboardPayload([kei], modz[index]),
params => {
if ("error" in params) return reject(params.error); if ("error" in params) return reject(params.error);
resolve(); send("keyboardReport", hidKeyboardPayload([], 0), params => {
}); if ("error" in params) return reject(params.error);
}, resolve();
); });
}); },
);
});
}
} }
} catch (error) { } catch (error) {
console.error(error); console.error(error);
notifications.error("Failed to paste text"); notifications.error("Failed to paste text");
} }
}, [rpcDataChannel?.readyState, send, setDisableVideoFocusTrap, setPasteMode]); }, [rpcDataChannel?.readyState, send, setDisableVideoFocusTrap, setPasteMode, keyboardLayout]);
useEffect(() => { useEffect(() => {
if (TextAreaRef.current) { if (TextAreaRef.current) {
@ -113,7 +148,7 @@ export default function PasteModal() {
// @ts-expect-error TS doesn't recognize Intl.Segmenter in some environments // @ts-expect-error TS doesn't recognize Intl.Segmenter in some environments
[...new Intl.Segmenter().segment(value)] [...new Intl.Segmenter().segment(value)]
.map(x => x.segment) .map(x => x.segment)
.filter(char => !chars[char]), .filter(char => !chars[keyboardLayout][char]),
), ),
]; ];
@ -132,6 +167,11 @@ export default function PasteModal() {
)} )}
</div> </div>
</div> </div>
<div className="space-y-4">
<p className="text-xs text-slate-600 dark:text-slate-400">
Sending text using keyboard layout: {layouts[keyboardLayout]}
</p>
</div>
</div> </div>
</div> </div>
</div> </div>

View File

@ -302,6 +302,9 @@ interface SettingsState {
backlightSettings: BacklightSettings; backlightSettings: BacklightSettings;
setBacklightSettings: (settings: BacklightSettings) => void; setBacklightSettings: (settings: BacklightSettings) => void;
keyboardLayout: string;
setKeyboardLayout: (layout: string) => void;
} }
export const useSettingsStore = create( export const useSettingsStore = create(
@ -330,6 +333,9 @@ export const useSettingsStore = create(
}, },
setBacklightSettings: (settings: BacklightSettings) => setBacklightSettings: (settings: BacklightSettings) =>
set({ backlightSettings: settings }), set({ backlightSettings: settings }),
keyboardLayout: "en-US",
setKeyboardLayout: layout => set({ keyboardLayout: layout }),
}), }),
{ {
name: "settings", name: "settings",

45
ui/src/keyboardLayouts.ts Normal file
View File

@ -0,0 +1,45 @@
import { chars as chars_fr_BE, name as name_fr_BE } from "@/keyboardLayouts/fr_BE"
import { chars as chars_cs_CZ, name as name_cs_CZ } from "@/keyboardLayouts/cs_CZ"
import { chars as chars_en_UK, name as name_en_UK } from "@/keyboardLayouts/en_UK"
import { chars as chars_en_US, name as name_en_US } from "@/keyboardLayouts/en_US"
import { chars as chars_fr_FR, name as name_fr_FR } from "@/keyboardLayouts/fr_FR"
import { chars as chars_de_DE, name as name_de_DE } from "@/keyboardLayouts/de_DE"
import { chars as chars_it_IT, name as name_it_IT } from "@/keyboardLayouts/it_IT"
import { chars as chars_nb_NO, name as name_nb_NO } from "@/keyboardLayouts/nb_NO"
import { chars as chars_es_ES, name as name_es_ES } from "@/keyboardLayouts/es_ES"
import { chars as chars_sv_SE, name as name_sv_SE } from "@/keyboardLayouts/sv_SE"
import { chars as chars_fr_CH, name as name_fr_CH } from "@/keyboardLayouts/fr_CH"
import { chars as chars_de_CH, name as name_de_CH } from "@/keyboardLayouts/de_CH"
type KeyInfo = { key: string | number; shift?: boolean, altRight?: boolean }
export type KeyCombo = KeyInfo & { deadKey?: boolean, accentKey?: KeyInfo }
export const layouts: Record<string, string> = {
be_FR: name_fr_BE,
cs_CZ: name_cs_CZ,
en_UK: name_en_UK,
en_US: name_en_US,
fr_FR: name_fr_FR,
de_DE: name_de_DE,
it_IT: name_it_IT,
nb_NO: name_nb_NO,
es_ES: name_es_ES,
sv_SE: name_sv_SE,
fr_CH: name_fr_CH,
de_CH: name_de_CH,
}
export const chars: Record<string, Record<string, KeyCombo>> = {
be_FR: chars_fr_BE,
cs_CZ: chars_cs_CZ,
en_UK: chars_en_UK,
en_US: chars_en_US,
fr_FR: chars_fr_FR,
de_DE: chars_de_DE,
it_IT: chars_it_IT,
nb_NO: chars_nb_NO,
es_ES: chars_es_ES,
sv_SE: chars_sv_SE,
fr_CH: chars_fr_CH,
de_CH: chars_de_CH,
};

View File

@ -0,0 +1,244 @@
import { KeyCombo } from "../keyboardLayouts"
export const name = "Čeština";
const keyTrema = { key: "Backslash" } // tréma (umlaut), two dots placed above a vowel
const keyAcute = { key: "Equal" } // accent aigu (acute accent), mark ´ placed above the letter
const keyHat = { key: "Digit3", shift: true, altRight: true } // accent circonflexe (accent hat), mark ^ placed above the letter
const keyCaron = { key: "Equal", shift: true } // caron or haček (inverted hat), mark ˇ placed above the letter
const keyGrave = { key: "Digit7", shift: true, altRight: true } // accent grave, mark ` placed above the letter
const keyTilde = { key: "Digit1", shift: true, altRight: true } // tilde, mark ~ placed above the letter
const keyRing = { key: "Backquote", shift: true } // kroužek (little ring), mark ° placed above the letter
const keyOverdot = { key: "Digit8", shift: true, altRight: true } // overdot (dot above), mark ˙ placed above the letter
const keyHook = { key: "Digit6", shift: true, altRight: true } // ogonoek (little hook), mark ˛ placed beneath a letter
const keyCedille = { key: "Equal", shift: true, altRight: true } // accent cedille (cedilla), mark ¸ placed beneath a letter
export const chars = {
A: { key: "KeyA", shift: true },
"Ä": { key: "KeyA", shift: true, accentKey: keyTrema },
"Á": { key: "KeyA", shift: true, accentKey: keyAcute },
"Â": { key: "KeyA", shift: true, accentKey: keyHat },
"À": { key: "KeyA", shift: true, accentKey: keyGrave },
"Ã": { key: "KeyA", shift: true, accentKey: keyTilde },
"Ȧ": { key: "KeyA", shift: true, accentKey: keyOverdot },
"Ą": { key: "KeyA", shift: true, accentKey: keyHook },
B: { key: "KeyB", shift: true },
"Ḃ": { key: "KeyB", shift: true, accentKEy: keyOverdot },
C: { key: "KeyC", shift: true },
"Č": { key: "KeyC", shift: true, accentKey: keyCaron },
"Ċ": { key: "KeyC", shift: true, accentKey: keyOverdot },
"Ç": { key: "KeyC", shift: true, accentKey: keyCedille },
D: { key: "KeyD", shift: true },
"Ď": { key: "KeyD", shift: true, accentKey: keyCaron },
"Ḋ": { key: "KeyD", shift: true, accentKey: keyOverdot },
E: { key: "KeyE", shift: true },
"Ë": { key: "KeyE", shift: true, accentKey: keyTrema },
"É": { key: "KeyE", shift: true, accentKey: keyAcute },
"Ê": { key: "KeyE", shift: true, accentKey: keyHat },
"Ě": { key: "KeyE", shift: true, accentKey: keyCaron },
"È": { key: "KeyE", shift: true, accentKey: keyGrave },
"Ẽ": { key: "KeyE", shift: true, accentKey: keyTilde },
"Ė": { key: "KeyE", shift: true, accentKEy: keyOverdot },
"Ę": { key: "KeyE", shift: true, accentKey: keyHook },
F: { key: "KeyF", shift: true },
"Ḟ": { key: "KeyF", shift: true, accentKey: keyOverdot },
G: { key: "KeyG", shift: true },
"Ġ": { key: "KeyG", shift: true, accentKey: keyOverdot },
H: { key: "KeyH", shift: true },
"Ḣ": { key: "KeyH", shift: true, accentKey: keyOverdot },
I: { key: "KeyI", shift: true },
"Ï": { key: "KeyI", shift: true, accentKey: keyTrema },
"Í": { key: "KeyI", shift: true, accentKey: keyAcute },
"Î": { key: "KeyI", shift: true, accentKey: keyHat },
"Ì": { key: "KeyI", shift: true, accentKey: keyGrave },
"Ĩ": { key: "KeyI", shift: true, accentKey: keyTilde },
"İ": { key: "KeyI", shift: true, accentKey: keyOverdot },
"Į": { key: "KeyI", shift: true, accentKey: keyHook },
J: { key: "KeyJ", shift: true },
K: { key: "KeyK", shift: true },
L: { key: "KeyL", shift: true },
"Ŀ": { key: "KeyL", shift: true },
M: { key: "KeyM", shift: true },
"Ṁ": { key: "KeyM", shift: true },
N: { key: "KeyN", shift: true },
"Ň": { key: "KeyN", shift: true, accentKey: keyCaron },
"Ñ": { key: "KeyN", shift: true, accentKey: keyTilde },
"Ṅ": { key: "KeyN", shift: true, accentKEy: keyOverdot },
O: { key: "KeyO", shift: true },
"Ö": { key: "KeyO", shift: true, accentKey: keyTrema },
"Ó": { key: "KeyO", shift: true, accentKey: keyAcute },
"Ô": { key: "KeyO", shift: true, accentKey: keyHat },
"Ò": { key: "KeyO", shift: true, accentKey: keyGrave },
"Õ": { key: "KeyO", shift: true, accentKey: keyTilde },
"Ȯ": { key: "KeyO", shift: true, accentKey: keyOverdot },
"Ǫ": { key: "KeyO", shift: true, accentKey: keyHook },
P: { key: "KeyP", shift: true },
"Ṗ": { key: "KeyP", shift: true, accentKey: keyOverdot },
Q: { key: "KeyQ", shift: true },
R: { key: "KeyR", shift: true },
"Ř": { key: "KeyR", shift: true, accentKey: keyCaron },
"Ṙ": { key: "KeyR", shift: true, accentKey: keyOverdot },
S: { key: "KeyS", shift: true },
"Š": { key: "KeyS", shift: true, accentKey: keyCaron },
"Ṡ": { key: "KeyS", shift: true, accentKey: keyOverdot },
T: { key: "KeyT", shift: true },
"Ť": { key: "KeyT", shift: true, accentKey: keyCaron },
"Ṫ": { key: "KeyT", shift: true, accentKey: keyOverdot },
U: { key: "KeyU", shift: true },
"Ü": { key: "KeyU", shift: true, accentKey: keyTrema },
"Ú": { key: "KeyU", shift: true, accentKey: keyAcute },
"Û": { key: "KeyU", shift: true, accentKey: keyHat },
"Ù": { key: "KeyU", shift: true, accentKey: keyGrave },
"Ũ": { key: "KeyU", shift: true, accentKey: keyTilde },
"Ů": { key: "KeyU", shift: true, accentKey: keyRing },
"Ų": { key: "KeyU", shift: true, accentKey: keyHook },
V: { key: "KeyV", shift: true },
W: { key: "KeyW", shift: true },
"Ẇ": { key: "KeyW", shift: true, accentKey: keyOverdot },
X: { key: "KeyX", shift: true },
"Ẋ": { key: "KeyX", shift: true, accentKey: keyOverdot },
Y: { key: "KeyY", shift: true },
"Ý": { key: "KeyY", shift: true, accentKey: keyAcute },
"Ẏ": { key: "KeyY", shift: true, accentKey: keyOverdot },
Z: { key: "KeyZ", shift: true },
"Ż": { key: "KeyZ", shift: true, accentKey: keyOverdot },
a: { key: "KeyA" },
"ä": { key: "KeyA", accentKey: keyTrema },
"â": { key: "KeyA", accentKey: keyHat },
"à": { key: "KeyA", accentKey: keyGrave },
"ã": { key: "KeyA", accentKey: keyTilde },
"ȧ": { key: "KeyA", accentKey: keyOverdot },
"ą": { key: "KeyA", accentKey: keyHook },
b: { key: "KeyB" },
"{": { key: "KeyB", altRight: true },
"ḃ": { key: "KeyB", accentKey: keyOverdot },
c: { key: "KeyC" },
"&": { key: "KeyC", altRight: true },
"ç": { key: "KeyC", accentKey: keyCedille },
"ċ": { key: "KeyC", accentKey: keyOverdot },
d: { key: "KeyD" },
"ď": { key: "KeyD", accentKey: keyCaron },
"ḋ": { key: "KeyD", accentKey: keyOverdot },
"Đ": { key: "KeyD", altRight: true },
e: { key: "KeyE" },
"ë": { key: "KeyE", accentKey: keyTrema },
"ê": { key: "KeyE", accentKey: keyHat },
"ẽ": { key: "KeyE", accentKey: keyTilde },
"è": { key: "KeyE", accentKey: keyGrave },
"ė": { key: "KeyE", accentKey: keyOverdot },
"ę": { key: "KeyE", accentKey: keyHook },
"€": { key: "KeyE", altRight: true },
f: { key: "KeyF" },
"ḟ": { key: "KeyF", accentKey: keyOverdot },
"[": { key: "KeyF", altRight: true },
g: { key: "KeyG" },
"ġ": { key: "KeyG", accentKey: keyOverdot },
"]": { key: "KeyF", altRight: true },
h: { key: "KeyH" },
"ḣ": { key: "KeyH", accentKey: keyOverdot },
i: { key: "KeyI" },
"ï": { key: "KeyI", accentKey: keyTrema },
"î": { key: "KeyI", accentKey: keyHat },
"ì": { key: "KeyI", accentKey: keyGrave },
"ĩ": { key: "KeyI", accentKey: keyTilde },
"ı": { key: "KeyI", accentKey: keyOverdot },
"į": { key: "KeyI", accentKey: keyHook },
j: { key: "KeyJ" },
"ȷ": { key: "KeyJ", accentKey: keyOverdot },
k: { key: "KeyK" },
"ł": { key: "KeyK", altRight: true },
l: { key: "KeyL" },
"ŀ": { key: "KeyL", accentKey: keyOverdot },
"Ł": { key: "KeyL", altRight: true },
m: { key: "KeyM" },
"ṁ": { key: "KeyM", accentKey: keyOverdot },
n: { key: "KeyN" },
"}": { key: "KeyN", altRight: true },
"ň": { key: "KeyN", accentKey: keyCaron },
"ñ": { key: "KeyN", accentKey: keyTilde },
"ṅ": { key: "KeyN", accentKey: keyOverdot },
o: { key: "KeyO" },
"ö": { key: "Key0", accentKey: keyTrema },
"ó": { key: "KeyO", accentKey: keyAcute },
"ô": { key: "KeyO", accentKey: keyHat },
"ò": { key: "KeyO", accentKey: keyGrave },
"õ": { key: "KeyO", accentKey: keyTilde },
"ȯ": { key: "KeyO", accentKey: keyOverdot },
"ǫ": { key: "KeyO", accentKey: keyHook },
p: { key: "KeyP" },
"ṗ": { key: "KeyP", accentKey: keyOverdot },
q: { key: "KeyQ" },
r: { key: "KeyR" },
"ṙ": { key: "KeyR", accentKey: keyOverdot },
s: { key: "KeyS" },
"ṡ": { key: "KeyS", accentKey: keyOverdot },
"đ": { key: "KeyS", altRight: true },
t: { key: "KeyT" },
"ť": { key: "KeyT", accentKey: keyCaron },
"ṫ": { key: "KeyT", accentKey: keyOverdot },
u: { key: "KeyU" },
"ü": { key: "KeyU", accentKey: keyTrema },
"û": { key: "KeyU", accentKey: keyHat },
"ù": { key: "KeyU", accentKey: keyGrave },
"ũ": { key: "KeyU", accentKey: keyTilde },
"ų": { key: "KeyU", accentKey: keyHook },
v: { key: "KeyV" },
"@": { key: "KeyV", altRight: true },
w: { key: "KeyW" },
"ẇ": { key: "KeyW", accentKey: keyOverdot },
x: { key: "KeyX" },
"#": { key: "KeyX", altRight: true },
"ẋ": { key: "KeyX", accentKey: keyOverdot },
y: { key: "KeyY" },
"ẏ": { key: "KeyY", accentKey: keyOverdot },
z: { key: "KeyZ" },
"ż": { key: "KeyZ", accentKey: keyOverdot },
";": { key: "Backquote" },
"°": { key: "Backquote", shift: true, deadKey: true },
"+": { key: "Digit1" },
1: { key: "Digit1", shift: true },
"ě": { key: "Digit2" },
2: { key: "Digit2", shift: true },
"š": { key: "Digit3" },
3: { key: "Digit3", shift: true },
"č": { key: "Digit4" },
4: { key: "Digit4", shift: true },
"ř": { key: "Digit5" },
5: { key: "Digit5", shift: true },
"ž": { key: "Digit6" },
6: { key: "Digit6", shift: true },
"ý": { key: "Digit7" },
7: { key: "Digit7", shift: true },
"á": { key: "Digit8" },
8: { key: "Digit8", shift: true },
"í": { key: "Digit9" },
9: { key: "Digit9", shift: true },
"é": { key: "Digit0" },
0: { key: "Digit0", shift: true },
"=": { key: "Minus" },
"%": { key: "Minus", shift: true },
"ú": { key: "BracketLeft" },
"/": { key: "BracketLeft", shift: true },
")": { key: "BracketRight" },
"(": { key: "BracketRight", shift: true },
"ů": { key: "Semicolon" },
"\"": { key: "Semicolon", shift: true },
"§": { key: "Quote" },
"!": { key: "Quote", shift: true },
"'": { key: "Backslash", shift: true },
",": { key: "Comma" },
"?": { key: "Comma", shift: true },
"<": { key: "Comma", altRight: true },
".": { key: "Period" },
":": { key: "Period", shift: true },
">": { key: "Period", altRight: true },
"-": { key: "Slash" },
"_": { key: "Slash", shift: true },
"*": { key: "Slash", altRight: true },
"\\": { key: "IntlBackslash" },
"|": { key: "IntlBackslash", shift: true },
" ": { key: "Space" },
"\n": { key: "Enter" },
Enter: { key: "Enter" },
Tab: { key: "Tab" },
} as Record<string, KeyCombo>;

View File

@ -0,0 +1,165 @@
import { KeyCombo } from "../keyboardLayouts"
export const name = "Schwiizerdütsch";
const keyTrema = { key: "BracketRight" } // tréma (umlaut), two dots placed above a vowel
const keyAcute = { key: "Minus", altRight: true } // accent aigu (acute accent), mark ´ placed above the letter
const keyHat = { key: "Equal" } // accent circonflexe (accent hat), mark ^ placed above the letter
const keyGrave = { key: "Equal", shift: true } // accent grave, mark ` placed above the letter
const keyTilde = { key: "Equal", altRight: true } // tilde, mark ~ placed above the letter
export const chars = {
A: { key: "KeyA", shift: true },
"Ä": { key: "KeyA", shift: true, accentKey: keyTrema },
"Á": { key: "KeyA", shift: true, accentKey: keyAcute },
"Â": { key: "KeyA", shift: true, accentKey: keyHat },
"À": { key: "KeyA", shift: true, accentKey: keyGrave },
"Ã": { key: "KeyA", shift: true, accentKey: keyTilde },
B: { key: "KeyB", shift: true },
C: { key: "KeyC", shift: true },
D: { key: "KeyD", shift: true },
E: { key: "KeyE", shift: true },
"Ë": { key: "KeyE", shift: true, accentKey: keyTrema },
"É": { key: "KeyE", shift: true, accentKey: keyAcute },
"Ê": { key: "KeyE", shift: true, accentKey: keyHat },
"È": { key: "KeyE", shift: true, accentKey: keyGrave },
"Ẽ": { key: "KeyE", shift: true, accentKey: keyTilde },
F: { key: "KeyF", shift: true },
G: { key: "KeyG", shift: true },
H: { key: "KeyH", shift: true },
I: { key: "KeyI", shift: true },
"Ï": { key: "KeyI", shift: true, accentKey: keyTrema },
"Í": { key: "KeyI", shift: true, accentKey: keyAcute },
"Î": { key: "KeyI", shift: true, accentKey: keyHat },
"Ì": { key: "KeyI", shift: true, accentKey: keyGrave },
"Ĩ": { key: "KeyI", shift: true, accentKey: keyTilde },
J: { key: "KeyJ", shift: true },
K: { key: "KeyK", shift: true },
L: { key: "KeyL", shift: true },
M: { key: "KeyM", shift: true },
N: { key: "KeyN", shift: true },
O: { key: "KeyO", shift: true },
"Ö": { key: "KeyO", shift: true, accentKey: keyTrema },
"Ó": { key: "KeyO", shift: true, accentKey: keyAcute },
"Ô": { key: "KeyO", shift: true, accentKey: keyHat },
"Ò": { key: "KeyO", shift: true, accentKey: keyGrave },
"Õ": { key: "KeyO", shift: true, accentKey: keyTilde },
P: { key: "KeyP", shift: true },
Q: { key: "KeyQ", shift: true },
R: { key: "KeyR", shift: true },
S: { key: "KeyS", shift: true },
T: { key: "KeyT", shift: true },
U: { key: "KeyU", shift: true },
"Ü": { key: "KeyU", shift: true, accentKey: keyTrema },
"Ú": { key: "KeyU", shift: true, accentKey: keyAcute },
"Û": { key: "KeyU", shift: true, accentKey: keyHat },
"Ù": { key: "KeyU", shift: true, accentKey: keyGrave },
"Ũ": { key: "KeyU", shift: true, accentKey: keyTilde },
V: { key: "KeyV", shift: true },
W: { key: "KeyW", shift: true },
X: { key: "KeyX", shift: true },
Y: { key: "KeyZ", shift: true },
Z: { key: "KeyY", shift: true },
a: { key: "KeyA" },
"á": { key: "KeyA", accentKey: keyAcute },
"â": { key: "KeyA", accentKey: keyHat },
"ã": { key: "KeyA", accentKey: keyTilde },
b: { key: "KeyB" },
c: { key: "KeyC" },
d: { key: "KeyD" },
e: { key: "KeyE" },
"ë": { key: "KeyE", accentKey: keyTrema },
"ê": { key: "KeyE", accentKey: keyHat },
"ẽ": { key: "KeyE", accentKey: keyTilde },
"€": { key: "KeyE", altRight: true },
f: { key: "KeyF" },
g: { key: "KeyG" },
h: { key: "KeyH" },
i: { key: "KeyI" },
"ï": { key: "KeyI", accentKey: keyTrema },
"í": { key: "KeyI", accentKey: keyAcute },
"î": { key: "KeyI", accentKey: keyHat },
"ì": { key: "KeyI", accentKey: keyGrave },
"ĩ": { key: "KeyI", accentKey: keyTilde },
j: { key: "KeyJ" },
k: { key: "KeyK" },
l: { key: "KeyL" },
m: { key: "KeyM" },
n: { key: "KeyN" },
o: { key: "KeyO" },
"ó": { key: "KeyO", accentKey: keyAcute },
"ô": { key: "KeyO", accentKey: keyHat },
"ò": { key: "KeyO", accentKey: keyGrave },
"õ": { key: "KeyO", accentKey: keyTilde },
p: { key: "KeyP" },
q: { key: "KeyQ" },
r: { key: "KeyR" },
s: { key: "KeyS" },
t: { key: "KeyT" },
u: { key: "KeyU" },
"ú": { key: "KeyU", accentKey: keyAcute },
"û": { key: "KeyU", accentKey: keyHat },
"ù": { key: "KeyU", accentKey: keyGrave },
"ũ": { key: "KeyU", accentKey: keyTilde },
v: { key: "KeyV" },
w: { key: "KeyW" },
x: { key: "KeyX" },
y: { key: "KeyZ" },
z: { key: "KeyY" },
"§": { key: "Backquote" },
"°": { key: "Backquote", shift: true },
1: { key: "Digit1" },
"+": { key: "Digit1", shift: true },
"|": { key: "Digit1", altRight: true },
2: { key: "Digit2" },
"\"": { key: "Digit2", shift: true },
"@": { key: "Digit2", altRight: true },
3: { key: "Digit3" },
"*": { key: "Digit3", shift: true },
"#": { key: "Digit3", altRight: true },
4: { key: "Digit4" },
"ç": { key: "Digit4", shift: true },
5: { key: "Digit5" },
"%": { key: "Digit5", shift: true },
6: { key: "Digit6" },
"&": { key: "Digit6", shift: true },
7: { key: "Digit7" },
"/": { key: "Digit7", shift: true },
8: { key: "Digit8" },
"(": { key: "Digit8", shift: true },
9: { key: "Digit9" },
")": { key: "Digit9", shift: true },
0: { key: "Digit0" },
"=": { key: "Digit0", shift: true },
"'": { key: "Minus" },
"?": { key: "Minus", shift: true },
"^": { key: "Equal", deadKey: true },
"`": { key: "Equal", shift: true },
"~": { key: "Equal", altRight: true, deadKey: true },
"ü": { key: "BracketLeft" },
"è": { key: "BracketLeft", shift: true },
"[": { key: "BracketLeft", altRight: true },
"!": { key: "BracketRight", shift: true },
"]": { key: "BracketRight", altRight: true },
"ö": { key: "Semicolon" },
"é": { key: "Semicolon", shift: true },
"ä": { key: "Quote" },
"à": { key: "Quote", shift: true },
"{": { key: "Quote", altRight: true },
"$": { key: "Backslash" },
"£": { key: "Backslash", shift: true },
"}": { key: "Backslash", altRight: true },
",": { key: "Comma" },
";": { key: "Comma", shift: true },
".": { key: "Period" },
":": { key: "Period", shift: true },
"-": { key: "Slash" },
"_": { key: "Slash", shift: true },
"<": { key: "IntlBackslash" },
">": { key: "IntlBackslash", shift: true },
"\\": { key: "IntlBackslash", altRight: true },
" ": { key: "Space" },
"\n": { key: "Enter" },
Enter: { key: "Enter" },
Tab: { key: "Tab" },
} as Record<string, KeyCombo>;

View File

@ -0,0 +1,152 @@
import { KeyCombo } from "../keyboardLayouts"
export const name = "Deutsch";
const keyAcute = { key: "Equal" } // accent aigu (acute accent), mark ´ placed above the letter
const keyHat = { key: "Backquote" } // accent circonflexe (accent hat), mark ^ placed above the letter
const keyGrave = { key: "Equal", shift: true } // accent grave, mark ` placed above the letter
export const chars = {
A: { key: "KeyA", shift: true },
"Á": { key: "KeyA", shift: true, accentKey: keyAcute },
"Â": { key: "KeyA", shift: true, accentKey: keyHat },
"À": { key: "KeyA", shift: true, accentKey: keyGrave },
B: { key: "KeyB", shift: true },
C: { key: "KeyC", shift: true },
D: { key: "KeyD", shift: true },
E: { key: "KeyE", shift: true },
"É": { key: "KeyE", shift: true, accentKey: keyAcute },
"Ê": { key: "KeyE", shift: true, accentKey: keyHat },
"È": { key: "KeyE", shift: true, accentKey: keyGrave },
F: { key: "KeyF", shift: true },
G: { key: "KeyG", shift: true },
H: { key: "KeyH", shift: true },
I: { key: "KeyI", shift: true },
"Í": { key: "KeyI", shift: true, accentKey: keyAcute },
"Î": { key: "KeyI", shift: true, accentKey: keyHat },
"Ì": { key: "KeyI", shift: true, accentKey: keyGrave },
J: { key: "KeyJ", shift: true },
K: { key: "KeyK", shift: true },
L: { key: "KeyL", shift: true },
M: { key: "KeyM", shift: true },
N: { key: "KeyN", shift: true },
O: { key: "KeyO", shift: true },
"Ó": { key: "KeyO", shift: true, accentKey: keyAcute },
"Ô": { key: "KeyO", shift: true, accentKey: keyHat },
"Ò": { key: "KeyO", shift: true, accentKey: keyGrave },
P: { key: "KeyP", shift: true },
Q: { key: "KeyQ", shift: true },
R: { key: "KeyR", shift: true },
S: { key: "KeyS", shift: true },
T: { key: "KeyT", shift: true },
U: { key: "KeyU", shift: true },
"Ú": { key: "KeyU", shift: true, accentKey: keyAcute },
"Û": { key: "KeyU", shift: true, accentKey: keyHat },
"Ù": { key: "KeyU", shift: true, accentKey: keyGrave },
V: { key: "KeyV", shift: true },
W: { key: "KeyW", shift: true },
X: { key: "KeyX", shift: true },
Y: { key: "KeyZ", shift: true },
Z: { key: "KeyY", shift: true },
a: { key: "KeyA" },
"á": { key: "KeyA", accentKey: keyAcute },
"â": { key: "KeyA", accentKey: keyHat },
"à": { key: "KeyA", accentKey: keyGrave},
b: { key: "KeyB" },
c: { key: "KeyC" },
d: { key: "KeyD" },
e: { key: "KeyE" },
"é": { key: "KeyE", accentKey: keyAcute},
"ê": { key: "KeyE", accentKey: keyHat },
"è": { key: "KeyE", accentKey: keyGrave },
"€": { key: "KeyE", altRight: true },
f: { key: "KeyF" },
g: { key: "KeyG" },
h: { key: "KeyH" },
i: { key: "KeyI" },
"í": { key: "KeyI", accentKey: keyAcute },
"î": { key: "KeyI", accentKey: keyHat },
"ì": { key: "KeyI", accentKey: keyGrave },
j: { key: "KeyJ" },
k: { key: "KeyK" },
l: { key: "KeyL" },
m: { key: "KeyM" },
"µ": { key: "KeyM", altRight: true },
n: { key: "KeyN" },
o: { key: "KeyO" },
"ó": { key: "KeyO", accentKey: keyAcute },
"ô": { key: "KeyO", accentKey: keyHat },
"ò": { key: "KeyO", accentKey: keyGrave },
p: { key: "KeyP" },
q: { key: "KeyQ" },
"@": { key: "KeyQ", altRight: true },
r: { key: "KeyR" },
s: { key: "KeyS" },
t: { key: "KeyT" },
u: { key: "KeyU" },
"ú": { key: "KeyU", accentKey: keyAcute },
"û": { key: "KeyU", accentKey: keyHat },
"ù": { key: "KeyU", accentKey: keyGrave },
v: { key: "KeyV" },
w: { key: "KeyW" },
x: { key: "KeyX" },
y: { key: "KeyZ" },
z: { key: "KeyY" },
"°": { key: "Backquote", shift: true },
"^": { key: "Backquote", deadKey: true },
1: { key: "Digit1" },
"!": { key: "Digit1", shift: true },
2: { key: "Digit2" },
"\"": { key: "Digit2", shift: true },
"²": { key: "Digit2", altRight: true },
3: { key: "Digit3" },
"§": { key: "Digit3", shift: true },
"³": { key: "Digit3", altRight: true },
4: { key: "Digit4" },
"$": { key: "Digit4", shift: true },
5: { key: "Digit5" },
"%": { key: "Digit5", shift: true },
6: { key: "Digit6" },
"&": { key: "Digit6", shift: true },
7: { key: "Digit7" },
"/": { key: "Digit7", shift: true },
"{": { key: "Digit7", altRight: true },
8: { key: "Digit8" },
"(": { key: "Digit8", shift: true },
"[": { key: "Digit8", altRight: true },
9: { key: "Digit9" },
")": { key: "Digit9", shift: true },
"]": { key: "Digit9", altRight: true },
0: { key: "Digit0" },
"=": { key: "Digit0", shift: true },
"}": { key: "Digit0", altRight: true },
"ß": { key: "Minus" },
"?": { key: "Minus", shift: true },
"\\": { key: "Minus", altRight: true },
"´": { key: "Equal", deadKey: true },
"`": { key: "Equal", shift: true, deadKey: true },
"ü": { key: "BracketLeft" },
"Ü": { key: "BracketLeft", shift: true },
"+": { key: "BracketRight" },
"*": { key: "BracketRight", shift: true },
"~": { key: "BracketRight", altRight: true },
"ö": { key: "Semicolon" },
"Ö": { key: "Semicolon", shift: true },
"ä": { key: "Quote" },
"Ä": { key: "Quote", shift: true },
"#": { key: "Backslash" },
"'": { key: "Backslash", shift: true },
",": { key: "Comma" },
";": { key: "Comma", shift: true },
".": { key: "Period" },
":": { key: "Period", shift: true },
"-": { key: "Slash" },
"_": { key: "Slash", shift: true },
"<": { key: "IntlBackslash" },
">": { key: "IntlBackslash", shift: true },
"|": { key: "IntlBackslash", altRight: true },
" ": { key: "Space" },
"\n": { key: "Enter" },
Enter: { key: "Enter" },
Tab: { key: "Tab" },
} as Record<string, KeyCombo>;

View File

@ -0,0 +1,107 @@
import { KeyCombo } from "../keyboardLayouts"
export const name = "English (UK)";
export const chars = {
A: { key: "KeyA", shift: true },
B: { key: "KeyB", shift: true },
C: { key: "KeyC", shift: true },
D: { key: "KeyD", shift: true },
E: { key: "KeyE", shift: true },
F: { key: "KeyF", shift: true },
G: { key: "KeyG", shift: true },
H: { key: "KeyH", shift: true },
I: { key: "KeyI", shift: true },
J: { key: "KeyJ", shift: true },
K: { key: "KeyK", shift: true },
L: { key: "KeyL", shift: true },
M: { key: "KeyM", shift: true },
N: { key: "KeyN", shift: true },
O: { key: "KeyO", shift: true },
P: { key: "KeyP", shift: true },
Q: { key: "KeyQ", shift: true },
R: { key: "KeyR", shift: true },
S: { key: "KeyS", shift: true },
T: { key: "KeyT", shift: true },
U: { key: "KeyU", shift: true },
V: { key: "KeyV", shift: true },
W: { key: "KeyW", shift: true },
X: { key: "KeyX", shift: true },
Y: { key: "KeyY", shift: true },
Z: { key: "KeyZ", shift: true },
a: { key: "KeyA" },
b: { key: "KeyB" },
c: { key: "KeyC" },
d: { key: "KeyD" },
e: { key: "KeyE" },
f: { key: "KeyF" },
g: { key: "KeyG" },
h: { key: "KeyH" },
i: { key: "KeyI" },
j: { key: "KeyJ" },
k: { key: "KeyK" },
l: { key: "KeyL" },
m: { key: "KeyM" },
n: { key: "KeyN" },
o: { key: "KeyO" },
p: { key: "KeyP" },
q: { key: "KeyQ" },
r: { key: "KeyR" },
s: { key: "KeyS" },
t: { key: "KeyT" },
u: { key: "KeyU" },
v: { key: "KeyV" },
w: { key: "KeyW" },
x: { key: "KeyX" },
y: { key: "KeyY" },
z: { key: "KeyZ" },
1: { key: "Digit1" },
"!": { key: "Digit1", shift: true },
2: { key: "Digit2" },
"\"": { key: "Digit2", shift: true },
3: { key: "Digit3" },
"£": { key: "Digit3", shift: true },
4: { key: "Digit4" },
$: { key: "Digit4", shift: true },
"€": { key: "Digit4", altRight: true },
5: { key: "Digit5" },
"%": { key: "Digit5", shift: true },
6: { key: "Digit6" },
"^": { key: "Digit6", shift: true },
7: { key: "Digit7" },
"&": { key: "Digit7", shift: true },
8: { key: "Digit8" },
"*": { key: "Digit8", shift: true },
9: { key: "Digit9" },
"(": { key: "Digit9", shift: true },
0: { key: "Digit0" },
")": { key: "Digit0", shift: true },
"-": { key: "Minus" },
_: { key: "Minus", shift: true },
"=": { key: "Equal" },
"+": { key: "Equal", shift: true },
"'": { key: "Quote" },
'@': { key: "Quote", shift: true },
",": { key: "Comma" },
"<": { key: "Comma", shift: true },
"/": { key: "Slash" },
"?": { key: "Slash", shift: true },
".": { key: "Period" },
">": { key: "Period", shift: true },
";": { key: "Semicolon" },
":": { key: "Semicolon", shift: true },
"[": { key: "BracketLeft" },
"{": { key: "BracketLeft", shift: true },
"]": { key: "BracketRight" },
"}": { key: "BracketRight", shift: true },
"#": { key: "Backslash" },
"~": { key: "Backslash", shift: true },
"`": { key: "Backquote" },
"¬": { key: "Backquote", shift: true },
"\\": { key: "IntlBackslash" },
"|": { key: "IntlBackslash", shift: true },
" ": { key: "Space" },
"\n": { key: "Enter" },
Enter: { key: "Enter" },
Tab: { key: "Tab" },
} as Record<string, KeyCombo>

View File

@ -0,0 +1,113 @@
import { KeyCombo } from "../keyboardLayouts"
export const name = "English (US)";
export const chars = {
A: { key: "KeyA", shift: true },
B: { key: "KeyB", shift: true },
C: { key: "KeyC", shift: true },
D: { key: "KeyD", shift: true },
E: { key: "KeyE", shift: true },
F: { key: "KeyF", shift: true },
G: { key: "KeyG", shift: true },
H: { key: "KeyH", shift: true },
I: { key: "KeyI", shift: true },
J: { key: "KeyJ", shift: true },
K: { key: "KeyK", shift: true },
L: { key: "KeyL", shift: true },
M: { key: "KeyM", shift: true },
N: { key: "KeyN", shift: true },
O: { key: "KeyO", shift: true },
P: { key: "KeyP", shift: true },
Q: { key: "KeyQ", shift: true },
R: { key: "KeyR", shift: true },
S: { key: "KeyS", shift: true },
T: { key: "KeyT", shift: true },
U: { key: "KeyU", shift: true },
V: { key: "KeyV", shift: true },
W: { key: "KeyW", shift: true },
X: { key: "KeyX", shift: true },
Y: { key: "KeyY", shift: true },
Z: { key: "KeyZ", shift: true },
a: { key: "KeyA" },
b: { key: "KeyB" },
c: { key: "KeyC" },
d: { key: "KeyD" },
e: { key: "KeyE" },
f: { key: "KeyF" },
g: { key: "KeyG" },
h: { key: "KeyH" },
i: { key: "KeyI" },
j: { key: "KeyJ" },
k: { key: "KeyK" },
l: { key: "KeyL" },
m: { key: "KeyM" },
n: { key: "KeyN" },
o: { key: "KeyO" },
p: { key: "KeyP" },
q: { key: "KeyQ" },
r: { key: "KeyR" },
s: { key: "KeyS" },
t: { key: "KeyT" },
u: { key: "KeyU" },
v: { key: "KeyV" },
w: { key: "KeyW" },
x: { key: "KeyX" },
y: { key: "KeyY" },
z: { key: "KeyZ" },
1: { key: "Digit1" },
"!": { key: "Digit1", shift: true },
2: { key: "Digit2" },
"@": { key: "Digit2", shift: true },
3: { key: "Digit3" },
"#": { key: "Digit3", shift: true },
4: { key: "Digit4" },
$: { key: "Digit4", shift: true },
"%": { key: "Digit5", shift: true },
5: { key: "Digit5" },
"^": { key: "Digit6", shift: true },
6: { key: "Digit6" },
"&": { key: "Digit7", shift: true },
7: { key: "Digit7" },
"*": { key: "Digit8", shift: true },
8: { key: "Digit8" },
"(": { key: "Digit9", shift: true },
9: { key: "Digit9" },
")": { key: "Digit0", shift: true },
0: { key: "Digit0" },
"-": { key: "Minus" },
_: { key: "Minus", shift: true },
"=": { key: "Equal" },
"+": { key: "Equal", shift: true },
"'": { key: "Quote" },
'"': { key: "Quote", shift: true },
",": { key: "Comma" },
"<": { key: "Comma", shift: true },
"/": { key: "Slash" },
"?": { key: "Slash", shift: true },
".": { key: "Period" },
">": { key: "Period", shift: true },
";": { key: "Semicolon" },
":": { key: "Semicolon", shift: true },
"[": { key: "BracketLeft" },
"{": { key: "BracketLeft", shift: true },
"]": { key: "BracketRight" },
"}": { key: "BracketRight", shift: true },
"\\": { key: "Backslash" },
"|": { key: "Backslash", shift: true },
"`": { key: "Backquote" },
"~": { key: "Backquote", shift: true },
"§": { key: "IntlBackslash" },
"±": { key: "IntlBackslash", shift: true },
" ": { key: "Space", shift: false },
"\n": { key: "Enter", shift: false },
Enter: { key: "Enter", shift: false },
Tab: { key: "Tab", shift: false },
PrintScreen: { key: "Prt Sc", shift: false },
SystemRequest: { key: "Prt Sc", shift: true },
ScrollLock: { key: "ScrollLock", shift: false},
Pause: { key: "Pause", shift: false },
Break: { key: "Pause", shift: true },
Insert: { key: "Insert", shift: false },
Delete: { key: "Delete", shift: false },
} as Record<string, KeyCombo>

View File

@ -0,0 +1,168 @@
import { KeyCombo } from "../keyboardLayouts"
export const name = "Español";
const keyTrema = { key: "Quote", shift: true } // tréma (umlaut), two dots placed above a vowel
const keyAcute = { key: "Quote" } // accent aigu (acute accent), mark ´ placed above the letter
const keyHat = { key: "BracketRight", shift: true } // accent circonflexe (accent hat), mark ^ placed above the letter
const keyGrave = { key: "BracketRight" } // accent grave, mark ` placed above the letter
const keyTilde = { key: "Key4", altRight: true } // tilde, mark ~ placed above the letter
export const chars = {
A: { key: "KeyA", shift: true },
"Ä": { key: "KeyA", shift: true, accentKey: keyTrema },
"Á": { key: "KeyA", shift: true, accentKey: keyAcute },
"Â": { key: "KeyA", shift: true, accentKey: keyHat },
"À": { key: "KeyA", shift: true, accentKey: keyGrave },
"Ã": { key: "KeyA", shift: true, accentKey: keyTilde },
B: { key: "KeyB", shift: true },
C: { key: "KeyC", shift: true },
D: { key: "KeyD", shift: true },
E: { key: "KeyE", shift: true },
"Ë": { key: "KeyE", shift: true, accentKey: keyTrema },
"É": { key: "KeyE", shift: true, accentKey: keyAcute },
"Ê": { key: "KeyE", shift: true, accentKey: keyHat },
"È": { key: "KeyE", shift: true, accentKey: keyGrave },
"Ẽ": { key: "KeyE", shift: true, accentKey: keyTilde },
F: { key: "KeyF", shift: true },
G: { key: "KeyG", shift: true },
H: { key: "KeyH", shift: true },
I: { key: "KeyI", shift: true },
"Ï": { key: "KeyI", shift: true, accentKey: keyTrema },
"Í": { key: "KeyI", shift: true, accentKey: keyAcute },
"Î": { key: "KeyI", shift: true, accentKey: keyHat },
"Ì": { key: "KeyI", shift: true, accentKey: keyGrave },
"Ĩ": { key: "KeyI", shift: true, accentKey: keyTilde },
J: { key: "KeyJ", shift: true },
K: { key: "KeyK", shift: true },
L: { key: "KeyL", shift: true },
M: { key: "KeyM", shift: true },
N: { key: "KeyN", shift: true },
O: { key: "KeyO", shift: true },
"Ö": { key: "KeyO", shift: true, accentKey: keyTrema },
"Ó": { key: "KeyO", shift: true, accentKey: keyAcute },
"Ô": { key: "KeyO", shift: true, accentKey: keyHat },
"Ò": { key: "KeyO", shift: true, accentKey: keyGrave },
"Õ": { key: "KeyO", shift: true, accentKey: keyTilde },
P: { key: "KeyP", shift: true },
Q: { key: "KeyQ", shift: true },
R: { key: "KeyR", shift: true },
S: { key: "KeyS", shift: true },
T: { key: "KeyT", shift: true },
U: { key: "KeyU", shift: true },
"Ü": { key: "KeyU", shift: true, accentKey: keyTrema },
"Ú": { key: "KeyU", shift: true, accentKey: keyAcute },
"Û": { key: "KeyU", shift: true, accentKey: keyHat },
"Ù": { key: "KeyU", shift: true, accentKey: keyGrave },
"Ũ": { key: "KeyU", shift: true, accentKey: keyTilde },
V: { key: "KeyV", shift: true },
W: { key: "KeyW", shift: true },
X: { key: "KeyX", shift: true },
Y: { key: "KeyY", shift: true },
Z: { key: "KeyZ", shift: true },
a: { key: "KeyA" },
"ä": { key: "KeyA", accentKey: keyTrema },
"á": { key: "KeyA", accentKey: keyAcute },
"â": { key: "KeyA", accentKey: keyHat },
"à": { key: "KeyA", accentKey: keyGrave },
"ã": { key: "KeyA", accentKey: keyTilde },
b: { key: "KeyB" },
c: { key: "KeyC" },
d: { key: "KeyD" },
e: { key: "KeyE" },
"ë": { key: "KeyE", accentKey: keyTrema },
"é": { key: "KeyE", accentKey: keyAcute },
"ê": { key: "KeyE", accentKey: keyHat },
"è": { key: "KeyE", accentKey: keyGrave },
"ẽ": { key: "KeyE", accentKey: keyTilde },
"€": { key: "KeyE", altRight: true },
f: { key: "KeyF" },
g: { key: "KeyG" },
h: { key: "KeyH" },
i: { key: "KeyI" },
"ï": { key: "KeyI", accentKey: keyTrema },
"í": { key: "KeyI", accentKey: keyAcute },
"î": { key: "KeyI", accentKey: keyHat },
"ì": { key: "KeyI", accentKey: keyGrave },
"ĩ": { key: "KeyI", accentKey: keyTilde },
j: { key: "KeyJ" },
k: { key: "KeyK" },
l: { key: "KeyL" },
m: { key: "KeyM" },
n: { key: "KeyN" },
o: { key: "KeyO" },
"ö": { key: "KeyO", accentKey: keyTrema },
"ó": { key: "KeyO", accentKey: keyAcute },
"ô": { key: "KeyO", accentKey: keyHat },
"ò": { key: "KeyO", accentKey: keyGrave },
"õ": { key: "KeyO", accentKey: keyTilde },
p: { key: "KeyP" },
q: { key: "KeyQ" },
r: { key: "KeyR" },
s: { key: "KeyS" },
t: { key: "KeyT" },
u: { key: "KeyU" },
"ü": { key: "KeyU", accentKey: keyTrema },
"ú": { key: "KeyU", accentKey: keyAcute },
"û": { key: "KeyU", accentKey: keyHat },
"ù": { key: "KeyU", accentKey: keyGrave },
"ũ": { key: "KeyU", accentKey: keyTilde },
v: { key: "KeyV" },
w: { key: "KeyW" },
x: { key: "KeyX" },
y: { key: "KeyY" },
z: { key: "KeyZ" },
"º": { key: "Backquote" },
"ª": { key: "Backquote", shift: true },
"\\": { key: "Backquote", altRight: true },
1: { key: "Digit1" },
"!": { key: "Digit1", shift: true },
"|": { key: "Digit1", altRight: true },
2: { key: "Digit2" },
"\"": { key: "Digit2", shift: true },
"@": { key: "Digit2", altRight: true },
3: { key: "Digit3" },
"·": { key: "Digit3", shift: true },
"#": { key: "Digit3", altRight: true },
4: { key: "Digit4" },
"$": { key: "Digit4", shift: true },
5: { key: "Digit5" },
"%": { key: "Digit5", shift: true },
6: { key: "Digit6" },
"&": { key: "Digit6", shift: true },
"¬": { key: "Digit6", altRight: true },
7: { key: "Digit7" },
"/": { key: "Digit7", shift: true },
8: { key: "Digit8" },
"(": { key: "Digit8", shift: true },
9: { key: "Digit9" },
")": { key: "Digit9", shift: true },
0: { key: "Digit0" },
"=": { key: "Digit0", shift: true },
"'": { key: "Minus" },
"?": { key: "Minus", shift: true },
"¡": { key: "Equal", deadKey: true },
"¿": { key: "Equal", shift: true },
"[": { key: "BracketLeft", altRight: true },
"+": { key: "BracketRight" },
"*": { key: "BracketRight", shift: true },
"]": { key: "BracketRight", altRight: true },
"ñ": { key: "Semicolon" },
"Ñ": { key: "Semicolon", shift: true },
"{": { key: "Quote", altRight: true },
"ç": { key: "Backslash" },
"Ç": { key: "Backslash", shift: true },
"}": { key: "Backslash", altRight: true },
",": { key: "Comma" },
";": { key: "Comma", shift: true },
".": { key: "Period" },
":": { key: "Period", shift: true },
"-": { key: "Slash" },
"_": { key: "Slash", shift: true },
"<": { key: "IntlBackslash" },
">": { key: "IntlBackslash", shift: true },
" ": { key: "Space" },
"\n": { key: "Enter" },
Enter: { key: "Enter" },
Tab: { key: "Tab" },
} as Record<string, KeyCombo>;

View File

@ -0,0 +1,167 @@
import { KeyCombo } from "../keyboardLayouts"
export const name = "Belgisch Nederlands";
const keyTrema = { key: "BracketLeft", shift: true } // tréma (umlaut), two dots placed above a vowel
const keyHat = { key: "BracketLeft" } // accent circonflexe (accent hat), mark ^ placed above the letter
const keyAcute = { key: "Semicolon", altRight: true } // accent aigu (acute accent), mark ´ placed above the letter
const keyGrave = { key: "Quote", shift: true } // accent grave, mark ` placed above the letter
const keyTilde = { key: "Slash", altRight: true } // tilde, mark ~ placed above the letter
export const chars = {
A: { key: "KeyQ", shift: true },
"Ä": { key: "KeyQ", shift: true, accentKey: keyTrema },
"Â": { key: "KeyQ", shift: true, accentKey: keyHat },
"Á": { key: "KeyQ", shift: true, accentKey: keyAcute },
"À": { key: "KeyQ", shift: true, accentKey: keyGrave },
"Ã": { key: "KeyQ", shift: true, accentKey: keyTilde },
B: { key: "KeyB", shift: true },
C: { key: "KeyC", shift: true },
D: { key: "KeyD", shift: true },
E: { key: "KeyE", shift: true },
"Ë": { key: "KeyE", shift: true, accentKey: keyTrema },
"Ê": { key: "KeyE", shift: true, accentKey: keyHat },
"É": { key: "KeyE", shift: true, accentKey: keyAcute },
"È": { key: "KeyE", shift: true, accentKey: keyGrave },
"Ẽ": { key: "KeyE", shift: true, accentKey: keyTilde },
F: { key: "KeyF", shift: true },
G: { key: "KeyG", shift: true },
H: { key: "KeyH", shift: true },
I: { key: "KeyI", shift: true },
"Ï": { key: "KeyI", shift: true, accentKey: keyTrema },
"Î": { key: "KeyI", shift: true, accentKey: keyHat },
"Í": { key: "KeyI", shift: true, accentKey: keyAcute },
"Ì": { key: "KeyI", shift: true, accentKey: keyGrave },
"Ĩ": { key: "KeyI", shift: true, accentKey: keyTilde },
J: { key: "KeyJ", shift: true },
K: { key: "KeyK", shift: true },
L: { key: "KeyL", shift: true },
M: { key: "Semicolon", shift: true },
N: { key: "KeyN", shift: true },
O: { key: "KeyO", shift: true },
"Ö": { key: "KeyO", shift: true, accentKey: keyTrema },
"Ô": { key: "KeyO", shift: true, accentKey: keyHat },
"Ó": { key: "KeyO", shift: true, accentKey: keyAcute },
"Ò": { key: "KeyO", shift: true, accentKey: keyGrave },
"Õ": { key: "KeyO", shift: true, accentKey: keyTilde },
P: { key: "KeyP", shift: true },
Q: { key: "KeyA", shift: true },
R: { key: "KeyR", shift: true },
S: { key: "KeyS", shift: true },
T: { key: "KeyT", shift: true },
U: { key: "KeyU", shift: true },
"Ü": { key: "KeyU", shift: true, accentKey: keyTrema },
"Û": { key: "KeyU", shift: true, accentKey: keyHat },
"Ú": { key: "KeyU", shift: true, accentKey: keyAcute },
"Ù": { key: "KeyU", shift: true, accentKey: keyGrave },
"Ũ": { key: "KeyU", shift: true, accentKey: keyTilde },
V: { key: "KeyV", shift: true },
W: { key: "KeyW", shift: true },
X: { key: "KeyX", shift: true },
Y: { key: "KeyZ", shift: true },
Z: { key: "KeyY", shift: true },
a: { key: "KeyQ" },
"ä": { key: "KeyQ", accentKey: keyTrema },
"â": { key: "KeyQ", accentKey: keyHat },
"á": { key: "KeyQ", accentKey: keyAcute },
"ã": { key: "KeyQ", accentKey: keyTilde },
b: { key: "KeyB" },
c: { key: "KeyC" },
d: { key: "KeyD" },
e: { key: "KeyE" },
"ë": { key: "KeyE", accentKey: keyTrema },
"ê": { key: "KeyE", accentKey: keyHat },
"ẽ": { key: "KeyE", accentKey: keyTilde },
"€": { key: "KeyE", altRight: true },
f: { key: "KeyF" },
g: { key: "KeyG" },
h: { key: "KeyH" },
i: { key: "KeyI" },
"ï": { key: "KeyI", accentKey: keyTrema },
"î": { key: "KeyI", accentKey: keyHat },
"í": { key: "KeyI", accentKey: keyAcute },
"ì": { key: "KeyI", accentKey: keyGrave },
"ĩ": { key: "KeyI", accentKey: keyTilde },
j: { key: "KeyJ" },
k: { key: "KeyK" },
l: { key: "KeyL" },
m: { key: "Semicolon" },
n: { key: "KeyN" },
o: { key: "KeyO" },
"ö": { key: "KeyO", accentKey: keyTrema },
"ó": { key: "KeyO", accentKey: keyAcute },
"ô": { key: "KeyO", accentKey: keyHat },
"ò": { key: "KeyO", accentKey: keyGrave },
"õ": { key: "KeyO", accentKey: keyTilde },
p: { key: "KeyP" },
q: { key: "KeyA" },
r: { key: "KeyR" },
s: { key: "KeyS" },
t: { key: "KeyT" },
u: { key: "KeyU" },
"ü": { key: "KeyU", accentKey: keyTrema },
"û": { key: "KeyU", accentKey: keyHat },
"ú": { key: "KeyU", accentKey: keyAcute },
"ũ": { key: "KeyU", accentKey: keyTilde },
v: { key: "KeyV" },
w: { key: "KeyW" },
x: { key: "KeyX" },
y: { key: "KeyZ" },
z: { key: "KeyY" },
"²": { key: "Backquote" },
"³": { key: "Backquote", shift: true },
"&": { key: "Digit1" },
1: { key: "Digit1", shift: true },
"|": { key: "Digit1", altRight: true },
"é": { key: "Digit2" },
2: { key: "Digit2", shift: true },
"@": { key: "Digit2", altRight: true },
"\"": { key: "Digit3" },
3: { key: "Digit3", shift: true },
"#": { key: "Digit3", altRight: true },
"'": { key: "Digit4" },
4: { key: "Digit4", shift: true },
"(": { key: "Digit5" },
5: { key: "Digit5", shift: true },
"§": { key: "Digit6" },
6: { key: "Digit6", shift: true },
"^": { key: "Digit6", altRight: true },
"è": { key: "Digit7" },
7: { key: "Digit7", shift: true },
"!": { key: "Digit8" },
8: { key: "Digit8", shift: true },
"ç": { key: "Digit9" },
9: { key: "Digit9", shift: true },
"{": { key: "Digit9", altRight: true },
"à": { key: "Digit0" },
0: { key: "Digit0", shift: true },
"}": { key: "Digit0", altRight: true },
")": { key: "Minus" },
"°": { key: "Minus", shift: true },
"-": { key: "Equal", deadKey: true },
"_": { key: "Equal", shift: true },
"[": { key: "BracketLeft", altRight: true },
"$": { key: "BracketRight" },
"*": { key: "BracketRight", altRight: true },
"]": { key: "BracketRight", altRight: true },
"ù": { key: "Quote" },
"%": { key: "Quote", shift: true },
"µ": { key: "Backslash" },
"£": { key: "Backslash", shift: true },
",": { key: "KeyM" },
"?": { key: "KeyM", shift: true },
";": { key: "Comma" },
".": { key: "Comma", shift: true },
":": { key: "Period" },
"/": { key: "Period", shift: true },
"=": { key: "Slash" },
"+": { key: "Slash", shift: true },
"~": { key: "Slash", deadKey: true },
"<": { key: "IntlBackslash" },
">": { key: "IntlBackslash", shift: true },
"\\": { key: "IntlBackslash", altRight: true },
" ": { key: "Space" },
"\n": { key: "Enter" },
Enter: { key: "Enter" },
Tab: { key: "Tab" },
} as Record<string, KeyCombo>;

View File

@ -0,0 +1,14 @@
import { KeyCombo } from "../keyboardLayouts"
import { chars as chars_de_CH } from "./de_CH"
export const name = "Français de Suisse";
export const chars = {
...chars_de_CH,
"è": { key: "BracketLeft" },
"ü": { key: "BracketLeft", shift: true },
"é": { key: "Semicolon" },
"ö": { key: "Semicolon", shift: true },
"à": { key: "Quote" },
"ä": { key: "Quote", shift: true },
} as Record<string, KeyCombo>;

View File

@ -0,0 +1,139 @@
import { KeyCombo } from "../keyboardLayouts"
export const name = "Français";
const keyTrema = { key: "BracketLeft", shift: true } // tréma (umlaut), two dots placed above a vowel
const keyHat = { key: "BracketLeft" } // accent circonflexe (accent hat), mark ^ placed above the letter
export const chars = {
A: { key: "KeyQ", shift: true },
"Ä": { key: "KeyQ", shift: true, accentKey: keyTrema },
"Â": { key: "KeyQ", shift: true, accentKey: keyHat },
B: { key: "KeyB", shift: true },
C: { key: "KeyC", shift: true },
D: { key: "KeyD", shift: true },
E: { key: "KeyE", shift: true },
"Ë": { key: "KeyE", shift: true, accentKey: keyTrema },
"Ê": { key: "KeyE", shift: true, accentKey: keyHat },
F: { key: "KeyF", shift: true },
G: { key: "KeyG", shift: true },
H: { key: "KeyH", shift: true },
I: { key: "KeyI", shift: true },
"Ï": { key: "KeyI", shift: true, accentKey: keyTrema },
"Î": { key: "KeyI", shift: true, accentKey: keyHat },
J: { key: "KeyJ", shift: true },
K: { key: "KeyK", shift: true },
L: { key: "KeyL", shift: true },
M: { key: "Semicolon", shift: true },
N: { key: "KeyN", shift: true },
O: { key: "KeyO", shift: true },
"Ö": { key: "KeyO", shift: true, accentKey: keyTrema },
"Ô": { key: "KeyO", shift: true, accentKey: keyHat },
P: { key: "KeyP", shift: true },
Q: { key: "KeyA", shift: true },
R: { key: "KeyR", shift: true },
S: { key: "KeyS", shift: true },
T: { key: "KeyT", shift: true },
U: { key: "KeyU", shift: true },
"Ü": { key: "KeyU", shift: true, accentKey: keyTrema },
"Û": { key: "KeyU", shift: true, accentKey: keyHat },
V: { key: "KeyV", shift: true },
W: { key: "KeyZ", shift: true },
X: { key: "KeyX", shift: true },
Y: { key: "KeyY", shift: true },
Z: { key: "KeyW", shift: true },
a: { key: "KeyQ" },
"ä": { key: "KeyQ", accentKey: keyTrema },
"â": { key: "KeyQ", accentKey: keyHat },
b: { key: "KeyB" },
c: { key: "KeyC" },
d: { key: "KeyD" },
e: { key: "KeyE" },
"ë": { key: "KeyE", accentKey: keyTrema },
"ê": { key: "KeyE", accentKey: keyHat },
"€": { key: "KeyE", altRight: true },
f: { key: "KeyF" },
g: { key: "KeyG" },
h: { key: "KeyH" },
i: { key: "KeyI" },
"ï": { key: "KeyI", accentKey: keyTrema },
"î": { key: "KeyI", accentKey: keyHat },
j: { key: "KeyJ" },
k: { key: "KeyK" },
l: { key: "KeyL" },
m: { key: "Semicolon" },
n: { key: "KeyN" },
o: { key: "KeyO" },
"ö": { key: "KeyO", accentKey: keyTrema },
"ô": { key: "KeyO", accentKey: keyHat },
p: { key: "KeyP" },
q: { key: "KeyA" },
r: { key: "KeyR" },
s: { key: "KeyS" },
t: { key: "KeyT" },
u: { key: "KeyU" },
"ü": { key: "KeyU", accentKey: keyTrema },
"û": { key: "KeyU", accentKey: keyHat },
v: { key: "KeyV" },
w: { key: "KeyZ" },
x: { key: "KeyX" },
y: { key: "KeyY" },
z: { key: "KeyW" },
"²": { key: "Backquote" },
"&": { key: "Digit1" },
1: { key: "Digit1", shift: true },
"é": { key: "Digit2" },
2: { key: "Digit2", shift: true },
"~": { key: "Digit2", altRight: true },
"\"": { key: "Digit3" },
3: { key: "Digit3", shift: true },
"#": { key: "Digit3", altRight: true },
"'": { key: "Digit4" },
4: { key: "Digit4", shift: true },
"{": { key: "Digit4", altRight: true },
"(": { key: "Digit5" },
5: { key: "Digit5", shift: true },
"[": { key: "Digit5", altRight: true },
"-": { key: "Digit6" },
6: { key: "Digit6", shift: true },
"|": { key: "Digit6", altRight: true },
"è": { key: "Digit7" },
7: { key: "Digit7", shift: true },
"`": { key: "Digit7", altRight: true },
"_": { key: "Digit8" },
8: { key: "Digit8", shift: true },
"\\": { key: "Digit8", altRight: true },
"ç": { key: "Digit9" },
9: { key: "Digit9", shift: true },
"^": { key: "Digit9", altRight: true },
"à": { key: "Digit0" },
0: { key: "Digit0", shift: true },
"@": { key: "Digit0", altRight: true },
")": { key: "Minus" },
"°": { key: "Minus", shift: true },
"]": { key: "Minus", altRight: true },
"=": { key: "Equal" },
"+": { key: "Equal", shift: true },
"}": { key: "Equal", altRight: true },
"$": { key: "BracketRight" },
"£": { key: "BracketRight", shift: true },
"¤": { key: "BracketRight", altRight: true },
"ù": { key: "Quote" },
"%": { key: "Quote", shift: true },
"*": { key: "Backslash" },
"µ": { key: "Backslash", shift: true },
",": { key: "KeyM" },
"?": { key: "KeyM", shift: true },
";": { key: "Comma" },
".": { key: "Comma", shift: true },
":": { key: "Period" },
"/": { key: "Period", shift: true },
"!": { key: "Slash" },
"§": { key: "Slash", shift: true },
"<": { key: "IntlBackslash" },
">": { key: "IntlBackslash", shift: true },
" ": { key: "Space" },
"\n": { key: "Enter" },
Enter: { key: "Enter" },
Tab: { key: "Tab" },
} as Record<string, KeyCombo>;

View File

@ -0,0 +1,113 @@
import { KeyCombo } from "../keyboardLayouts"
export const name = "Italiano";
export const chars = {
A: { key: "KeyA", shift: true },
B: { key: "KeyB", shift: true },
C: { key: "KeyC", shift: true },
D: { key: "KeyD", shift: true },
E: { key: "KeyE", shift: true },
F: { key: "KeyF", shift: true },
G: { key: "KeyG", shift: true },
H: { key: "KeyH", shift: true },
I: { key: "KeyI", shift: true },
J: { key: "KeyJ", shift: true },
K: { key: "KeyK", shift: true },
L: { key: "KeyL", shift: true },
M: { key: "KeyM", shift: true },
N: { key: "KeyN", shift: true },
O: { key: "KeyO", shift: true },
P: { key: "KeyP", shift: true },
Q: { key: "KeyQ", shift: true },
R: { key: "KeyR", shift: true },
S: { key: "KeyS", shift: true },
T: { key: "KeyT", shift: true },
U: { key: "KeyU", shift: true },
V: { key: "KeyV", shift: true },
W: { key: "KeyW", shift: true },
X: { key: "KeyX", shift: true },
Y: { key: "KeyY", shift: true },
Z: { key: "KeyZ", shift: true },
a: { key: "KeyA" },
b: { key: "KeyB" },
c: { key: "KeyC" },
d: { key: "KeyD" },
e: { key: "KeyE" },
"€": { key: "KeyE", altRight: true },
f: { key: "KeyF" },
g: { key: "KeyG" },
h: { key: "KeyH" },
i: { key: "KeyI" },
j: { key: "KeyJ" },
k: { key: "KeyK" },
l: { key: "KeyL" },
m: { key: "KeyM" },
n: { key: "KeyN" },
o: { key: "KeyO" },
p: { key: "KeyP" },
q: { key: "KeyQ" },
r: { key: "KeyR" },
s: { key: "KeyS" },
t: { key: "KeyT" },
u: { key: "KeyU" },
v: { key: "KeyV" },
w: { key: "KeyW" },
x: { key: "KeyX" },
y: { key: "KeyY" },
z: { key: "KeyZ" },
"\\": { key: "Backquote" },
"|": { key: "Backquote", shift: true },
1: { key: "Digit1" },
"!": { key: "Digit1", shift: true },
2: { key: "Digit2" },
"\"": { key: "Digit2", shift: true },
3: { key: "Digit3" },
"£": { key: "Digit3", shift: true },
4: { key: "Digit4" },
"$": { key: "Digit4", shift: true },
5: { key: "Digit5" },
"%": { key: "Digit5", shift: true },
6: { key: "Digit6" },
"&": { key: "Digit6", shift: true },
7: { key: "Digit7" },
"/": { key: "Digit7", shift: true },
8: { key: "Digit8" },
"(": { key: "Digit8", shift: true },
9: { key: "Digit9" },
")": { key: "Digit9", shift: true },
0: { key: "Digit0" },
"=": { key: "Digit0", shift: true },
"'": { key: "Minus" },
"?": { key: "Minus", shift: true },
"ì": { key: "Equal" },
"^": { key: "Equal", shift: true },
"è": { key: "BracketLeft" },
"é": { key: "BracketLeft", shift: true },
"[": { key: "BracketLeft", altRight: true },
"{": { key: "BracketLeft", shift: true, altRight: true },
"+": { key: "BracketRight" },
"*": { key: "BracketRight", shift: true },
"]": { key: "BracketRight", altRight: true },
"}": { key: "BracketRight", shift: true, altRight: true },
"ò": { key: "Semicolon" },
"ç": { key: "Semicolon", shift: true },
"@": { key: "Semicolon", altRight: true },
"à": { key: "Quote" },
"°": { key: "Quote", shift: true },
"#": { key: "Quote", altRight: true },
"ù": { key: "Backslash" },
"§": { key: "Backslash", shift: true },
",": { key: "Comma" },
";": { key: "Comma", shift: true },
".": { key: "Period" },
":": { key: "Period", shift: true },
"-": { key: "Slash" },
"_": { key: "Slash", shift: true },
"<": { key: "IntlBackslash" },
">": { key: "IntlBackslash", shift: true },
" ": { key: "Space" },
"\n": { key: "Enter" },
Enter: { key: "Enter" },
Tab: { key: "Tab" },
} as Record<string, KeyCombo>;

View File

@ -0,0 +1,167 @@
import { KeyCombo } from "../keyboardLayouts"
export const name = "Norsk bokmål";
const keyTrema = { key: "BracketRight" } // tréma (umlaut), two dots placed above a vowel
const keyAcute = { key: "Equal", altRight: true } // accent aigu (acute accent), mark ´ placed above the letter
const keyHat = { key: "BracketRight", shift: true } // accent circonflexe (accent hat), mark ^ placed above the letter
const keyGrave = { key: "Equal", shift: true } // accent grave, mark ` placed above the letter
const keyTilde = { key: "BracketRight", altRight: true } // tilde, mark ~ placed above the letter
export const chars = {
A: { key: "KeyA", shift: true },
"Ä": { key: "KeyA", shift: true, accentKey: keyTrema },
"Á": { key: "KeyA", shift: true, accentKey: keyAcute },
"Â": { key: "KeyA", shift: true, accentKey: keyHat },
"À": { key: "KeyA", shift: true, accentKey: keyGrave },
"Ã": { key: "KeyA", shift: true, accentKey: keyTilde },
B: { key: "KeyB", shift: true },
C: { key: "KeyC", shift: true },
D: { key: "KeyD", shift: true },
E: { key: "KeyE", shift: true },
"Ë": { key: "KeyE", shift: true, accentKey: keyTrema },
"É": { key: "KeyE", shift: true, accentKey: keyAcute },
"Ê": { key: "KeyE", shift: true, accentKey: keyHat },
"È": { key: "KeyE", shift: true, accentKey: keyGrave },
"Ẽ": { key: "KeyE", shift: true, accentKey: keyTilde },
F: { key: "KeyF", shift: true },
G: { key: "KeyG", shift: true },
H: { key: "KeyH", shift: true },
I: { key: "KeyI", shift: true },
"Ï": { key: "KeyI", shift: true, accentKey: keyTrema },
"Í": { key: "KeyI", shift: true, accentKey: keyAcute },
"Î": { key: "KeyI", shift: true, accentKey: keyHat },
"Ì": { key: "KeyI", shift: true, accentKey: keyGrave },
"Ĩ": { key: "KeyI", shift: true, accentKey: keyTilde },
J: { key: "KeyJ", shift: true },
K: { key: "KeyK", shift: true },
L: { key: "KeyL", shift: true },
M: { key: "KeyM", shift: true },
N: { key: "KeyN", shift: true },
O: { key: "KeyO", shift: true },
"Ö": { key: "KeyO", shift: true, accentKey: keyTrema },
"Ó": { key: "KeyO", shift: true, accentKey: keyAcute },
"Ô": { key: "KeyO", shift: true, accentKey: keyHat },
"Ò": { key: "KeyO", shift: true, accentKey: keyGrave },
"Õ": { key: "KeyO", shift: true, accentKey: keyTilde },
P: { key: "KeyP", shift: true },
Q: { key: "KeyQ", shift: true },
R: { key: "KeyR", shift: true },
S: { key: "KeyS", shift: true },
T: { key: "KeyT", shift: true },
U: { key: "KeyU", shift: true },
"Ü": { key: "KeyU", shift: true, accentKey: keyTrema },
"Ú": { key: "KeyU", shift: true, accentKey: keyAcute },
"Û": { key: "KeyU", shift: true, accentKey: keyHat },
"Ù": { key: "KeyU", shift: true, accentKey: keyGrave },
"Ũ": { key: "KeyU", shift: true, accentKey: keyTilde },
V: { key: "KeyV", shift: true },
W: { key: "KeyW", shift: true },
X: { key: "KeyX", shift: true },
Y: { key: "KeyZ", shift: true },
Z: { key: "KeyY", shift: true },
a: { key: "KeyA" },
"ä": { key: "KeyA", accentKey: keyTrema },
"á": { key: "KeyA", accentKey: keyAcute },
"â": { key: "KeyA", accentKey: keyHat },
"à": { key: "KeyA", accentKey: keyGrave },
"ã": { key: "KeyA", accentKey: keyTilde },
b: { key: "KeyB" },
c: { key: "KeyC" },
d: { key: "KeyD" },
e: { key: "KeyE" },
"ë": { key: "KeyE", accentKey: keyTrema },
"é": { key: "KeyE", accentKey: keyAcute },
"ê": { key: "KeyE", accentKey: keyHat },
"è": { key: "KeyE", accentKey: keyGrave },
"ẽ": { key: "KeyE", accentKey: keyTilde },
"€": { key: "KeyE", altRight: true },
f: { key: "KeyF" },
g: { key: "KeyG" },
h: { key: "KeyH" },
i: { key: "KeyI" },
"ï": { key: "KeyI", accentKey: keyTrema },
"í": { key: "KeyI", accentKey: keyAcute },
"î": { key: "KeyI", accentKey: keyHat },
"ì": { key: "KeyI", accentKey: keyGrave },
"ĩ": { key: "KeyI", accentKey: keyTilde },
j: { key: "KeyJ" },
k: { key: "KeyK" },
l: { key: "KeyL" },
m: { key: "KeyM" },
n: { key: "KeyN" },
o: { key: "KeyO" },
"ö": { key: "KeyO", accentKey: keyTrema },
"ó": { key: "KeyO", accentKey: keyAcute },
"ô": { key: "KeyO", accentKey: keyHat },
"ò": { key: "KeyO", accentKey: keyGrave },
"õ": { key: "KeyO", accentKey: keyTilde },
p: { key: "KeyP" },
q: { key: "KeyQ" },
r: { key: "KeyR" },
s: { key: "KeyS" },
t: { key: "KeyT" },
u: { key: "KeyU" },
"ü": { key: "KeyU", accentKey: keyTrema },
"ú": { key: "KeyU", accentKey: keyAcute },
"û": { key: "KeyU", accentKey: keyHat },
"ù": { key: "KeyU", accentKey: keyGrave },
"ũ": { key: "KeyU", accentKey: keyTilde },
v: { key: "KeyV" },
w: { key: "KeyW" },
x: { key: "KeyX" },
y: { key: "KeyZ" },
z: { key: "KeyY" },
"|": { key: "Backquote" },
"§": { key: "Backquote", shift: true },
1: { key: "Digit1" },
"!": { key: "Digit1", shift: true },
2: { key: "Digit2" },
"\"": { key: "Digit2", shift: true },
"@": { key: "Digit2", altRight: true },
3: { key: "Digit3" },
"#": { key: "Digit3", shift: true },
"£": { key: "Digit3", altRight: true },
4: { key: "Digit4" },
"¤": { key: "Digit4", shift: true },
"$": { key: "Digit4", altRight: true },
5: { key: "Digit5" },
"%": { key: "Digit5", shift: true },
6: { key: "Digit6" },
"&": { key: "Digit6", shift: true },
7: { key: "Digit7" },
"/": { key: "Digit7", shift: true },
"{": { key: "Digit7", altRight: true },
8: { key: "Digit8" },
"(": { key: "Digit8", shift: true },
"[": { key: "Digit8", altRight: true },
9: { key: "Digit9" },
")": { key: "Digit9", shift: true },
"]": { key: "Digit9", altRight: true },
0: { key: "Digit0" },
"=": { key: "Digit0", shift: true },
"}": { key: "Digit0", altRight: true },
"+": { key: "Minus" },
"?": { key: "Minus", shift: true },
"\\": { key: "Equal" },
"å": { key: "BracketLeft" },
"Å": { key: "BracketLeft", shift: true },
"ø": { key: "Semicolon" },
"Ø": { key: "Semicolon", shift: true },
"æ": { key: "Quote" },
"Æ": { key: "Quote", shift: true },
"'": { key: "Backslash" },
"*": { key: "Backslash", shift: true },
",": { key: "Comma" },
";": { key: "Comma", shift: true },
".": { key: "Period" },
":": { key: "Period", shift: true },
"-": { key: "Slash" },
"_": { key: "Slash", shift: true },
"<": { key: "IntlBackslash" },
">": { key: "IntlBackslash", shift: true },
" ": { key: "Space" },
"\n": { key: "Enter" },
Enter: { key: "Enter" },
Tab: { key: "Tab" },
} as Record<string, KeyCombo>;

View File

@ -0,0 +1,164 @@
import { KeyCombo } from "../keyboardLayouts"
export const name = "Svenska";
const keyTrema = { key: "BracketRight" } // tréma (umlaut), two dots placed above a vowel
const keyAcute = { key: "Equal" } // accent aigu (acute accent), mark ´ placed above the letter
const keyHat = { key: "BracketRight", shift: true } // accent circonflexe (accent hat), mark ^ placed above the letter
const keyGrave = { key: "Equal", shift: true } // accent grave, mark ` placed above the letter
const keyTilde = { key: "BracketRight", altRight: true } // tilde, mark ~ placed above the letter
export const chars = {
A: { key: "KeyA", shift: true },
"Á": { key: "KeyA", shift: true, accentKey: keyAcute },
"Â": { key: "KeyA", shift: true, accentKey: keyHat },
"À": { key: "KeyA", shift: true, accentKey: keyGrave },
"Ã": { key: "KeyA", shift: true, accentKey: keyTilde },
B: { key: "KeyB", shift: true },
C: { key: "KeyC", shift: true },
D: { key: "KeyD", shift: true },
E: { key: "KeyE", shift: true },
"Ë": { key: "KeyE", shift: true, accentKey: keyTrema },
"É": { key: "KeyE", shift: true, accentKey: keyAcute },
"Ê": { key: "KeyE", shift: true, accentKey: keyHat },
"È": { key: "KeyE", shift: true, accentKey: keyGrave },
"Ẽ": { key: "KeyE", shift: true, accentKey: keyTilde },
F: { key: "KeyF", shift: true },
G: { key: "KeyG", shift: true },
H: { key: "KeyH", shift: true },
I: { key: "KeyI", shift: true },
"Ï": { key: "KeyI", shift: true, accentKey: keyTrema },
"Í": { key: "KeyI", shift: true, accentKey: keyAcute },
"Î": { key: "KeyI", shift: true, accentKey: keyHat },
"Ì": { key: "KeyI", shift: true, accentKey: keyGrave },
"Ĩ": { key: "KeyI", shift: true, accentKey: keyTilde },
J: { key: "KeyJ", shift: true },
K: { key: "KeyK", shift: true },
L: { key: "KeyL", shift: true },
M: { key: "KeyM", shift: true },
N: { key: "KeyN", shift: true },
O: { key: "KeyO", shift: true },
"Ó": { key: "KeyO", shift: true, accentKey: keyAcute },
"Ô": { key: "KeyO", shift: true, accentKey: keyHat },
"Ò": { key: "KeyO", shift: true, accentKey: keyGrave },
"Õ": { key: "KeyO", shift: true, accentKey: keyTilde },
P: { key: "KeyP", shift: true },
Q: { key: "KeyQ", shift: true },
R: { key: "KeyR", shift: true },
S: { key: "KeyS", shift: true },
T: { key: "KeyT", shift: true },
U: { key: "KeyU", shift: true },
"Ü": { key: "KeyU", shift: true, accentKey: keyTrema },
"Ú": { key: "KeyU", shift: true, accentKey: keyAcute },
"Û": { key: "KeyU", shift: true, accentKey: keyHat },
"Ù": { key: "KeyU", shift: true, accentKey: keyGrave },
"Ũ": { key: "KeyU", shift: true, accentKey: keyTilde },
V: { key: "KeyV", shift: true },
W: { key: "KeyW", shift: true },
X: { key: "KeyX", shift: true },
Y: { key: "KeyY", shift: true },
Z: { key: "KeyZ", shift: true },
a: { key: "KeyA" },
"á": { key: "KeyA", accentKey: keyAcute },
"â": { key: "KeyA", accentKey: keyHat },
"à": { key: "KeyA", accentKey: keyGrave },
"ã": { key: "KeyA", accentKey: keyTilde },
b: { key: "KeyB" },
c: { key: "KeyC" },
d: { key: "KeyD" },
e: { key: "KeyE" },
"ë": { key: "KeyE", accentKey: keyTrema },
"é": { key: "KeyE", accentKey: keyAcute },
"ê": { key: "KeyE", accentKey: keyHat },
"è": { key: "KeyE", accentKey: keyGrave },
"ẽ": { key: "KeyE", accentKey: keyTilde },
"€": { key: "KeyE", altRight: true },
f: { key: "KeyF" },
g: { key: "KeyG" },
h: { key: "KeyH" },
i: { key: "KeyI" },
"ï": { key: "KeyI", accentKey: keyTrema },
"í": { key: "KeyI", accentKey: keyAcute },
"î": { key: "KeyI", accentKey: keyHat },
"ì": { key: "KeyI", accentKey: keyGrave },
"ĩ": { key: "KeyI", accentKey: keyTilde },
j: { key: "KeyJ" },
k: { key: "KeyK" },
l: { key: "KeyL" },
m: { key: "KeyM" },
n: { key: "KeyN" },
o: { key: "KeyO" },
"ó": { key: "KeyO", accentKey: keyAcute },
"ô": { key: "KeyO", accentKey: keyHat },
"ò": { key: "KeyO", accentKey: keyGrave },
"õ": { key: "KeyO", accentKey: keyTilde },
p: { key: "KeyP" },
q: { key: "KeyQ" },
r: { key: "KeyR" },
s: { key: "KeyS" },
t: { key: "KeyT" },
u: { key: "KeyU" },
"ü": { key: "KeyU", accentKey: keyTrema },
"ú": { key: "KeyU", accentKey: keyAcute },
"û": { key: "KeyU", accentKey: keyHat },
"ù": { key: "KeyU", accentKey: keyGrave },
"ũ": { key: "KeyU", accentKey: keyTilde },
v: { key: "KeyV" },
w: { key: "KeyW" },
x: { key: "KeyX" },
y: { key: "KeyY" },
z: { key: "KeyZ" },
"§": { key: "Backquote" },
"½": { key: "Backquote", shift: true },
1: { key: "Digit1" },
"!": { key: "Digit1", shift: true },
2: { key: "Digit2" },
"\"": { key: "Digit2", shift: true },
"@": { key: "Digit2", altRight: true },
3: { key: "Digit3" },
"#": { key: "Digit3", shift: true },
"£": { key: "Digit3", altRight: true },
4: { key: "Digit4" },
"¤": { key: "Digit4", shift: true },
"$": { key: "Digit4", altRight: true },
5: { key: "Digit5" },
"%": { key: "Digit5", shift: true },
6: { key: "Digit6" },
"&": { key: "Digit6", shift: true },
7: { key: "Digit7" },
"/": { key: "Digit7", shift: true },
"{": { key: "Digit7", altRight: true },
8: { key: "Digit8" },
"(": { key: "Digit8", shift: true },
"[": { key: "Digit8", altRight: true },
9: { key: "Digit9" },
")": { key: "Digit9", shift: true },
"]": { key: "Digit9", altRight: true },
0: { key: "Digit0" },
"=": { key: "Digit0", shift: true },
"}": { key: "Digit0", altRight: true },
"+": { key: "Minus" },
"?": { key: "Minus", shift: true },
"\\": { key: "Minus", altRight: true },
"å": { key: "BracketLeft" },
"Å": { key: "BracketLeft", shift: true },
"ö": { key: "Semicolon" },
"Ö": { key: "Semicolon", shift: true },
"ä": { key: "Quote" },
"Ä": { key: "Quote", shift: true },
"'": { key: "Backslash" },
"*": { key: "Backslash", shift: true },
",": { key: "Comma" },
";": { key: "Comma", shift: true },
".": { key: "Period" },
":": { key: "Period", shift: true },
"-": { key: "Slash" },
"_": { key: "Slash", shift: true },
"<": { key: "IntlBackslash" },
">": { key: "IntlBackslash", shift: true },
"|": { key: "IntlBackslash", altRight: true },
" ": { key: "Space" },
"\n": { key: "Enter" },
Enter: { key: "Enter" },
Tab: { key: "Tab" },
} as Record<string, KeyCombo>;

View File

@ -43,7 +43,7 @@ export const keys = {
F13: 0x68, F13: 0x68,
Home: 0x4a, Home: 0x4a,
Insert: 0x49, Insert: 0x49,
IntlBackslash: 0x31, IntlBackslash: 0x64,
KeyA: 0x04, KeyA: 0x04,
KeyB: 0x05, KeyB: 0x05,
KeyC: 0x06, KeyC: 0x06,
@ -104,116 +104,6 @@ export const keys = {
Tab: 0x2b, Tab: 0x2b,
} as Record<string, number>; } as Record<string, number>;
export const chars = {
A: { key: "KeyA", shift: true },
B: { key: "KeyB", shift: true },
C: { key: "KeyC", shift: true },
D: { key: "KeyD", shift: true },
E: { key: "KeyE", shift: true },
F: { key: "KeyF", shift: true },
G: { key: "KeyG", shift: true },
H: { key: "KeyH", shift: true },
I: { key: "KeyI", shift: true },
J: { key: "KeyJ", shift: true },
K: { key: "KeyK", shift: true },
L: { key: "KeyL", shift: true },
M: { key: "KeyM", shift: true },
N: { key: "KeyN", shift: true },
O: { key: "KeyO", shift: true },
P: { key: "KeyP", shift: true },
Q: { key: "KeyQ", shift: true },
R: { key: "KeyR", shift: true },
S: { key: "KeyS", shift: true },
T: { key: "KeyT", shift: true },
U: { key: "KeyU", shift: true },
V: { key: "KeyV", shift: true },
W: { key: "KeyW", shift: true },
X: { key: "KeyX", shift: true },
Y: { key: "KeyY", shift: true },
Z: { key: "KeyZ", shift: true },
a: { key: "KeyA", shift: false },
b: { key: "KeyB", shift: false },
c: { key: "KeyC", shift: false },
d: { key: "KeyD", shift: false },
e: { key: "KeyE", shift: false },
f: { key: "KeyF", shift: false },
g: { key: "KeyG", shift: false },
h: { key: "KeyH", shift: false },
i: { key: "KeyI", shift: false },
j: { key: "KeyJ", shift: false },
k: { key: "KeyK", shift: false },
l: { key: "KeyL", shift: false },
m: { key: "KeyM", shift: false },
n: { key: "KeyN", shift: false },
o: { key: "KeyO", shift: false },
p: { key: "KeyP", shift: false },
q: { key: "KeyQ", shift: false },
r: { key: "KeyR", shift: false },
s: { key: "KeyS", shift: false },
t: { key: "KeyT", shift: false },
u: { key: "KeyU", shift: false },
v: { key: "KeyV", shift: false },
w: { key: "KeyW", shift: false },
x: { key: "KeyX", shift: false },
y: { key: "KeyY", shift: false },
z: { key: "KeyZ", shift: false },
1: { key: "Digit1", shift: false },
"!": { key: "Digit1", shift: true },
2: { key: "Digit2", shift: false },
"@": { key: "Digit2", shift: true },
3: { key: "Digit3", shift: false },
"#": { key: "Digit3", shift: true },
4: { key: "Digit4", shift: false },
$: { key: "Digit4", shift: true },
"%": { key: "Digit5", shift: true },
5: { key: "Digit5", shift: false },
"^": { key: "Digit6", shift: true },
6: { key: "Digit6", shift: false },
"&": { key: "Digit7", shift: true },
7: { key: "Digit7", shift: false },
"*": { key: "Digit8", shift: true },
8: { key: "Digit8", shift: false },
"(": { key: "Digit9", shift: true },
9: { key: "Digit9", shift: false },
")": { key: "Digit0", shift: true },
0: { key: "Digit0", shift: false },
"-": { key: "Minus", shift: false },
_: { key: "Minus", shift: true },
"=": { key: "Equal", shift: false },
"+": { key: "Equal", shift: true },
"'": { key: "Quote", shift: false },
'"': { key: "Quote", shift: true },
",": { key: "Comma", shift: false },
"<": { key: "Comma", shift: true },
"/": { key: "Slash", shift: false },
"?": { key: "Slash", shift: true },
".": { key: "Period", shift: false },
">": { key: "Period", shift: true },
";": { key: "Semicolon", shift: false },
":": { key: "Semicolon", shift: true },
"[": { key: "BracketLeft", shift: false },
"{": { key: "BracketLeft", shift: true },
"]": { key: "BracketRight", shift: false },
"}": { key: "BracketRight", shift: true },
"\\": { key: "Backslash", shift: false },
"|": { key: "Backslash", shift: true },
"`": { key: "Backquote", shift: false },
"~": { key: "Backquote", shift: true },
"§": { key: "IntlBackslash", shift: false },
"±": { key: "IntlBackslash", shift: true },
" ": { key: "Space", shift: false },
"\n": { key: "Enter", shift: false },
Enter: { key: "Enter", shift: false },
Tab: { key: "Tab", shift: false },
PrintScreen: { key: "Prt Sc", shift: false },
SystemRequest: { key: "Prt Sc", shift: true },
ScrollLock: { key: "ScrollLock", shift: false},
Pause: { key: "Pause", shift: false },
Break: { key: "Pause", shift: true },
Insert: { key: "Insert", shift: false },
Delete: { key: "Delete", shift: false },
} as Record<string, { key: string | number; shift: boolean }>;
export const modifiers = { export const modifiers = {
ControlLeft: 0x01, ControlLeft: 0x01,
ControlRight: 0x10, ControlRight: 0x10,

View File

@ -32,7 +32,8 @@ import { CLOUD_API, DEVICE_API } from "./ui.config";
import OtherSessionRoute from "./routes/devices.$id.other-session"; import OtherSessionRoute from "./routes/devices.$id.other-session";
import MountRoute from "./routes/devices.$id.mount"; import MountRoute from "./routes/devices.$id.mount";
import * as SettingsRoute from "./routes/devices.$id.settings"; import * as SettingsRoute from "./routes/devices.$id.settings";
import SettingsKeyboardMouseRoute from "./routes/devices.$id.settings.mouse"; import SettingsMouseRoute from "./routes/devices.$id.settings.mouse";
import SettingsKeyboardRoute from "./routes/devices.$id.settings.keyboard";
import api from "./api"; import api from "./api";
import * as SettingsIndexRoute from "./routes/devices.$id.settings._index"; import * as SettingsIndexRoute from "./routes/devices.$id.settings._index";
import SettingsAdvancedRoute from "./routes/devices.$id.settings.advanced"; import SettingsAdvancedRoute from "./routes/devices.$id.settings.advanced";
@ -147,7 +148,11 @@ if (isOnDevice) {
}, },
{ {
path: "mouse", path: "mouse",
element: <SettingsKeyboardMouseRoute />, element: <SettingsMouseRoute />,
},
{
path: "keyboard",
element: <SettingsKeyboardRoute />,
}, },
{ {
path: "advanced", path: "advanced",
@ -276,7 +281,11 @@ if (isOnDevice) {
}, },
{ {
path: "mouse", path: "mouse",
element: <SettingsKeyboardMouseRoute />, element: <SettingsMouseRoute />,
},
{
path: "keyboard",
element: <SettingsKeyboardRoute />,
}, },
{ {
path: "advanced", path: "advanced",

View File

@ -0,0 +1,74 @@
import { useCallback, useEffect } from "react";
import { useSettingsStore } from "@/hooks/stores";
import { useJsonRpc } from "@/hooks/useJsonRpc";
import notifications from "@/notifications";
import { SettingsPageHeader } from "@components/SettingsPageheader";
import { layouts } from "@/keyboardLayouts";
import { SelectMenuBasic } from "../components/SelectMenuBasic";
import { SettingsItem } from "./devices.$id.settings";
export default function SettingsKeyboardRoute() {
const keyboardLayout = useSettingsStore(state => state.keyboardLayout);
const setKeyboardLayout = useSettingsStore(
state => state.setKeyboardLayout,
);
const layoutOptions = Object.entries(layouts).map(([code, language]) => { return { value: code, label: language } })
const [send] = useJsonRpc();
useEffect(() => {
send("getKeyboardLayout", {}, resp => {
if ("error" in resp) return;
setKeyboardLayout(resp.result as string);
});
}, []);
const onKeyboardLayoutChange = useCallback(
(e: React.ChangeEvent<HTMLSelectElement>) => {
const layout = e.target.value;
send("setKeyboardLayout", { layout }, resp => {
if ("error" in resp) {
notifications.error(
`Failed to set keyboard layout: ${resp.error.data || "Unknown error"}`,
);
}
notifications.success("Keyboard layout set successfully");
setKeyboardLayout(layout);
});
},
[send, setKeyboardLayout],
);
return (
<div className="space-y-4">
<SettingsPageHeader
title="Keyboard"
description="Configure keyboard layout settings for your device"
/>
<div className="space-y-4">
{ /* this menu item could be renamed to plain "Keyboard layout" in the future, when also the virtual keyboard layout mappings are being implemented */ }
<SettingsItem
title="Paste text"
description="Keyboard layout of target operating system"
>
<SelectMenuBasic
size="SM"
label=""
fullWidth
value={keyboardLayout}
onChange={onKeyboardLayoutChange}
options={layoutOptions}
/>
</SettingsItem>
<p className="text-xs text-slate-600 dark:text-slate-400">
Pasting text sends individual key strokes to the target device. The keyboard layout determines which key codes are being sent. Ensure that the keyboard layout in JetKVM matches the settings in the operating system.
</p>
</div>
</div>
);
}

View File

@ -15,7 +15,7 @@ import { cx } from "../cva.config";
import { SettingsItem } from "./devices.$id.settings"; import { SettingsItem } from "./devices.$id.settings";
export default function SettingsKeyboardMouseRoute() { export default function SettingsMouseRoute() {
const hideCursor = useSettingsStore(state => state.isCursorHidden); const hideCursor = useSettingsStore(state => state.isCursorHidden);
const setHideCursor = useSettingsStore(state => state.setCursorVisibility); const setHideCursor = useSettingsStore(state => state.setCursorVisibility);

View File

@ -1,6 +1,7 @@
import { NavLink, Outlet, useLocation } from "react-router-dom"; import { NavLink, Outlet, useLocation } from "react-router-dom";
import { import {
LuSettings, LuSettings,
LuMouse,
LuKeyboard, LuKeyboard,
LuVideo, LuVideo,
LuCpu, LuCpu,
@ -19,6 +20,7 @@ import { LinkButton } from "@/components/Button";
import LoadingSpinner from "@/components/LoadingSpinner"; import LoadingSpinner from "@/components/LoadingSpinner";
import { useUiStore } from "@/hooks/stores"; import { useUiStore } from "@/hooks/stores";
import useKeyboard from "@/hooks/useKeyboard"; import useKeyboard from "@/hooks/useKeyboard";
import { FeatureFlag } from "../components/FeatureFlag";
import { cx } from "../cva.config"; import { cx } from "../cva.config";
@ -149,11 +151,25 @@ export default function SettingsRoute() {
className={({ isActive }) => (isActive ? "active" : "")} className={({ isActive }) => (isActive ? "active" : "")}
> >
<div className="flex items-center gap-x-2 rounded-md px-2.5 py-2.5 text-sm transition-colors hover:bg-slate-100 dark:hover:bg-slate-700 in-[.active]:bg-blue-50 in-[.active]:text-blue-700! md:in-[.active]:bg-transparent dark:in-[.active]:bg-blue-900 dark:in-[.active]:text-blue-200! dark:md:in-[.active]:bg-transparent"> <div className="flex items-center gap-x-2 rounded-md px-2.5 py-2.5 text-sm transition-colors hover:bg-slate-100 dark:hover:bg-slate-700 in-[.active]:bg-blue-50 in-[.active]:text-blue-700! md:in-[.active]:bg-transparent dark:in-[.active]:bg-blue-900 dark:in-[.active]:text-blue-200! dark:md:in-[.active]:bg-transparent">
<LuKeyboard className="h-4 w-4 shrink-0" />
<LuMouse className="h-4 w-4 shrink-0" />
<h1>Mouse</h1> <h1>Mouse</h1>
</div> </div>
</NavLink> </NavLink>
</div> </div>
<FeatureFlag minAppVersion="0.4.0" name="Paste text">
<div className="shrink-0">
<NavLink
to="keyboard"
className={({ isActive }) => (isActive ? "active" : "")}
>
<div className="flex items-center gap-x-2 rounded-md px-2.5 py-2.5 text-sm transition-colors hover:bg-slate-100 dark:hover:bg-slate-700 [.active_&]:bg-blue-50 [.active_&]:!text-blue-700 md:[.active_&]:bg-transparent dark:[.active_&]:bg-blue-900 dark:[.active_&]:!text-blue-200 dark:md:[.active_&]:bg-transparent">
<LuKeyboard className="h-4 w-4 shrink-0" />
<h1>Keyboard</h1>
</div>
</NavLink>
</div>
</FeatureFlag>
<div className="shrink-0"> <div className="shrink-0">
<NavLink <NavLink
to="video" to="video"