keyshift
v1.1.4
Published
Fix text typed with the wrong keyboard layout using a global shortcut on Windows, macOS and Linux X11.
Maintainers
Readme
KeyShift
Overview
KeyShift fixes text that was accidentally typed with the wrong keyboard layout.
For example, you intended to type:
سلامbut your keyboard layout was English, so you typed:
sghlPress the configured KeyShift shortcut and the text is converted automatically:
سلامOn Windows, KeyShift reads the keyboard layouts installed in the operating system. On macOS and Linux it currently provides the English (US) and Persian ISIRI 9147 conversion pair.
Features
- Global keyboard shortcut on Windows, macOS and X11 Linux
- Converts text between two configured keyboard layouts
- Automatic conversion-direction detection
- Uses installed keyboard layouts on Windows
- English (US) and Persian ISIRI 9147 on macOS and Linux
- English and Persian conversion
- Turkish, German, French, Arabic and other installed layouts on Windows
- Optional clipboard preservation
- Convert the complete focused text field
- Convert only selected text
- Configurable copy and paste delays
- Clipboard-only fallback for restricted desktop sessions
- Native Windows host and portable macOS/Linux host
- CLI-based configuration
- Lightweight installation
- A single background host process
Platform support
KeyShift can be installed and run on:
Windows 10
Windows 11
macOS
LinuxThe Windows host supports every keyboard layout exposed by the Windows
keyboard APIs. The macOS/Linux host currently supports the en-US and
fa-IR pair.
Linux global input uses X11. It works in X11 sessions and with compatible XWayland applications. Native Wayland compositors can restrict global hooks and injected keystrokes, so a full Wayland session is not guaranteed.
KeyShift does not currently run on:
Android
iOSRequirements
Before installing KeyShift, make sure the following software is available:
- Node.js 18 or newer
- npm
Platform requirements:
- Windows: .NET Framework 4.x runtime
- macOS: grant Accessibility permission to the terminal/Node process
- Linux X11:
xcliporxsel - Linux Wayland clipboard:
wl-clipboard; global input still depends on X11/XWayland compatibility
The npm package includes the Windows executable and prebuilt macOS/Linux global-input bindings. The .NET Framework compiler is required only when building the Windows host from source.
Installation
Install KeyShift globally:
npm install -g keyshiftOn Ubuntu/Debian X11, install a clipboard helper:
sudo apt-get install xclipOn Wayland, install wl-clipboard as well. For macOS, allow the terminal or
Node.js under System Settings > Privacy & Security > Accessibility so
KeyShift can observe the shortcut and send copy/paste keystrokes.
After a successful macOS/Linux shortcut conversion, KeyShift also triggers
the configured system input-language shortcut. Defaults are Control+Space
on macOS and Meta+Space on Linux. If your desktop uses another shortcut,
configure it and restart KeyShift:
keyshift config set languageSwitchShortcut Alt+Shift
keyshift restartDisable this behavior with:
keyshift config set switchInputLanguage falseIf the desktop blocks global input (for example, a native Wayland session), copy the text and run this command to convert the clipboard directly:
keyshift convert-clipboardPaste the converted text normally. This fallback also helps verify that the installed macOS/Linux package, layout conversion and clipboard backend work before troubleshooting global-input permissions.
Initialize the default configuration:
keyshift initStart KeyShift:
keyshift startCheck its status:
keyshift statusExpected result:
runningQuick start
After installation:
keyshift init
keyshift layouts
keyshift startOpen Notepad or another editable application.
Type:
sghlPress:
Ctrl + Alt + KThe result should be:
سلامThe default shortcut is:
Control+Alt+KKeyboard layouts
Display the layouts available to KeyShift:
keyshift layoutsExample output:
Supported portable keyboard layouts:
en-US English (US)
fa-IR Persian (ISIRI 9147)On Windows, keyshift layouts instead lists the layouts installed in the
current Windows session, for example:
00000409 English (United States)
00000429 Persian [active]
0000041F Turkish (Turkey)Use these IDs when configuring sourceLayout and targetLayout. Portable
aliases include en, us, 00000409, fa, ir, persian, 00000429
and 00050429.
Common Windows keyboard layout identifiers include:
| Layout | ID |
| ----------------------- | ---------: |
| English — United States | 00000409 |
| Persian | 00000429 |
| Arabic | 00000401 |
| Turkish Q | 0000041F |
| German | 00000407 |
| French | 0000040C |
| Russian | 00000419 |
Always prefer the values returned by:
keyshift layoutsThe exact layout installed on a machine may differ from the common examples.
Commands
Author
Mohadese Ghadimi
- GitHub: mohadesegh
- LinkedIn: Mohadese Ghadimi
Initialise configuration
keyshift initCreates or resets the KeyShift configuration file.
Configuration path:
%APPDATA%\keyshift\config.jsonkeyshift init only creates the configuration. It does not start or replace
the native host.
Start KeyShift
keyshift startStarts the native KeyShift host in the background.
Example output:
KeyShift running. Shortcut: Control+Alt+K
Conversion: 00000409 <-> 00000429
Mode: autoStop KeyShift
keyshift stopStops the native host and removes the saved process ID.
Restart KeyShift
keyshift restartEquivalent to:
keyshift stop
keyshift startRestart KeyShift after changing configuration.
Check status
keyshift statusPossible results:
runningor:
stoppedList installed layouts
keyshift layoutsDisplays installed Windows layouts on Windows, or the supported portable layouts on macOS and Linux.
Convert clipboard text
keyshift convert-clipboardConverts the current clipboard contents using the configured portable layout pair. This command is available on macOS and Linux and does not require a global keyboard hook.
Display logs
keyshift logsThe log file is stored under the platform application-data directory.
Run keyshift init to print the exact directory.
Display configuration
keyshift config showExample:
{
"shortcut": "Control+Alt+K",
"layoutMode": "auto",
"sourceLayout": "00000409",
"targetLayout": "00000429",
"directionDetection": "hybrid",
"preserveClipboard": false,
"copyDelayMs": 150,
"pasteDelayMs": 120,
"selectAllText": true
}Reset configuration
keyshift config resetRestores the default configuration.
Restart KeyShift afterwards:
keyshift restartUpdate the native host
keyshift stop
keyshift update-host
keyshift startUse this command after upgrading KeyShift or replacing the packaged Windows host. On macOS and Linux the portable host is part of the installed package, so no separate host copy is needed.
The host cannot be replaced while keyshift-host.exe is running because
Windows locks executable files that are currently in use.
Configuration
KeyShift configuration is stored in %APPDATA%\keyshift on Windows,
~/Library/Application Support/keyshift on macOS, and
${XDG_CONFIG_HOME:-~/.config}/keyshift on Linux.
Default configuration:
{
"shortcut": "Control+Alt+K",
"layoutMode": "auto",
"sourceLayout": "00000409",
"targetLayout": "00000429",
"directionDetection": "hybrid",
"preserveClipboard": false,
"copyDelayMs": 150,
"pasteDelayMs": 120,
"selectAllText": true
}Configuration values should normally be changed through the CLI instead of editing the JSON file manually.
Changing the shortcut
keyshift config set shortcut Control+Alt+KExamples:
keyshift config set shortcut Control+Shift+K
keyshift config set shortcut Alt+F8
keyshift config set shortcut Control+Alt+SpaceAfter changing the shortcut:
keyshift restartSupported shortcut modifiers:
Control
Ctrl
Alt
Shift
Win
WindowsSupported main keys:
A-Z
Space
F1-F12Setting source and target layouts
First display installed layouts:
keyshift layoutsThen configure the desired pair.
English US to Persian:
keyshift config set sourceLayout 00000409
keyshift config set targetLayout 00000429Restart KeyShift:
keyshift restartLayout modes
KeyShift supports two layout modes:
auto
pairAuto mode
keyshift config set layoutMode autoIn auto mode, KeyShift detects the likely conversion direction using:
- Characters found in the copied text
- Script direction
- Characters supported by each layout
- The active Windows keyboard layout
This is the recommended mode for bidirectional conversion.
Example:
English → Persian
Persian → EnglishPair mode
keyshift config set layoutMode pairPair mode always converts:
sourceLayout → targetLayoutIt does not reverse the configured direction automatically.
This is useful when conversion must always run in one direction.
Direction detection
KeyShift supports three direction-detection methods:
hybrid
content
active-layoutHybrid
keyshift config set directionDetection hybridHybrid mode uses text content and the active Windows keyboard layout.
This is the recommended default.
Content
keyshift config set directionDetection contentUses the characters and scripts found in the copied text.
The active Windows layout is used less aggressively.
Active layout
keyshift config set directionDetection active-layoutUses the keyboard layout currently active in the focused Windows application.
Convert all text or selected text
By default, KeyShift sends Ctrl+A before copying text.
That means all text in the focused editable control is converted.
keyshift config set selectAllText trueTo convert only manually selected text:
keyshift config set selectAllText falseRestart after changing the setting:
keyshift restartWhen selectAllText is false, select the text manually before pressing the
KeyShift shortcut.
Clipboard preservation
By default, KeyShift does not restore the previous clipboard contents:
keyshift config set preserveClipboard falseTo restore text that was already in the clipboard:
keyshift config set preserveClipboard trueRestart KeyShift:
keyshift restartClipboard preservation currently focuses on Unicode text. Rich clipboard formats, copied files and application-specific clipboard data may not be preserved.
Copy and paste delays
Some applications require more time to respond to synthetic copy and paste commands.
Change the copy delay:
keyshift config set copyDelayMs 250Change the paste delay:
keyshift config set pasteDelayMs 200Restart KeyShift:
keyshift restartRecommended values:
copyDelayMs: 100-300
pasteDelayMs: 100-300If conversion works in Notepad but fails in a heavier application, increase both values.
How KeyShift works
When the shortcut is pressed, KeyShift performs these steps:
- Waits for the physical shortcut keys to be released.
- Focuses on the currently active application.
- Optionally sends
Ctrl+A(Command+Aon macOS). - Sends the platform copy shortcut.
- Reads the selected Unicode text from the platform clipboard.
- Determines the source and target keyboard layouts.
- Maps each character back to its physical virtual key.
- Resolves the corresponding character in the target layout.
- Writes the converted value to the clipboard.
- Sends the platform paste shortcut.
- Optionally restores the previous clipboard text.
The Windows native host uses APIs including:
GetKeyboardLayout
GetKeyboardLayoutList
LoadKeyboardLayout
VkKeyScanEx
MapVirtualKeyEx
ToUnicodeEx
SendInput
SetWindowsHookExThe macOS/Linux host uses uiohook-napi for the global shortcut and key
injection, plus pbcopy/pbpaste, wl-clipboard, xclip, or xsel for
clipboard access. The TypeScript CLI manages configuration, process lifecycle,
logs and host selection.
Application data
KeyShift stores its runtime files in:
Windows: %APPDATA%\keyshift
macOS: ~/Library/Application Support/keyshift
Linux: ${XDG_CONFIG_HOME:-~/.config}/keyshiftThe directory contains:
config.json
keyshift-host.exe
keyshift.log
keyshift.pidkeyshift-host.exe exists only on Windows. The portable host runs from the
installed npm package.
Troubleshooting
EBUSY: resource busy or locked
Example:
EBUSY: resource busy or locked, copyfile ...
keyshift-host.exeThis means the native executable is currently running and Windows has locked the file.
Stop it:
keyshift stopIf it is still running:
taskkill /IM keyshift-host.exe /FThen update the host:
keyshift update-host
keyshift startKeyShift is already running
Check its status:
keyshift statusRestart it:
keyshift restartShortcut does not work
Check the log:
keyshift logsTry a different shortcut:
keyshift config set shortcut Control+Shift+K
keyshift restartMake sure another application is not using the same global shortcut.
No text is converted
Make sure the focused application supports:
Ctrl+A
Ctrl+C
Ctrl+VTest with Windows Notepad first.
When converting selected text only:
keyshift config set selectAllText falsemake sure text is selected before pressing the shortcut.
Wrong conversion direction
Use hybrid mode:
keyshift config set layoutMode auto
keyshift config set directionDetection hybrid
keyshift restartCheck the configured layouts:
keyshift config showCheck installed layout IDs:
keyshift layoutsConversion is too slow
Reduce delays:
keyshift config set copyDelayMs 100
keyshift config set pasteDelayMs 100
keyshift restartDo not reduce delays too aggressively because some applications may fail to copy or paste in time.
Conversion fails in a specific application
Increase delays:
keyshift config set copyDelayMs 300
keyshift config set pasteDelayMs 300
keyshift restartSome applications, terminals, browser pages, elevated windows and protected input controls may block synthetic keyboard input or clipboard operations.
KeyShift cannot interact with an Administrator application
A non-elevated process may not be able to send input to an application running as Administrator.
Run KeyShift and the target application at the same privilege level.
Avoid running KeyShift as Administrator unless it is necessary.
View the raw log file
PowerShell:
Get-Content "$env:APPDATA\keyshift\keyshift.log"Follow new log entries:
Get-Content "$env:APPDATA\keyshift\keyshift.log" -WaitCompletely reset KeyShift
Stop KeyShift:
keyshift stopRemove application data:
Remove-Item "$env:APPDATA\keyshift" -Recurse -ForceReinitialise:
keyshift init
keyshift startLimitations
KeyShift is designed for standard keyboard layouts that map physical keys to Unicode characters. Windows resolves installed layouts dynamically; the portable macOS/Linux host currently supports English (US) and Persian.
The following cases may not convert completely:
- Chinese IMEs
- Japanese IMEs
- Korean IMEs
- Multi-stage composition systems
- Complex dead-key sequences
- Application-specific input editors
- Password fields
- Protected browser inputs
- Elevated applications when KeyShift is not elevated
- Applications that do not support standard copy and paste shortcuts
Some keyboard layouts may map one physical key to multiple Unicode characters. Those mappings depend on Windows and the installed keyboard layout.
Security and privacy
KeyShift does not send copied text to a server.
Text conversion happens locally on the device.
KeyShift temporarily uses the platform clipboard to copy, convert and paste text.
Sensitive text should not be converted in password fields or protected input controls.
Building from source
Clone the repository:
git clone <repository-url>
cd keyshiftInstall dependencies:
npm installBuild TypeScript and prepare the native host:
npm run buildOn macOS and Linux, this builds the CLI and packages the checked-in prebuilt
Windows host without invoking PowerShell. This makes npm pack work on all
three desktop platforms.
On Windows, the native host is compiled using the .NET Framework C# compiler:
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\csc.exeor:
C:\Windows\Microsoft.NET\Framework\v4.0.30319\csc.exeIf the compiler is unavailable, enable .NET Framework 4.x in Windows
Features or install the .NET Framework 4.8 Developer Pack.
Local development
Build the project:
npm run buildLink the package globally:
npm linkInitialise:
keyshift initList layouts:
keyshift layoutsStart:
keyshift startCheck status:
keyshift statusAfter changing the native C# host:
npm run build
keyshift stop
keyshift update-host
keyshift startTesting the npm package
Inspect the files that will be published:
npm pack --dry-runCreate the package archive:
npm packInstall the generated archive globally:
npm uninstall -g keyshift
npm install -g ./keyshift-1.1.4.tgzTest:
keyshift init
keyshift layouts
keyshift start
keyshift statusRun the release guard:
npm run release:checkThe cross-platform workflow must also pass Windows, Linux X11 and the packaged macOS checks. Because hosted macOS runners cannot grant Accessibility access, run the real global-shortcut test once on a Mac before release:
KEYSHIFT_TEST_GLOBAL_INPUT=1 bash scripts/test-macos.sh "$(command -v keyshift)"Publishing
Use the Release npm package GitHub Actions workflow. First run it with
dry_run enabled. For a real release, confirm the native macOS global test,
set dry_run to false, and enter the exact version from package.json.
The workflow blocks publication unless the cross-platform integration matrix
passes. prepublishOnly also rebuilds, retests, checks that the version is not
already present on npm, verifies the tarball contents, and requires the release
approval environment variable. Configure the npm-publish GitHub environment
and either npm trusted publishing or the NPM_TOKEN repository secret before
the first release.
Updating the version
Patch release:
npm version patchExample:
1.1.0 → 1.1.1Minor release:
npm version minorExample:
1.1.0 → 1.2.0Major release:
npm version majorExample:
1.1.0 → 2.0.0Do not manually run npm version minor if package.json already contains the
intended version.
Architecture
┌───────────────────────────────────────┐
│ KeyShift CLI │
│ │
│ init · start · stop · status · logs │
│ layouts · config · update-host │
└───────────────────┬───────────────────┘
│
▼
┌───────────────────────────────────────┐
│ Native Windows Host │
│ │
│ Global keyboard hook │
│ Shortcut detection │
│ Clipboard operations │
│ Windows keyboard-layout APIs │
│ Character conversion │
└───────────────────┬───────────────────┘
│
▼
┌───────────────────────────────────────┐
│ Focused Windows application │
└───────────────────────────────────────┘Project structure
keyshift/
├── native/
│ ├── KeyShiftHost.cs
│ └── keyshift-host.exe
├── scripts/
│ ├── build-host.js
│ └── build-host.ps1
├── src/
│ ├── cli.ts
│ ├── config.ts
│ └── types.ts
├── .gitignore
├── LICENSE
├── README.md
├── package.json
└── tsconfig.jsonRoadmap
Planned improvements include:
- Start automatically with Windows
- System tray icon
- Native notifications
- Application-specific rules
- Multiple layout pairs
- Configurable conversion profiles
- Better dead-key handling
- Signed Windows executable
- Automated release workflow
- macOS support
- Linux support
- Optional graphical configuration interface
Contributing
Contributions are welcome.
Before submitting a pull request:
- Create a feature branch.
- Keep changes focused.
- Build the TypeScript CLI.
- Build the native Windows host.
- Run
npm run release:checkand test installation throughnpm pack. - Test conversion on every affected operating system.
- Include a clear explanation of the change.
Suggested workflow:
git checkout -b feature/my-change
npm install
npm run build
npm pack --dry-runReporting issues
When reporting an issue, include:
- Windows version
- Node.js version
- KeyShift version
- Source layout ID
- Target layout ID
- KeyShift configuration
- Application where the issue occurred
- Relevant log output
Commands:
node --version
keyshift config show
keyshift layouts
keyshift logsDo not include sensitive clipboard contents in public issue reports.
Licence
KeyShift is released under the MIT Licence.
See the LICENSE file for details.
